贪吃蛇的规则大家应该都知道,简单来说就是吃到食物则蛇身增长,蛇可以在游戏区域内随意移动。游戏结束条件就是蛇撞到边界或者撞到自己色蛇身。
首先,实现一个函数,这个函数的作用是将光标移动到我们所期望的位置
/*
* 控制光标所在的位置
*/
void gotoxy(int x, int y)
{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
设计这个游戏,最重要的两个对象就是蛇和食物,因此用两个结构体来代表这两个对象。食物只关注其坐标位置,蛇的话,成员包括坐标数组,长度和分数。
// 定义游戏区域的边界宽度
#define WIDTH 100
#define HIGH 20
// 定义蛇的最大长度
#define MAX_LENGHT 100
// 定义游戏初始速度
#define SPEED 500
// 定义游戏中的两个对象:食物和蛇
struct
{
int x;
int y;
}food;
struct
{
int len;
int x_buf[MAX_LENGHT];
int y_buf[MAX_LENGHT];
int score;
}snake;
// 要用到的全局变量
int food_flag = 0;
int key = 72;
这个没什么难度,就是循环打印,直接放代码。
/*
* 绘制游戏区域边界
*/
void DrawMap(void)
{
int x, y;
for (x = 0; x < WIDTH + 4; x += 2)
{
y = 0;
gotoxy(x, y);
printf("■");
y = HIGH + 2;
gotoxy(x, y);
printf("■");
}
for (y = 1; y < HIGH + 2; y++)
{
x = 0;
gotoxy(x, y);
printf("■");
x = WIDTH + 2;
gotoxy(x, y);
printf("■");
}
// 将光标移出游戏区域
gotoxy(0, HIGH + 5);
}
注意最后一行使用gotoxy函数移动函数很重要,不然光标会在游戏区域内持续闪烁,在退出游戏时,甚至会出现显示问题。(为了避免遗漏,我基本在每个涉及到光标移动的函数最后都加了这句话)
/*
* 初始化小蛇
*/
void CreateSnake(void)
{
int orgin_x, orgin_y;
orgin_x = WIDTH / 2 + 2;
orgin_y = HIGH / 2 + 1;
snake.len = 3;
snake.x_buf[0] = orgin_x;
snake.y_buf[0] = orgin_y;
snake.x_buf[1] = orgin_x;
snake.y_buf[1] = ++orgin_y;
snake.x_buf[2] = orgin_x;
snake.y_buf[2] = ++orgin_y;
snake.score = -1;
int i;
for (i = 0; i < snake.len; i++)
{
gotoxy(snake.x_buf[i], snake.y_buf[i]);
printf("■");
}
gotoxy(0, HIGH + 5);
}
我使用一个标志位food_flag来代表区域内是否存在食物(初始时刻,以及蛇吃到食物时,food_flag为0)。当food_flag为0时就执行生成食物的操作。由于要保证游戏的随机性,用随机数来生成食物坐标。
/*
* 当游戏区域内不存在食物时,随机创造一个食物
*/
void CreateFood(void)
{
if (food_flag == 0)
{
int flag = 0,i;
do
{
srand((unsigned int)time(NULL));
food.x = (rand() % (WIDTH/2))*2 + 2;
food.y = rand() % HIGH + 1;
// 判断生成的食物是否和蛇身重合
for (i = 0; i < snake.len; i++)
{
if (snake.x_buf[i] == food.x && snake.y_buf[i] == food.y)
{
flag = 1;
break;
}
}
} while (flag);
gotoxy(food.x, food.y);
printf("★");
// 吃到食物,则分数加1
snake.score++;
food_flag = 1;
}
gotoxy(0, HIGH + 5);
}
实现的方法都大同小异,但是我强调的点是随机生成坐标的实现。由于字符"■"是占两位,所以横坐标必须为2的倍数。其他的帖子里一般是用以下方法
food.x = rand() % WIDTH + 2;
food.y = rand() % HIGH + 1;
然后通过while循环来判断x是否适合要求,不符合就重新生成。但这种做法会出现一种问题,游戏进行过程中会出现卡顿的效果。主要原因就是奇偶数出现概率各占50%,因此有大概率出现多次的循环操作。
我的方法如下,这样的好处是可以保证生成的横坐标一定是偶数。避免了大量的循环操作。
food.x = (rand() % (WIDTH/2))*2 + 2;
food.y = rand() % HIGH + 1;
首先要对snake对象中坐标缓存区内的坐标进行后移,以便装入新的坐标。在此要分两种情况,一是小蛇没吃到食物,则抹去最后一节,在头部加入新坐标;二是小蛇吃到食物,则不删去最后一节。
void SnakeMove(int x, int y)
{
// 判断是否吃到食物,吃到长度加1
if (!food_flag)
snake.len++;
// 没吃到则抹去最后一节
else
{
gotoxy(snake.x_buf[snake.len - 1], snake.y_buf[snake.len - 1]);
printf(" ");
}
int i;
for (i = snake.len - 1; i > 0; i--)
{
snake.x_buf[i] = snake.x_buf[i - 1];
snake.y_buf[i] = snake.y_buf[i - 1];
}
snake.x_buf[0] = x;
snake.y_buf[0] = y;
gotoxy(snake.x_buf[0], snake.y_buf[0]);
printf("■");
gotoxy(0, HIGH + 5);
}
随后根据用户按键,改变小蛇前进方向。这块部分代码也是我移植过来的,有详细的注释,就不在这赘述了。
void move()
{
int pre_key = key, x, y;
if (_kbhit())//如果用户按下了键盘中的某个键
{
fflush(stdin);//清空缓冲区的字符
//getch()读取方向键的时候,会返回两次,第一次调用返回0或者224,第二次调用返回的才是实际值
key = _getch();//第一次调用返回的不是实际值
key = _getch();//第二次调用返回实际值
}
// 小蛇移动方向不能和上一次的方向相反
if (pre_key == 72 && key == 80)
key = 72;
if (pre_key == 80 && key == 72)
key = 80;
if (pre_key == 75 && key == 77)
key = 75;
if (pre_key == 77 && key == 75)
key = 77;
switch (key)
{
case 75:
x = snake.x_buf[0] - 2;//往左
y = snake.y_buf[0];
break;
case 77:
x = snake.x_buf[0] + 2;//往右
y = snake.y_buf[0];
break;
case 72:
x = snake.x_buf[0];
y = snake.y_buf[0] - 1;//往上
break;
case 80:
x = snake.x_buf[0];
y = snake.y_buf[0] + 1;//往下
break;
}
if (x == food.x&&y == food.y)
food_flag = 0;
SnakeMove(x, y);
}
主要是包括两个方面,(1)失败:小蛇撞墙或者撞到自己的身体;(2)成功:小蛇长度达到最大长度。
void check(void)
{
int i;
// 失败条件
if (snake.x_buf[0] == 0 | snake.x_buf[0] == WIDTH + 4 | snake.y_buf[0] == 0 | snake.y_buf[0] == HIGH + 2)
{
printf("Game Over!\n");
exit(0);
}
for (i = 1; i < snake.len; i++)
{
if (snake.x_buf[0] == snake.x_buf[i] && snake.y_buf[0] == snake.y_buf[i])
{
printf("Game Over!\n");
exit(0);
}
}
// 胜利条件
if (snake.len == MAX_LENGHT)
{
printf("Your are win!\n");
exit(0);
}
// 打印得分
gotoxy(0, HIGH + 6);
printf("Your score: %d", snake.score);
}
#include
#include
#include
#include
#include
int main(void)
{
DrawMap();
CreateSnake();
while (1)
{
CreateFood();
move();
// 用于控制游戏的速度
Sleep(SPEED - 2 * snake.len);
check();
}
}