赞
踩
目录
我们假设实现一个棋盘27行,58列的棋盘(行和列可以根据自己的情况修改),再围绕地图画出墙,如下:
- #include<stdio.h>
- #include<stdlib.h>
- #include<windows.h>
- #include<stdbool.h>
- #include<locale.h>
-
- #define Case break;case
-
- #define WALL L'□'
- #define BODY L'●'
- #define FOOD L'☆'
-
- //默认的起始坐标
- #define POS_X 24
- #define POS_Y 5
-
- #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
-
- //贪吃蛇,蛇身节点的定义
- typedef struct SnakeNode
- {
- int x;
- int y;
- struct SnakeNode* next;
- }SnakeNode, * pSnakeNode;
- //typedef struct SnakeNode* pSnakeNode;
-
- enum GAME_STATUS
- //游戏状态
- {
- OK = 1,//正常运行
- ESC,//按了ESC键退出,正常退出
- KILL_BY_WALL,//撞墙
- KILL_BY_SELF//撞到自身
- };
-
- //行走的方向
- enum DIRECTION
- //方向
- {
- UP = 1,
- DOWN,
- LEFT,
- RIGHT
- };
-
- //贪吃蛇
- typedef struct Snake
- {
- pSnakeNode pSnake;//维护整条蛇的指针,是指向蛇头
- pSnakeNode pFood;//指向食物的指针
- int Score;//当前累积的分数
- int FoodWeight;//一个食物的分数,默认每个食物10分
- int SleepTime;//每走一步休眠时间?
- //蛇休眠的时间,休眠的时间越短,蛇的速度越快,休眠的时间越长,蛇的速度越慢
- enum GAME_STATUS status;//游戏当前的状态
- enum DIRECTION dir;//蛇当前走的方向,蛇头的方向默认是向右
- //...
- }Snake,* pSnake;
- //typedef struct Snake* pSnake;
-
- //定位控制台的光标位置
- void SetPos(int x, int y);
-
- //游戏开始的准备
- void GameStart(pSnake ps);
-
- //打印欢迎界面
- void welcomeToGame();
-
- //绘制地图
- void CreateMap();
-
- //初始化蛇
- void InitSnake(pSnake ps);
-
- //创建食物
- void CreateFood(pSnake ps);
-
- //游戏运行的整个逻辑
- void GameRun(pSnake ps);
-
- //打印帮助信息
- void PrintHelpInfo();
-
- //蛇移动的函数- 每次走一步
- void SnakeMove(pSnake ps);
-
- //判断蛇头的下一步要走的位置处是否是食物
- int NextIsFood(pSnake ps, pSnakeNode pNext);
-
- //下一步要走的位置处就是食物,就吃掉食物
- void EatFood(pSnake ps, pSnakeNode pNext);
-
- //下一步要走的位置处不是食物,不吃食物
- void NotEatFood(pSnake ps, pSnakeNode pNext);
-
- //检测是否撞墙
- void KillByWall(pSnake ps);
-
- //检测是否撞自己
- void KillBySelf(pSnake ps);
-
- //游戏结束的资源释放
- void GameEnd(pSnake ps);
初始化状态,假设蛇的长度是5,蛇身的每个节点是●,在固定的一个坐标处,比如(24, 5)处开始出现蛇,连续5个节点。
注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的一个节点有一半儿出现在墙体中,另外一般在墙外的现象,坐标不好对齐。
关于食物,就是在墙体内随机生成一个坐标(x坐标必须是2的倍数),坐标不能和蛇的身体重合,然后打印★。
在游戏运行的过程中,蛇每次吃一个食物,蛇的身体就会变长一节,如果我们使用链表存储蛇的信
息,那么蛇的每一节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行。
- #define WALL L'□' 墙
- #define BODY L'●' 蛇身
- #define FOOD L'☆' 食物
- //贪吃蛇,蛇身节点的定义
- typedef struct SnakeNode
- {
- int x;
- int y;
- struct SnakeNode* next;
- //是一个指向下一个 SnakeNode 类型节点的指针,用于构建链表来表示蛇的身体。
- }SnakeNode, * pSnakeNode;
- //typedef struct SnakeNode* pSnakeNode;
pSnakeNode pSnake:这是一个指向 SnakeNode 类型的指针,代表蛇的头部。通常,贪吃蛇的实现会用一个链表来表示蛇的身体,其中每个节点(SnakeNode)代表蛇身体的一部分,而 pSnake 指向这个链表的第一个节点,即蛇头。
pSnakeNode pFood:这是一个指向 SnakeNode 类型的指针,代表食物的位置。在贪吃蛇游戏中,食物会被随机放置在游戏区域内,当蛇吃到食物时,这个食物会被移除,并且蛇的身体会增长。
enum GAME_STATUS status;:这是一个枚举类型,表示游戏当前的状态。具体的枚举值没有在代码中给出,但可能包括“游戏中”、“游戏结束”等状态。
enum DIRECTION dir;:这是一个枚举类型,表示蛇当前移动的方向。具体的枚举值也没有在代码中给出,但通常包括“向上”、“向下”、“向左”、“向右”等方向。
- typedef struct Snake
- {
- pSnakeNode pSnake;//维护整条蛇的指针,是指向蛇头
- pSnakeNode pFood;//指向食物的指针
- int Score;//当前累积的分数
- int FoodWeight;//一个食物的分数,默认每个食物10分
- int SleepTime;//每走一步休眠时间?
- //蛇休眠的时间,休眠的时间越短,蛇的速度越快,休眠的时间越长,蛇的速度越慢
- enum GAME_STATUS status;//游戏当前的状态
- enum DIRECTION dir;//蛇当前走的方向,蛇头的方向默认是向右
- //...
- }Snake,* pSnake;
- //typedef struct Snake* pSnake;
- //行走的方向
- enum DIRECTION
- //方向
- {
- UP = 1,
- DOWN,
- LEAF,
- RIGHT
- };
- enum GAME_STATUS
- //游戏状态
- {
- OK = 1,//正常运行
- ESC,//按了ESC键退出,正常退出
- KILL_BY_WALL,//撞墙
- KILL_BY_SELF//撞到自身
- };
- void GameStart(pSnake ps)
- {
- //设置控制台的信息,窗口大小,窗口名
- system("mode con cols=120 lines=40");
- system("title 贪吃蛇");
-
- //隐藏光标
- HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_CURSOR_INFO CursorInfo;
- GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
- CursorInfo.bVisible = false;
- SetConsoleCursorInfo(handle, &CursorInfo);//设置光标信息
-
- //打印欢迎信息
- welcomeToGame();
-
- //绘制地图
- CreateMap();
-
- //初始化蛇
- InitSnake(ps);
-
- //创建食物
- CreateFood(ps);
-
- }
- //定位控制台的光标位置
- void SetPos(int x, int y)
- {
- //获得设备句柄
- HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
-
- //根据句柄设置光标的位置
- COORD pos = { x,y };
- SetConsoleCursorPosition(handle, pos);
- }
- void welcomeToGame()
- {
- //欢迎信息
- SetPos(35, 10);
- printf("欢迎来到贪吃蛇小游戏\n");
- SetPos(38, 20);
- system("pause");
- system("cls");
-
- //功能介绍信息
- SetPos(15, 10);
- printf("用 ↑ . ↓ . ← . → 来控制蛇的移动,F3是加速,F4是减速\n");
- SetPos(15, 11);
- printf("加速能得到更高的分数");
- SetPos(38, 20);
- system("pause");
- system("cls");
-
- }
创建地图就是将墙打印出来,因为是宽字符打印,所有使用wprintf函数,打印格式串前使用L
打印地图的关键是要算好坐标,才能在想要的位置打印墙体。
- void CreateMap()
- {
- int i = 0;
- //上
- SetPos(0, 0);
- for (i = 0; i <= 56; i += 2)
- {
- wprintf(L"%lc", WALL);
- }
- //下
- SetPos(0, 25);
- for (i = 0; i <= 56; i += 2)
- {
- 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);
- }
- }
蛇最开始长度为5节,每节对应链表的一个节点,蛇身的每一个节点都有自己的坐标。
创建5个节点,然后将每个节点存放在链表中进行管理。创建完蛇身后,将蛇的每一节打印在屏幕上。再设置当前游戏的状态,蛇移动的速度,默认的方向,初始成绩,蛇的状态,每个食物的分数。
结构体成员:记录它们的坐标:(x,y),和记录下一个位置的前驱结构体指针:next。
- void InitSnake(pSnake ps)
- {
- //创建五个蛇身的节点
- pSnakeNode cur = NULL;
- int i = 0;
- for (i = 0; i < 5; ++i)
- {
- cur = (pSnakeNode)malloc(sizeof(SnakeNode));
- if (cur == NULL)
- {
- perror("InsitSnkae():malloc()");
- return;
- }
- cur->x = POS_X + 2 * i;
- cur->y = POS_Y;
- cur->next = NULL;
-
- //头插法
- if (ps->pSnake == NULL)
- {
- ps->pSnake = cur;
- }
- else {
- cur->next = ps->pSnake;
- ps->pSnake = cur;
- }
- }
-
- //打印蛇身
- cur = ps->pSnake;
- while (cur)
- {
- SetPos(cur->x, cur->y);
- wprintf(L"%lc", BODY);
- cur = cur->next;
- }
- //贪吃蛇的其他信息
- ps->dir = RIGHT;
- ps->FoodWeight = 10;
- ps->pFood = NULL;
- ps->Score = 0;
- ps->SleepTime = 200;
- ps->status = OK;
-
- }
- void CreateFood(pSnake ps)
- {
- int x = 0;//x范围: 2~54 -> 0~52 + 2 -> rand()%53 + 2
- int y = 0;//y范围: 1~25 -> 0~24 + 1 -> rand()%24 + 1
-
- again:
- do {
- x = rand() % 53 + 2;
- y = rand() % 24 + 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->x = x;
- pFood->y = y;
-
- ps->pFood = pFood;
- SetPos(x, y);
- wprintf(L"%lc", FOOD);
-
- }
游戏运行期间,右侧打印帮助信息,提示玩家
根据游戏状态检查游戏是否继续,如果是状态是OK,游戏继续,否则游戏结束。
如果游戏继续,就是检测按键情况,确定蛇下一步的方向,或者是否加速减速,是否暂停或者退出游戏。
确定了蛇的方向和速度,蛇就可以移动了。
- void GameRun(pSnake ps)
- {
- //打印帮助信息
- PrintHelpInfo();
-
- //检测按键
- do
- {
- //当前的分数情况
- SetPos(62, 10);
- printf("总分:%5d\n", ps->Score);
-
- SetPos(62, 11);
- printf("食物的分支:%02d\n", ps->FoodWeight);
- //检测按键
- //上、下、左、右、ESC、空格、F3、F4
- 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_ESCAPE))
- {
- ps->status = ESC;
- break;
- }
- else if (KEY_PRESS(VK_SPACE))
- {
- //游戏要暂停
- pause();//暂停和开始
- }
- else if (KEY_PRESS(VK_F3))
- {
- if (ps->SleepTime >= 80)
- {
- ps->SleepTime -= 30;
- ps->FoodWeight += 2;
- }
- }
- else if (KEY_PRESS(VK_F4))
- {
- if (ps->FoodWeight > 2)
- {
- ps->SleepTime += 30;
- ps->FoodWeight -= 2;
- }
- }
-
- //走一步
- SnakeMove(ps);
- //睡眠一下
- Sleep(ps->SleepTime);
-
-
- }while(ps->status == OK);
-
- }
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
- //打印帮助信息
- void PrintHelpInfo()
- {
- SetPos(62, 15);
- printf("1. 不能穿墙. 不能咬到自己");
- SetPos(62, 16);
- printf("2. 用 ↑ . ↓ . ← . → 来控制蛇的移动");
- SetPos(62, 17);
- printf("3. F3是加速,F4是减速");
-
- SetPos(62, 19);
- printf(" ");
-
- }
- void pause()
- {
- while (1)
- {
- Sleep(100);
- if (KEY_PRESS(VK_SPACE))
- {
- break;
- }
-
- }
- }
- //pSnakeNode psn 是下一个节点的地址
- //pSnake ps 维护蛇的指针
- int NextIsFood(pSnake ps, pSnakeNode pNext)
- {
- if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
- return 1;
- else
- return 0;
- }
- //下一步要走的位置处就是食物,就吃掉食物
- void EatFood(pSnake ps, pSnakeNode pNext)
- {
- pNext->next = ps->pSnake;
- ps->pSnake = pNext;
-
- pSnakeNode cur = ps->pSnake;
- //打印蛇身
- while (cur)
- {
- SetPos(cur->x, cur->y);
- wprintf(L"%lc", BODY);
- cur = cur->next;
- }
- ps->Score += ps->FoodWeight;
-
- //释放旧的食物
- free(ps->pFood);
- //新建食物
- CreateFood(ps);
- }
将下一个节点头插入蛇的身体,并将之前蛇身最后一个节点打印为空格,放弃掉蛇身的最后一个节点
- //pSnakeNode psn 是下一个节点的地址
- //pSnake ps 维护蛇的指针
- void NotEatFood(pSnake ps, pSnakeNode pNext)
- {
- //头插法
- pNext->next = ps->pSnake;
- ps->pSnake = pNext;
-
- //释放尾结点
- pSnakeNode cur = ps->pSnake;
- while (cur->next->next)
- {
- 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 == 25)
- {
- 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;
- return;
- }
- cur = cur->next;
- }
- }
先创建下一个节点,根据移动方向和蛇头的坐标,蛇移动到下一个位置的坐标。
确定了下一个位置后,看下一个位置是否是食物(NextIsFood),是食物就做吃食物处理
(EatFood),如果不是食物则做前进一步的处理(NoFood)。
蛇身移动后,判断此次移动是否会造成撞墙(KillByWall)或者撞上自己蛇身(KillBySelf),从而影响游戏的状态。
- void SnakeMove(pSnake ps)
- {
- pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
- if (pNext == NULL)
- {
- perror("SnakeMove():malloc()");
- return;
- }
- pNext->next = NULL;
-
- switch (ps->dir)
- {
- case UP:
- pNext->x = ps->pSnake->x;
- pNext->y = ps->pSnake->y - 1;
- break;
- Case DOWN:
- pNext->x = ps->pSnake->x;
- pNext->y = ps->pSnake->y + 1;
- Case LEFT:
- pNext->x = ps->pSnake->x - 2;
- pNext->y = ps->pSnake->y;
- Case RIGHT:
- pNext->x = ps->pSnake->x + 2;
- pNext->y = ps->pSnake->y;
- break;
- }
-
- //下一个坐标是否是食物
- if (NextIsFood(ps, pNext))
- {
- //是食物就吃掉
- EatFood(ps, pNext);
- }
- else {
- //不是食物就正常一步
- NotEatFood(ps, pNext);
- }
- //检测撞墙
- KillByWall(ps);
-
- //检测是否撞自己
- KillBySelf(ps);
-
- }
游戏状态不再是OK(游戏继续)的时候,要告知游戏结束的原因,并且释放蛇身节点。
- //游戏结束的资源释放
- void GameEnd(pSnake ps)
- {
- SetPos(20, 15);
- switch (ps->status)
- {
- case ESC:
- printf("主动退出游戏,正常退出\n");
- Case KILL_BY_WALL:
- printf("很遗憾,撞墙了,游戏结束\n");
- Case KILL_BY_SELF:
- printf("很遗憾,撞到自己了,游戏结束\n");
- break;
- }
-
- //释放贪吃蛇的链表资源
- pSnakeNode cur = ps->pSnake;
- pSnakeNode del = NULL;
-
- while (cur)
- {
- del = cur;
- cur = cur->next;
- free(del);
- }
- free(ps->pFood);
- ps = NULL;
- }
- void test()
- {
- //创建贪食蛇
- Snake snake = { 0 };
-
- //GameStart(&snake);//游戏开始前的初始化
- //GameRun();//玩游戏的过程
- //GameEnd();//善后的工作
- int ch = 0;
- do
- {
- Snake snake = { 0 };
- GameStart(&snake);//游戏开始前的初始化
- GameRun(&snake);//玩游戏的过程
- GameEnd(&snake);//善后的工作
- SetPos(15, 20);
- printf("再来一局吗?(Y/N):");
- ch = getchar();
- getchar();// 清理\n
- } while (ch == 'Y' || ch == 'y');
- }
-
-
-
-
- int main()
- {
- //修改适配本地的环境
- setlocale(LC_ALL, "");
-
- test();//贪吃蛇游戏的测试
- SetPos(0, 30);
-
- return 0;
- }
今天就先到这了!!!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。