好久没写博客了,今天来水一点东西(bushi)
这段时间一直在写C语言这学期的课程设计(选的是游戏类的贪吃蛇游戏),今天就来写个博客来教下大概怎么写吧
- 注意:1.此篇文章仅供参考代码以及学习技术,禁止在违法开源协议(选用的是最宽松的开源协议)/照搬代码/看不懂代码就直接写/直接挪用代码作为课程设计等上述行为
2.目前项目仍然存在一些问题,以实际情况为准 *
清屏函数
void clearScreen()
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coordScreen = {0, 0};
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
// 获取控制台缓冲区信息
GetConsoleScreenBufferInfo(hConsole, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
// 用空格填充整个缓冲区
FillConsoleOutputCharacter(hConsole, ' ', dwConSize, coordScreen, &cCharsWritten);
// 获取当前文本属性
GetConsoleScreenBufferInfo(hConsole, &csbi);
// 用当前文本属性填充整个缓冲区
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
// 将光标位置设置到左上角
SetConsoleCursorPosition(hConsole, coordScreen);
}
本身我们可以使用system(“cls”)函数来清屏,但是经过实际测试,效果并不好,同时在某些特定的情况下会出现一些问题,所以我查找资料使用了这个方法直接调用WindowsAPI来进行清屏,同时在后续需要的时候直接使用clearScreen()即可
主菜单构建
在长达十秒钟的思维风暴后,我构建出主菜单的几个主要功能
printf("=================================n");
printf("欢迎来到贪吃蛇游戏!n");
printf("请选择你要进行的操作:n");
printf("0.加载游戏n");
printf("1.开始游戏n");
printf("2.查看分数n");
printf("3.关于我们n");
printf("4.游戏规则n");
printf("5.退出系统n");
printf("=================================n");
printf("请输入要进行的序号(0-5):");
同时,为了持续检测用户的输入值,我们再使用while循环来检测用户输入的序号
fflush(stdin);
char choice = getchar();
// 忽略换行符
if (choice == 'n')
{
continue;
}
switch (choice)
{
case '1':
clearScreen();
start();
break;
case '2':
query();
break;
case '3':
about();
break;
case '4':
printf("n==============游戏规则===============:n");
printf("1.移动:使用方向键(W上,S下,A左,D右)控制蛇的移动方向。n");
printf("2.吃食物(#):吃食物可以增加蛇的长度。n");
printf("3.陷阱(X):碰撞陷阱将会导致游戏失败。n");
printf("4.碰撞墙壁将会导致游戏失败。n");
printf("5.游戏中按住q将保存游戏数据并退出n");
printf("注意:为了保证游戏公平,每轮游戏最多仅允许暂停一次,否则判断失败!!!n");
printf("祝你玩得开心!n");
printf("按任意键继续...");
_getch();
clearScreen();
main();
break;
case '5':
exit(0);
break;
case '0':
load();
break;
default:
printf("输入错误!请重新输入:");
}
但是在这里,我们调用了一个新的函数fflush(stdin);
查询资料之后,这个函数的只要功能是清除输入缓冲区的残留的内容,因为我们使用的是持续读取,再我们获得操作指令之后可能仍然存在内容,为了保证执行正常,我们使用此函数来清除输入缓冲区
初始化
在开始游戏之前,我们需要进行一些初始化,比如初始化蛇的位置,初始化食物的位置,初始化蛇的长度,初始化蛇的方向等等
注意:在这里我们只进行初始化而不进行处理!!!
初始化蛇
在这里,我们采用结构体的方式来村塾蛇的位置
struct body {
int x, y;//蛇的坐标
};
什么?你问我为什么只有一个结构体,而且结构体里面只有一个x,y?
在这里,我们记录的是蛇头的坐标,舍得身体必定会经过蛇头所经过的坐标,所以这里我们记录蛇头坐标,蛇身坐标可以通过蛇头坐标和蛇的身体推算出来,所以我们会有一个定义蛇身体的函数
struct snake {
int size;//蛇的长度
struct body body[wide * high];//嵌套蛇的身子
}snake;
(不要在意结构体的名字,我在写的时候也没想到名字怎么变这么奇怪了)
初始化食物
在这里我们采用容量为2的数组来存储食物的位置(一个空间存x,一个存y)
int food[2] = { 0 };//food[0]为x坐标,food[1]为y坐标
初始化陷阱
陷阱的初始化与食物一样,但是我们的关卡难度会持续上升,陷阱的数量会随着关卡数量而增加
陷阱数量的问题我们在主函数中解决,在这里,我们只初始化而不进行处理
//初始化陷阱
typedef struct trap {
int x, y;
} trap;
//陷阱数量
int trapNum = 0;
//全局陷阱数组
trap traps[wide * high];
其他初始化
在头文件中应该没有初始化了,剩下的如果有应该在main函数中,随后我们声明了我们要在.c中要调用的函数以及一些变量
//函数声明部分
void initFood();//初始化食物
void initSnake();//初始化蛇
void showUI();//显示界面
void playGame();//开始游戏
void start();//在选择菜单为开始游戏引导代码声明
int score = 0;
//偏移坐标
int dx = 0;
int dy = 0;
void initWall();//初始化墙
//记录尾巴坐标
int lx = 0;
int ly = 0;
void initTrap(int score);
void save();
void load();
开始游戏
我们直接使用在main中的switch语句钓友我们的start函数
首先,这里要介绍一篇文章https://docs.pingcode.com/baike/1162036
这篇文章使用了WindowsAPI以隐藏光标来获得更好的视觉体验
具体代码如下
//隐藏光标获取良好体验https://docs.pingcode.com/baike/1162036
CONSOLE_CURSOR_INFO cci; // 声明cci结构体
cci.dwSize = sizeof(cci); // 设置大小
cci.bVisible = FALSE; // 隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
但在使用之前我们应该清一下屏,然后由于我们时开始一轮新的游戏,会有说一句游戏缓存,我们也要同步清理
clearScreen();
score = 0; // 防止上一轮分数未归零
SetConsoleOutputCP(65001); // 设置控制台为UTF-8编码,解决中文乱码
srand(time(NULL)); // 设置随机种子
随后我们要调用我们上一步的初始化函数来构造游戏界面
// 调用函数部分
initFood(); // 调用食物
initSnake(); // 调用蛇
//showUI();//显示界面
initWall(); // 调用墙
playGame(); // 开始玩游戏
但是,值得注意的是,游戏结束后依旧会有scanner在,此处清除键盘缓存区的内容
int c;
while ((c = getchar()) != 'n' && c != EOF)//目标时换行
{
// 读取并丢弃字符
}
总结(一)
综上便是第一部分的内容
完整代码暂不直接提供,但是源代码已经在github开源,可以参考一下
感谢支持!