emmm,昨天没有更新,,这个嘛。还是因为STM32的原因,,,昨天弄它的串口通信弄了一下午加半个晚上,,再加上降温了。。手冷,,昨天就没写。。今天因为妹妹开网课了,,我又给她把家里以前的台式拼起来,,所以耽误的不少时间。。
好了,话不多说,我们来开始今天的主题,这是最后一章了,我会把剩余的函数都讲了它。大家看完后可以自己尝试着也写一个,或者找啥子像素鸟啊,五子棋啊啥子试试,原理都是通的。这些项目除了可以激发兴趣外,还可以加深你对c语言的理解。
moveSnake函数
这个函数可以说是整个游戏的核心函数了,我们一定要弄明白蛇在整个游戏中的移动的逻辑。
首先,蛇在地图初始化的时候已经被我们打印出来了,但是在游戏中,即使我们不操作,蛇也是会自动移动的,这个移动的方向初始化时是向上,其他时候就是我们上次操作时的方向。
怎么样显示蛇“移动”的这个效果嘞?嘿嘿,通过坐标运算,把蛇的身子的其他值往前一个移动就可以了。
for (k = snake.len - 1; k>0; k--)//蛇的身子一格一格的移动
{
snake.x[k] = snake.x[k - 1];
snake.y[k] = snake.y[k - 1];
}
并且蛇在移动时,如果没有吃到过果子,是没有长大的。所以我们渲染蛇向前移动时,最后一格的蛇尾“#”也要变成“ ”。
同时,使用键盘控制蛇的移动时,如果蛇是向上走的,则你输入“S”向下是无效的。即需要考虑操作的合理性。
这个函数的完整代码:
void moveSnake()
{
if (!grow)//如果没有长大,则上次的最后一个点,即尾巴,的地方显示空.这里的grow是在更新食物时我们标记的
{
gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
printf(" ");//用空格覆盖之前的蛇尾巴
}
for (k = snake.len - 1; k>0; k--)//蛇的身子一格一格的移动
{
snake.x[k] = snake.x[k - 1];
snake.y[k] = snake.y[k - 1];
}
if (kbhit())//检查当前是否有键盘输入
{
ch = getch();
if (ch == ' ') //如果输入空格表示暂停,使用while函数一直卡着。
while (getch() != ' '){};
if (ch != UP && ch != DOWN && ch != LEFT && ch != RIGHT) //如果输入的时其他的键,则使用默认方向。这里的UP啊啥的都是之前定义好的
{
ch = Direction;
goto sport;//无条件跳转
}
if (Direction != ch)//如果输入的不默认方向
{
if ((Direction == UP &&ch != DOWN) || (Direction == DOWN &&ch != UP) || (Direction == LEFT && ch != RIGHT) || (Direction == RIGHT && ch != LEFT)) //Prevent reverse movement
{//确保输入的方向与当前方向逻辑上不违背,即确保蛇不能倒退
Direction = ch;
goto sport;
}
else
{//如果违背了。则保持当前
ch = Direction;
goto sport;
}
}
else
{
goto sport;
}
}
else
{
sport:
switch (ch) //移动蛇的头部
{
case UP: snake.x[0]--; break;//注意,在窗口中,坐标与我们常用的坐标是倒过来的
case DOWN: snake.x[0]++; break;
case LEFT: snake.y[0]--; break;
case RIGHT: snake.y[0]++; break;
default: break;
}
}
gotoxy(snake.x[0], snake.y[0]);
printf("@"); //输出蛇的头
grow = 0;//把grow标记回0
gotoxy(FRAME_HEIGHT, 0);//把光标始终弄在结尾处
}
emm,为了方便大家理解,我在它原本的基础上加了不少注释,方便大家理解。
isAlive函数
这个函数时用来判断蛇是否存活。主要的逻辑是判断蛇是否撞到边界和撞到自己。
这个函数很简单,就是一个判断函数
int isAlive(void)
{//判断蛇是否还活着,即判断蛇有没有撞上自己的身体,有没有碰到边界
if (snake.x[0] == 0 || snake.x[0] == FRAME_HEIGHT - 1 || snake.y[0] == FRAME_WIDTH - 1 || snake.y[0] == 0) //When you touch a wall or eat your own body, you die
return 0;
for (k = 1; k<snake.len; k++)
if (snake.x[k] == snake.x[0] && snake.y[k] == snake.y[0])
return 0;
return 1;
}
getSpeed函数
这个函数是用来根据蛇的长度判断蛇的速度的。但是我们主要的速度逻辑并不是在这里实现的,所以这个函数并不复杂。
void getSpeed(void)//根据长度修改速度
{
if (snake.len <= 6)
snake.speed = 200;
else if (snake.len <= 10)
snake.speed = 175;
else if (snake.len <= 20)
snake.speed = 150;
else if (snake.len <= 30)
snake.speed = 100;
else snake.speed = 50;
if (snake.len == 40)
finish = 0;//长度40为通关
}
main主函数
嘿嘿,关于snake.h和它的函数终于写完了。接下来我们就可以来完成最后一步了。之前说了,我们只要在开头把snake.h的头文件包含进来,就可以直接用了。具体逻辑我就不详细讲解了,但是注释我还是给大家打的挺详细的。
#include "snake.h"
#include <windows.h>
#include <math.h>
//Transposed structure object
extern struct Food food;//全局变量申明
extern struct Snake snake;
//main function
int main()
{
ret = menu();//显示初始化菜单,并且判断用户是否想玩游戏
if (ret == 1)//输入1,表示是
{
while (finish)
{
initMap(); //初始化地图
while (TRUE)
{
updataFood(); //updata food
getSpeed(); //speed
moveSnake(); //move snake
Sleep(snake.speed); //是计算机处理的程序的函数。。功能是暂停,单位是毫秒,它就是用来控制蛇的快慢的,嘿嘿。其实就是通过这个函数让系统进程变慢,从而控制蛇的快慢
printFont(FONT_WINDOW_STARTX, FONT_WINDOW_STARTY, snake.speed); //显示游戏信息
if (!(isAlive()))//如果没有存活,则结束
break;
}
if (finish)//如果没有标记通关,则显示你这次的长度 //Death or customs clearance
{
system("cls");//清屏函数
gotoxy(22, 5);
printf("您的游戏结束,您操作的贪吃蛇长度为%d,请再接再厉",snake.len);
gotoxy(24, 5);
printf("按(1)键退出游戏,按(2)键重玩");
char ch;
while((ch=getch(),ch)!= 50)
{
if(ch==49) return 0;
if(ch==50) break;
}
system("cls");
}
else//如果通关了
{
system("cls");
gotoxy(22, 5);
printf("恭喜你通关了");
gotoxy(24, 5);
printf("按(1)键退出游戏,按(2)键重玩");
char ch;
while((ch=getch(),ch)!= 50)
{
if(ch==49) return 0;//注意这里的49没有引号,是数字,因为“1”的ASIIC码值就是49,所以可以这样判断
if(ch==50) break;
}
system("cls");
}
}
}
}
关于贪吃蛇这个小项目的讲解就到这里了,大家可以自己去尝试下,完善下。其实在实际的写课设的过程中,写的顺序并不是我这个讲解的顺序,我只是为了方便大家理解。写过课设的童鞋都知道,大多都是自己先定一个大概的框架,然后再逐步完善的而不是像这个一样一口气写出来。
最后,大家如果有不明白的地方也欢迎在评论区问我。也可以来技术菌团的qq群一起讨论