文章目录
- 前言
- 一、蛇的结构的创建
- 二、游戏测试的逻辑
- 三、初始化游戏
- 四、游戏运行
- 五、游戏结束
- 总结
前言
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; // 总成绩初始为0ps->_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语言实现贪吃蛇游戏