C语言制作游戏——贪吃蛇
来源:互联网 发布:吉林国际软件 编辑:程序博客网 时间:2024/04/30 10:13
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:rfhklwt@163.com】
成品图
游戏模块组成
- 开拓疆土——绘制游戏窗口
- 上帝造蛇和食物——初始化蛇和食物
- 自动裁判员——菜单信息
- 食物从天而降——食物的随机生成
- 让蛇扭起来——移动蛇
- 大开吃戒——吃食物
- 瞬间移动——穿墙
- 生命凋零——蛇死亡
- 一切的开始——游戏开始
- 一切的结束——结束游戏
- 上帝控制台——主函数
开拓疆土——绘制游戏窗口
大家都玩过游戏,游戏一定有一个限制框来限制我们游戏主人公的行动,说白了就是地图,这就是我们的游戏窗口。见如下代码
void MakeFrame(HANDLE hOut){ //打印边框 SetPosition(hOut, FRAMEX, FRAMEY); //设置光标为左上角坐标处 printf("┏"); SetPosition(hOut, FRAMEX + 2 * FRAMEWIDTH - 2, FRAMEY); //设置光标为右上角坐标处 printf("┓"); SetPosition(hOut, FRAMEX, FRAMEY + FRAMEHEIGHT); //设置光标为左下角坐标处 printf("┗"); SetPosition(hOut, FRAMEX + 2 * FRAMEWIDTH - 2, FRAMEY + FRAMEHEIGHT); //设置光标为右下角坐标处 printf("┛"); //水平-顶端 for (int i = 2; i < 2 * FRAMEWIDTH - 2; i+=2) { SetPosition(hOut, FRAMEX + i, FRAMEY); printf("━"); } //水平-底端 for (int i = 2; i < 2 * FRAMEWIDTH - 2; i += 2) { SetPosition(hOut, FRAMEX + i, FRAMEY + FRAMEHEIGHT); printf("━"); } //竖直-左端 for (int i = 1; i < FRAMEHEIGHT; i++) { SetPosition(hOut, FRAMEX, FRAMEY + i); printf("|"); } //竖直-右端 for (int i = 1; i < FRAMEHEIGHT; i++) { SetPosition(hOut, FRAMEX + 2 * FRAMEWIDTH - 2, FRAMEY + i); printf("|"); } //打印游戏名称 SetPosition(hOut, FRAMEX + FRAMEWIDTH - 5, FRAMEY - 2); printf("贪吃蛇游戏"); //打印游戏操作 SetPosition(hOut, FRAMEX, FRAMEY + FRAMEHEIGHT + 2); printf("游戏操作: 上: ↑ 左: ← 右: → 下: ↓"); SetPosition(hOut, FRAMEX, FRAMEY + FRAMEHEIGHT + 4); printf("加速: 长按方向键 退出: ESC 暂停:Space");}
以上代码需要说明的有
1. SetPosition(HANDLE hOut, int x, int y)函数:
void SetPosition(HANDLE hOut, int x, int y){ COORD pos; pos.X = x; pos.Y = y; SetConsoleCursorPosition(hOut, pos);}
从上式代码可以看出,SetPosition(HANDLE hOut, int x, int y)函数是用于把光标放在我们需要的(x, y)上,从而能在(x, y)处打印我们需要的东西。
2. 常量:FRAMEX和FRAMEY和FRAMEHEIGHT和FRAMEWIDTH的含义
正如常量的名字一样,我们在主函数的前面定义了这四个常量,其含义和数值如下:
#define FRAMEX 4 //窗口左上角横坐标#define FRAMEY 4 //窗口左上角纵坐标#define FRAMEWIDTH 25 //游戏窗口宽度#define FRAMEHEIGHT 25 //游戏窗口高度
上帝造蛇和食物——初始化蛇和食物
根据蛇在本游戏中具有的性质,我们将这些性质打包到名为蛇的结构体中,同理也定义了一个食物的结构体。代码如下:
//蛇typedef struct SNAKE{ int x[100]; //蛇的横坐标, x[0]蛇尾横坐标 int y[100]; //蛇的纵坐标, y[0]蛇尾纵坐标 int nCount; //蛇吃食物总数 int nLength; //蛇的长度 int nSpeed; //蛇的移动速度}Snake;//食物typedef struct FOOD{ int x; //食物的横坐标 int y; //食物的纵坐标}Food;//打印蛇void PrintfSnake(HANDLE hOut, Snake* pSnake){ for (int i = 0; i < pSnake->nLength; i++) { SetPosition(hOut, pSnake->x[i], pSnake->y[i]); //打印蛇尾 if (i == 0) { SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_BLUE); printf("○"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } //打印蛇头 else if (i == pSnake->nLength - 1) { SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_GREEN); printf("¤"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } //打印蛇身 else { SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED); printf("⊙"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } }}
自动裁判员——菜单信息
作为一个心系玩家的开发者。我们需要将玩家在玩游戏时所产生的一些信息包括等级、得分等打印到屏幕上。这样可以让玩家在游戏得分中得到鼓舞,不断挑战自己。
void PrintfMenu(HANDLE hOut, Snake* pSnake, Food* pFood) { //游戏等级 SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 1); printf("游戏等级:%d", pSnake->nCount / 5 + 1); //每吃五个食物升一级 //游戏得分 SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 3); printf("游戏得分:%d分", pSnake->nCount); //每吃一个食物得一分 //食物个数 SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 5); printf("所吃食物总个数:%d个", pSnake->nCount); //玩家所吃的食物总数 //游戏速度 if (pSnake->nSpeed == 80) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:1"); } else if (pSnake->nSpeed == 70) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:2"); } else if (pSnake->nSpeed == 60) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:3"); } else if (pSnake->nSpeed == 50) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:4"); } else if (pSnake->nSpeed == 40) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:5"); } else if (pSnake->nSpeed == 30) { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:6"); } else { SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 7); printf("贪吃蛇的速度级别为:逆天级别"); } //食物坐标 SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 9); printf("食物的坐标:(%d, %d)", pFood->x, pFood->y); //温馨提示: SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 12); SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_BLUE); printf("游戏规则:吃到食物,碰到自身则蛇死亡。"); SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 14); printf("PS:这不是普通的墙哦,可以穿过去的(*^▽^*)"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);}
食物从天而降——食物的随机生成
关于食物的生成,有一下几点需要注意的:
- 食物出现的位置不能超过我们界定的游戏窗口
- 食物不能产生在我们的蛇身上(不然我们就看不到食物了)
根据以上两点,可写出如下代码:
void MakeFood(HANDLE hOut, Snake* pSnake, Food* pFood){ srand((unsigned)time(NULL)); /* 1.在游戏框架内 2.不在蛇身上 */ while(1) { pFood->x = rand() % (FRAMEWIDTH - 1); //0 - FRAMEWIDTH-1; pFood->y = rand() % FRAMEHEIGHT; //0 - FRAMEHEIGHT-1; if (pFood->x == 0 || pFood->y == 0) //食物不能再边界处 continue; pFood->x = 2 * pFood->x + FRAMEX; pFood->y += FRAMEY; //判断食物是不是在蛇的身上 int temp; for (temp = 0; temp < pSnake->nLength; temp++) { if (pFood->x == pSnake->x[temp] && pFood->y == pSnake->y[temp]) break; } if (temp == pSnake->nLength) { SetPosition(hOut, pFood->x, pFood->y); SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_RED); printf("◎"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); break; } }}
让蛇扭起来——移动蛇
看到这里,大家想想要如何让蛇移动起来呢?我们从前面的代码知道了整条蛇由众多的小蛇块组成,其中x[0]和y[0]是蛇尾的坐标。那么我们只要将让蛇尾去代替蛇尾前一块蛇的位置,以此不断推前,就能完成蛇的移动。在移动蛇的位置我们不考虑蛇头的位置,蛇头的运动由我们的玩家来控制,在StartGame()函数里面执行,现在可先不管。
如图,假设这条蛇的长度为6,x[5]为蛇头,x[0]为蛇尾。那么下一个时刻,除去蛇头,其余部位的位置应该如上图。x[0]所在的位置应该没有蛇的部位,反而,x[0]的位置挪到了x[1]的位置,以此类推,完成蛇的移动。至于蛇头的位置去哪了呢。不急~且看后文。看懂这个移动的原理后我们就可以写出下面的代码:
void MoveSnake(HANDLE hOut, Snake* pSnake){ SetPosition(hOut, pSnake->x[0], pSnake->y[0]); printf(" "); //后一个坐标代替前一个坐标 for (int i = 1; i < pSnake->nLength; i++) { pSnake->x[i - 1] = pSnake->x[i]; pSnake->y[i - 1] = pSnake->y[i]; }}
大开吃戒——吃食物
这部分没什么好讲的,大家看看代码就能理解了~
void EatFood(HANDLE hOut, Snake* pSnake, Food* pFood){ if (pSnake->x[pSnake->nLength - 1] == pFood->x && pSnake->y[pSnake->nLength - 1] == pFood->y) { //如果蛇头位置与食物位置相同,吃食物 pSnake->nLength++; for (int i = pSnake->nLength - 1; i > 0; i--) { pSnake->x[i] = pSnake->x[i - 1]; pSnake->y[i] = pSnake->y[i - 1]; } pSnake->x[0] = tail[0]; //得到蛇尾移动前的横坐标 pSnake->y[0] = tail[1]; //得到蛇尾移动前的纵坐标 //重新产生食物 MakeFood(hOut, pSnake, pFood); pSnake->nCount++; //食物的数量加一 //当蛇吃Up_level个食物时,速度加快Up_speed毫秒并且升一级 if (pSnake->nCount % 6 == 0) pSnake->nSpeed -= 10; }}
以上代码需要说明的有一点。
tail数组的含义
在主函数的前面,我们定义了一个全局变量tail[2]数组,来记录蛇尾移动前的位置
int tail[2]; //用于记住蛇尾坐标,其中tail[0]、tail[1]分别表示横、竖坐标
瞬间移动——穿墙
大家看成品图应该看到了,那一句蓝色的提示语,是的,在本游戏中,这条蛇是可以穿墙的,这穿墙不是指穿过墙跑到界外。而是,比如蛇撞到右边的墙壁中,则会从左边的墙壁出现。
void ThroughWall(HANDLE hOut, Snake* pSnake, char ch){ //如果蛇在上框且向上移动,穿墙 if (ch == 72 && pSnake->y[pSnake->nLength - 1] == FRAMEY) { pSnake->y[pSnake->nLength - 1] = FRAMEY + FRAMEHEIGHT - 1; } //如果蛇在下框且向下移动,穿墙 if (ch == 80 && pSnake->y[pSnake->nLength - 1] == FRAMEY + FRAMEHEIGHT) { pSnake->y[pSnake->nLength - 1] = FRAMEY + 1; } //如果蛇在右框且向右移动,穿墙 if (ch == 75 && pSnake->x[pSnake->nLength - 1] == FRAMEX) { pSnake->x[pSnake->nLength - 1] = FRAMEX + 2 * FRAMEWIDTH - 4; } //如果蛇在左框且向左移动,穿墙 if (ch == 77 && pSnake->x[pSnake->nLength - 1] == FRAMEX + 2 * FRAMEWIDTH - 2) { pSnake->x[pSnake->nLength - 1] = FRAMEX + 2; }}
生命凋零——蛇死亡
在本游戏中,蛇死亡的条件很简单,碰到自身就死了。
bool IfSnakeDie(Snake* pSnake){ //当蛇头碰到自身时,蛇死,返回值为TRUE for (int i = 0; i < pSnake->nLength - 1; i++) { if (pSnake->x[pSnake->nLength - 1] == pSnake->x[i] && pSnake->y[pSnake->nLength - 1] == pSnake->y[i]) return TRUE; } return FALSE;}
一切的开始——游戏开始
bool StartGame(){ unsigned char chOld = 77; unsigned char chNow = 77; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //定义蛇 Snake snake; //定义食物 Food food; //制作游戏窗口 MakeFrame(hOut); //初始化蛇 InitSnake(&snake); //产生食物 MakeFood(hOut, &snake, &food); while (1) { //打印菜单信息 PrintfMenu(hOut, &snake, &food); tail[0] = snake.x[0]; //记住蛇尾的横坐标 tail[1] = snake.y[0]; //记住蛇尾的纵坐标 bool speedUp = FALSE; //判断键盘是否按下 if (kbhit()) { chNow = getch(); //Sleep(10); if (kbhit()) speedUp = TRUE; } switch (chNow) { case 72: //向上 { MoveSnake(hOut, &snake); if (chOld == 80) //如果蛇调头,则无视调头继续走 { snake.y[snake.nLength - 1] += 1; chNow = chOld; break; } chOld = chNow; snake.y[snake.nLength - 1] -= 1; break; } case 80: //向下 { MoveSnake(hOut, &snake); if (chOld == 72) { snake.y[snake.nLength - 1] -= 1; chNow = chOld; break; } chOld = chNow; snake.y[snake.nLength - 1] += 1; break; } case 75: //向左 { MoveSnake(hOut, &snake); if (chOld == 77) { snake.x[snake.nLength - 1] += 2; chNow = chOld; break; } chOld = chNow; snake.x[snake.nLength - 1] -= 2; break; } case 77: //向右 { MoveSnake(hOut, &snake); if (chOld == 75) { snake.x[snake.nLength - 1] -= 2; chNow = chOld; break; } chOld = chNow; snake.x[snake.nLength - 1] += 2; break; } case 27: //ESC键 break; case 32: //暂停 { break; } default: //去掉其他按键的影响 chNow = chOld; break; } //判断是否要出暂停提示 if (chNow != 32) //清空暂停的提示 { SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 16); printf(" "); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } else//显示暂停指示 { SetConsoleTextAttribute(hOut, FOREGROUND_INTENSITY | FOREGROUND_RED); SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 16); printf("已暂停,按方向键可继续游戏"); SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } //穿墙 ThroughWall(hOut, &snake, chNow); //判断有无吃食物 EatFood(hOut, &snake, &food); //打印蛇 PrintfSnake(hOut, &snake); /* 游戏结束条件: 1. 蛇碰到自身 2. 按ESC键 */ if (IfSnakeDie(&snake) == TRUE || chNow == 27) { SetPosition(hOut, FRAMEX + FRAMEWIDTH - 2, FRAMEY + FRAMEHEIGHT / 2 - 1); bool State = OverGame(hOut, &snake); return State; } PrintfMenu(hOut, &snake, &food); if (speedUp == FALSE) { //SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 9); //printf(" "); Sleep(snake.nSpeed); } else { //SetPosition(hOut, FRAMEX + FRAMEWIDTH * 2 + 5, FRAMEY + 9); //printf("正在加速..."); Sleep(10); } } }
一切的结束——结束游戏
bool OverGame(HANDLE hOut, Snake* pSnake){ system("cls"); SetPosition(hOut, FRAMEX + FRAMEWIDTH + 10, FRAMEY + FRAMEHEIGHT / 4); printf("Game Over"); //打印得分 if (pSnake->nCount < 20) { SetPosition(hOut, FRAMEX + FRAMEWIDTH, FRAMEY + FRAMEHEIGHT / 4 + 2); printf("好可惜哦!你的游戏得分为:%d分, 要不再来一盘突破自己?", pSnake->nCount); } else if (pSnake->nCount < 30) { SetPosition(hOut, FRAMEX + FRAMEWIDTH, FRAMEY + FRAMEHEIGHT / 4 + 2); printf("不错哦!你的游戏得分为:%d分, 我猜你还没拿出实力吧", pSnake->nCount); } else { SetPosition(hOut, FRAMEX + FRAMEWIDTH, FRAMEY + FRAMEHEIGHT / 4 + 2); printf("哇好腻害(⊙o⊙)!你的游戏得分为:%d分, 你的手速已经超越地球人了", pSnake->nCount); } SetPosition(hOut, FRAMEX + FRAMEWIDTH, FRAMEY + FRAMEHEIGHT/4 + 4); printf("重新开始游戏:Enter 退出游戏:ESC"); int userKey = 0; while (1) { if ((userKey = getch()) == 13) return FALSE; else if ((userKey = getch()) == 27) return TRUE; else continue; }}
上帝控制台——主函数
int main(){ while(1) { //开始游戏 system("cls"); bool State= StartGame(); if (State) break; } return 0;}
- C语言制作游戏——贪吃蛇
- 【C语言编程设计】字符游戏——贪吃蛇
- C语言贪吃蛇游戏
- C语言:贪吃蛇游戏
- C语言游戏:贪吃蛇
- 贪吃蛇—C语言
- C语言之贪吃蛇游戏源码
- C 语言 win32 游戏编程 - 贪吃蛇
- 基于c语言的贪吃蛇游戏
- c语言"贪吃蛇"游戏源码
- C语言贪吃蛇游戏2
- 贪吃蛇游戏的C语言实现
- C语言小项目--贪吃蛇游戏
- C语言之贪吃蛇游戏
- 纯C语言贪吃蛇游戏
- 字符游戏——简单贪吃蛇的制作
- 贪吃蛇——C语言实现
- C语言小游戏——贪吃蛇
- 9月笔试
- selenium自动化学习一环境搭建
- POJ1837-Balance(01背包)
- Mapped Statements collection does not contain value for
- Java 基础 —— Objects and Classes
- C语言制作游戏——贪吃蛇
- 【threejs学习随记(三)】3D模型导入问题
- [快速幂] POJ-3641 Pseudoprime numbers
- Refetion2017.9.7
- Servlet 生命周期
- java JAR 将两个类文件放在同一目录的方法
- UVa1610 习题8-2 聚会游戏
- centos['sentəʊs]
- android路径相关