贪吃蛇游戏的设计,需要考虑三个步骤
1.数据设计
2.模块化设计
3.实现过程
其中数据设计和模块化设计是最重要的两个步骤
数据设计要考虑到贪吃蛇的基本元素,包括地图,蛇,食物,以及按键操作。
#include
#include
#include //光标设置的API
#include //食物随机
#include //按键监控
//*********1、数据设计**************//
//辅助宏定义
#define MAPHEIGHT 25 //地图的高
#define MAPLENGHT 60 //地图的宽
#define SNAKESIZE 50 //蛇的最大节数
//食物
struct {
int x, y;
}food;
//蛇
struct {
//蛇每一节的位置
int x[SNAKESIZE], y[SNAKESIZE];
//蛇的长度
int len;
//蛇的速度
int speed;
}snake;
//DWORD t1, t2; //控制蛇的速度
//全局变量
char key = 'a'; //初始化蛇的移动方向
int changflag = 0; //蛇的变化标记
char lastkey = 'A'; //记录上一个按键
void drawMap();
void createFood();
void keyDown();
void move(char key);
int snakeStatus();
void gotoxy(int x, int y);
主函数
int main()
{
drawMap(); //初始化地图
while (1)
{
createFood();//产生食物
Sleep(snake.speed);
keyDown();//按键处理
if (snakeStatus()== 0)break;//判断蛇的状态
}
gotoxy(MAPLENGHT / 2, MAPHEIGHT / 2);
printf("GameOver\t");
system("pause");
return 0;
}
辅助函数:光标移动
游戏是在控制台进行,所以我们需要一个函数来控制光标的移动,通过移动光标来输出内容
void gotoxy(int x, int y)
{
//调用win32 API 去设置控制台的光标位置
//1.找到控制台的这个窗口
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
//2.光标的结构体
COORD coord;
//
//3.设置坐标
coord.X = x;
coord.Y = y;
//4.同步到控制台
SetConsoleCursorPosition(handle, coord);
}
初始化
初始化包括绘制地图,绘制蛇的初始状态,以及绘制初始状态的食物。
这里需要注意的是,表示蛇头的字符 ■ 占用两个字节,产生的食物坐标必须是偶数。否则会出现蛇经过食物而没有增长的bug。
//初始化
void drawMap(){
srand((unsigned int)time(NULL));//随机函数种子
//圈地
int i;
for (i = 0; i <= MAPLENGHT; i+=2)//上下边框
{
gotoxy(i, 0);
printf("■");
gotoxy(i, MAPHEIGHT);
printf("■");
}
for (i = 0; i <= MAPHEIGHT;i++)//左右边框
{
gotoxy(0, i);
printf("■");
gotoxy(MAPLENGHT, i);
printf("■");
}
//画蛇
//画蛇头
snake.len = 3;
snake.speed = 300;
snake.x[0] = MAPLENGHT / 2;
snake.y[0] = MAPHEIGHT / 2;
gotoxy(snake.x[0], snake.y[0]);
printf("■");
//画蛇身
int k;
for (k = 1; k < snake.len; k ++)
{
snake.x[k] = snake.x[k - 1] + 2;
snake.y[k] = snake.y[k - 1] ;
gotoxy(snake.x[k], snake.y[k]);
printf("■");
}
//画食物
//确定坐标
while (1)
{
food.x = rand() % (MAPLENGHT - 4) + 2;
food.y = rand() % (MAPHEIGHT - 2) + 1;
if (food.x % 2 == 0 && food.y % 2 == 0)break;
}
//画
gotoxy(food.x, food.y);
printf("○");
}
产生食物
//产生食物
void createFood() {
int flag=0;
//蛇吃食物后才产生新食物
if (snake.x[0] == food.x&&snake.y[0] == food.y)
{
while (1)
{
srand((unsigned int)time(NULL));
food.x = rand() % (MAPLENGHT - 4) + 2;
food.y = rand() % (MAPHEIGHT - 2) + 1;
//判断食物是否产生在蛇的身体上
for (int i = 0; i < snake.len; i++)
{
if (food.x == snake.x[i] && food.y == snake.y[i])
flag = 1;//食物产生在蛇身上的标记
}
if (flag == 0 && (food.x % 2 == 0 && food.y % 2 == 0))//食物必须产生在偶数坐标上
break;
}
gotoxy(food.x, food.y);
printf("○");
snake.len++;
changflag++;//蛇的变化标志
}
}
按键处理
注意:这里需要屏蔽掉与上一次键入方向相反的方向,以免蛇反向运动时出现自杀的情况。
//按键操作
void keyDown() {
//无按键的处理
if (_kbhit())//******************?
{//有按键接收
fflush(stdin);//**********?
key = _getch();//接收按键
}
//判断是否反向
switch(lastkey)
{
case'a':case'A':
if (key == 'w' || key == 'W' || key == 's' || key == 'S'||key=='a'||key=='A')move(key); break;
case'd':case'D':
if (key == 'w' || key == 'W' || key == 's' || key == 'S'||key == 'd' || key == 'D')move(key); break;
case'w':case'W':
if (key == 'a' || key == 'A' || key == 'd' || key == 'D'|| key == 'w' || key == 'W')move(key); break;
case's':case'S':
if (key == 'a' || key == 'A' || key == 'd' || key == 'D'|| key == 's' || key == 'S')move(key); break;
}
}
控制蛇的运动
运动的逻辑是,若蛇身不增长(即蛇没有吃食物),则将最后一节蛇身擦去,否则不擦去;蛇整体向前移动2个坐标,某一节蛇身的位置取代该节的前一节蛇身;最后根据键入的方向判断蛇头的位置。
//控制蛇的运动
void move(char key) {
//擦除最后位置
if (!changflag)
{
gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
printf(" ");
}
//蛇身移动
for (int k = snake.len - 1; k > 0; k--)
{
snake.x[k] = snake.x[k - 1];
snake.y[k] = snake.y[k - 1];
}
//按键处理
switch (key)
{
case 'w':case 'W':snake.y[0]--; break;
case 's':case 'S':snake.y[0]++; break;
case 'a':case 'A':snake.x[0] -= 2; break;
case 'd':case 'D':snake.x[0] += 2; break;
}
gotoxy(snake.x[0], snake.y[0]);
printf("■");
changflag = 0;
gotoxy(MAPLENGHT + 2, 0);//将光标移至一侧
lastkey = key;//更新lastkey
}
蛇的状态函数
蛇撞到墙或者蛇撞到自己时游戏结束
//蛇的状态
int snakeStatus()
{
//蛇撞到墙
if ((snake.x[0] == 2 || snake.x[0] == MAPLENGHT-2) || (snake.y[0] == 1 || snake.y[0] == MAPHEIGHT-1))
{
return 0;
}
//蛇撞到身体
for (int i = 1; i < snake.len; i++)
{
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
{
return 0;
}
}
return 1;
}
关于贪吃蛇萌新需要了解的关键Windows API以及船新函数
1、船新函数
通过上面两个函数可以使贪吃蛇保持自动前进的状态并且控制速度
2.Windows API
参考
C编程——实现贪吃蛇大作战