前言
C语言实现贪吃蛇游戏
一、蛇的结构的创建
- 先创建一个蛇身的节点
- 在创建蛇的结构(包括:指向蛇头的指针,指向食物的指针,游戏的状态,蛇的速度,蛇的方向,食物的分数,总得分)
蛇的方向和游戏的状态采用枚举列举。
// 蛇的方向
enum DIRECTION
{
UP = 1,
DOWN,
LEFT,
RIGHT
};
// 游戏的状态(正常、撞墙、撞到自己、正常退出)
enum GAME_STATUS
{
ok,//正常状态
KILL_BY_WALL,// 撞墙死
KILL_BY_SELF,// 撞到自己死
END_NORMAL // esc正常退出
};
// 创建蛇身的节点
typedef struct SnakeNode
{
// 每个节点坐标
int x;
int y;
// 写一个蛇身节点的地址
struct SnakeNode* next;
}SnakeNode, *pSnakeNode;
// 真正的贪吃蛇
typedef struct Snake
{
pSnakeNode _pSnake;// 指向蛇头的指针
pSnakeNode _pFood; // 指向食物节点的指针
enum DIRECTION _dir; // 蛇的方向
enum GAME_STATUS _status;// 当前游戏的状态
int _food_weight; // 一个食物的分数
int _score; // 总分数
int _sleep_time; // 休眠时间,时间越长,速度越慢,时间越短,速度越快
}Snake, *pSnake;
二、游戏测试的逻辑
- 每次游戏开始都先清空屏幕
- 初始化游戏
- 游戏运行
- 游戏结束
void test()
{
int ch = 0;
do
{
system("cls");
// 创建贪吃蛇
Snake snake = { 0 };
// 初始化游戏
// 1. 打印欢迎界面
// 2. 打印功能介绍
// 3. 绘制地图
// 4. 创建蛇
// 5. 创建食物
// 6. 设置游戏的相关信息
GameStart(&snake);
// 游戏运行
GameRun(&snake);
// 游戏结束--- 游戏的善后工作
GameEnd(&snake);
SetPos(25, 18);
printf("要再来一局吗?(Y/N)");
ch = getchar();
while (getchar() != '\n');
} while (ch == 'Y' || ch == 'y');
SetPos(0, 27);
}
三、初始化游戏
- 打印欢迎界面
- 打印功能介绍
- 绘制地图
- 创建蛇
- 创建食物
- 在创建蛇和创建食物同时初始化蛇的基本信息
void SetPos(short x, short y)
{
// 获得标准输出设备的句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//定位光标的位置
COORD pos = { x, y };
SetConsoleCursorPosition(houtput, pos);
}
// 1-2.欢迎界面的打印及功能介绍函数
void WelcomeToGame()
{
SetPos(40, 14);
wprintf(L"欢迎来到贪吃蛇小游戏!!\n");
SetPos(42, 25);
system("pause");
system("cls");
SetPos(30, 14);
wprintf(L"按↑. ↓. ←. →来控制蛇的方向,按F3加速,F4减速");
SetPos(40, 16);
wprintf(L"加速可以得到跟高的分数");
SetPos(42, 25);
system("pause");
system("cls");
}
// 3. 打印地图
void CreateMap()
{
// 上
int i = 0;
for (i = 0; i < 29; i++)
{
wprintf(L"%lc", WALL);
}
// 下
SetPos(0, 26);
for (i = 0; i < 29; i++)
{
wprintf(L"%lc", WALL);
}
// 左
for (i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
// 右
for (i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", WALL);
}
}
// 4. 创建蛇
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
int i = 0;
for (i = 0; i < 5; i++)
{
// 申请一个蛇身节点
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (NULL == cur)
{
perror("InitSnake()::malloc()");
return;
}
cur->next = NULL;
cur->x = POS_X + 2 * i;
cur->y = POS_Y;
// 头插法插入链表
if (ps->_pSnake == NULL) // 空链表
{
ps->_pSnake = cur;
}
else // 非空链表
{
cur->next = ps->_pSnake;
ps->_pSnake = cur;
}
}
// 打印蛇身
cur = ps->_pSnake;
SetPos(cur->x, cur->y);
wprintf(L"%lc", HEAD);
cur = cur->next;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
// 设置贪吃蛇属性
ps->_dir = RIGHT; // 初始方向设置为向右
ps->_score = 0; // 总成绩初始为0
ps->_food_weight = 10;// 一个食物10分
ps->_sleep_time = 200; // 休眠时间,单位为毫秒
ps->_status = ok; // 默认游戏状态ok
}
// 5. 创建食物函数
void CreateFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
// 随机生成食物位置,不能在墙体上
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);
// 随机生成的食物也不能在蛇身上
pSnakeNode cur = ps->_pSnake;
while (cur)
{
if (x == cur->x && y == cur->y)
{
goto again;
}
cur = cur->next;
}
// 申请一个食物节点
pSnakeNode pFood = malloc(sizeof(SnakeNode));
if (pFood == NULL)
{
perror("CreateFood()::malloc()");
return;
}
pFood->next = NULL;
pFood->x = x;
pFood->y = y;
// 定位位置打印食物
SetPos(pFood->x, pFood->y);
wprintf(L"%lc", FOOD);
ps->_pFood = pFood;
}
// 初始化游戏
void GameStart(pSnake ps)
{
// 0. 先设置窗口大小再光标隐藏
//设置窗口大小
system("mode con cols=100 lines=30");
system("title 贪吃蛇");
// 获得标准输出设备的句柄
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
// 隐藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);// 获取控制台光标信息
CursorInfo.bVisible = false; // 隐藏控制台光标
SetConsoleCursorInfo(houtput, &CursorInfo);// 设置控制台光标信息
// 1. 打印欢迎界面
// 2. 打印功能介绍
WelcomeToGame();
// 3. 绘制地图
CreateMap();
// 4. 创建蛇
InitSnake(ps);
// 5. 创建食物
CreateFood(ps);
}
四、游戏运行
- 游戏运行主要包括
- 打印帮助信息
- 按键的检测(改变方向)
- 暂停游戏功能
- 判断下一个节点是否为食物(吃掉食物、没有食物)
- 检测是否撞墙
- 检测是否撞到自己
// 打印帮助信息函数
void PrintHelpInfo()
{
SetPos(64, 17);
wprintf(L"不能穿墙,不能咬到自己");
SetPos(64, 18);
wprintf(L"按↑. ↓. ←. →来控制蛇的方向");
SetPos(64, 19);
wprintf(L"+ 为加速,- 为减速");
SetPos(64, 20);
wprintf(L"ESC: 退出游戏!SPACE: 暂停游戏");
SetPos(64, 22);
wprintf(L"hhb@版权");
}
// 检测按键的宏
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1)? 1 : 0)
// 暂停函数
void Pause()
{
while (1)
{
Sleep(200);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
// 判断下一个节点是否为食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{
return (pn->x == ps->_pFood->x && pn->y == ps->_pFood->y);
}
// 吃掉食物函数
void EatFood(pSnakeNode pn, pSnake ps)
{
// 将食物节点头插到蛇头之前
ps->_pFood->next = ps->_pSnake;
ps->_pSnake = ps->_pFood;
// 释放next节点
free(pn);
pn = NULL;
// 打印蛇身
pSnakeNode cur = ps->_pSnake;
SetPos(cur->x, cur->y);
wprintf(L"%lc", HEAD);
cur = cur->next;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
ps->_score += ps->_food_weight;
// 吃掉食物,再创建一个新的事物
CreateFood(ps);
}
// 没有食物
void NoFood(pSnakeNode pn, pSnake ps)
{
// 新的节点头插到链表中
pn->next = ps->_pSnake;
ps->_pSnake = pn;
// 打印蛇
pSnakeNode cur = ps->_pSnake;
SetPos(cur->x, cur->y);
wprintf(L"%lc", HEAD);
cur = cur->next;
while (cur->next->next != NULL)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
// 将最后一个节点打印成空白
SetPos(cur->next->x, cur->next->y);
printf(" ");
// 释放最后一个节点
free(cur->next);
cur->next = NULL;
}
// 检测蛇是否撞墙
void KillByWall(pSnake ps)
{
if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||
ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
{
ps->_status = KILL_BY_WALL;
}
}
// 检测蛇是否撞到自己
void KillBySelf(pSnake ps)
{
pSnakeNode cur = ps->_pSnake->next;
while (cur)
{
if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
{
ps->_status = KILL_BY_SELF;
break;
}
cur = cur->next;
}
}
// 蛇向前走一步函数
void SnakeMove(pSnake ps)
{
// 创建下一个位置的节点
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pNextNode == NULL)
{
perror("SnakeMove()::malloc()");
return;
}
// 确定下一节点的位置
switch (ps->_dir)
{
case UP:
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y - 1;
break;
case DOWN:
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y + 1;
break;
case LEFT:
pNextNode->x = ps->_pSnake->x - 2;
pNextNode->y = ps->_pSnake->y;
break;
case RIGHT:
pNextNode->x = ps->_pSnake->x + 2;
pNextNode->y = ps->_pSnake->y;
break;
}
// 需要判断下一点是否是食物
if (NextIsFood(pNextNode, ps))
{
EatFood(pNextNode, ps);
}
else
{
NoFood(pNextNode, ps);
}
// 检测蛇是否撞到墙
KillByWall(ps);
// 检测蛇是否撞到自己
KillBySelf(ps);
}
// 游戏运行
void GameRun(pSnake ps)
{
// 打印帮助信息
PrintHelpInfo();
do
{
// 打印总得分和食物权重
SetPos(64, 10);
wprintf(L"总得分为: %d", ps->_score);
SetPos(64, 12);
wprintf(L"当前食物的分数为: %2d", ps->_food_weight);
// 检测按键
if (KEY_PRESS(VK_UP)&& ps->_dir != DOWN)
{
ps->_dir = UP;
}
else if (KEY_PRESS(VK_DOWN)&& ps->_dir != UP)
{
ps->_dir = DOWN;
}
else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
{
ps->_dir = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
{
ps->_dir = RIGHT;
}
else if (KEY_PRESS(VK_SPACE))
{
// 暂停
Pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
// 正常退出
ps->_status = END_NORMAL;
}
else if (KEY_PRESS(VK_ADD))
{
// 加速
if (ps->_sleep_time > 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_SUBTRACT))
{
// 减速
if (ps->_food_weight >2 )
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;
}
}
// 蛇向前走走一步
SnakeMove(ps);
// 蛇每走一步sleep一下
Sleep(ps->_sleep_time);
} while (ps->_status == ok);
}
五、游戏结束
- 根据蛇结构中游戏的状态打印对应的信息,并退出
- 销毁链表
- 在测试函数中循环执行,并增加提示信息,是否再来一把。
// 游戏结束--游戏善后工作
void GameEnd(pSnake ps)
{
SetPos(24, 12);
switch (ps->_status)
{
case END_NORMAL:
wprintf(L"您正常退出了!!\n");
break;
case KILL_BY_WALL:
wprintf(L"您撞到墙上了,游戏结束!!\n");
break;
case KILL_BY_SELF:
wprintf(L"您撞到自己了,游戏结束!!\n");
break;
}
// 销毁链表
pSnakeNode cur = ps->_pSnake;
while (cur)
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
}
总结
C语言实现贪吃蛇游戏
4174

被折叠的 条评论
为什么被折叠?



