贪吃蛇小游戏(c语言实现)解决bug
首先在这里非常非常非常感谢博主 2021dragon 的贪吃蛇游戏程序让我学到了很多。
如果以下代码看不懂,一定要到博主那里学习(博主那有详细完美的解析),然后再看我的就可 以了
原文链接:[引用内容](贪吃蛇(C语言实现)_贪吃蛇c语言代码-CSDN博客)
看不懂没关系多思考几遍,上机按照博主的思路写一遍(就是抄,但是要有思考的抄)
我就是这样明白的,后面再不断思考,也是花了几天时间,就明白了
《在博主 2021dragon 贪吃蛇(c语言实现)bug:“长按一个方向键,蛇就会暂停的问题进行探讨”》
1.我在基于博主代码上增加了游戏的初始界面:开始游戏,更多设置(选择颜色,难度)
2.并使蛇可以加速移动(也决绝了博主留下的bug)
3.并且优化我喜欢的代码顺序
4.置于颜色,难度选择,后续我应该可以用指针解决或者别的方法。以及后续对程序再优化(过几天...)
下面是我的代码,解释Bug在后面。
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#define ROW 22 //游戏区行数
#define COL 42 //游戏区列数
#define KONG 0 //标记空(什么也没有)
#define WALL 1 //标记墙
#define FOOD 2 //标记食物
#define HEAD 3 //标记蛇头
#define BODY 4 //标记蛇身
#define UP 72 //方向键:上
#define DOWN 80 //方向键:下
#define LEFT 75 //方向键:左
#define RIGHT 77 //方向键:右
#define SPACE 32 //暂停
#define ESC 27 //退出
//
//蛇头
struct Snake
{
int len; //记录蛇身长度
int x; //蛇头横坐标
int y; //蛇头纵坐标
}snake;
//蛇身
struct Body
{
int x; //蛇身横坐标
int y; //蛇身纵坐标
}body[ROW * COL]; //开辟足以存储蛇身的结构体数组
int face[ROW][COL]; //标记游戏区各个位置的状态
void HideCursor();//隐藏光标
void CursorJump(int x, int y);///光标跳转
void InitInterface(); // 初始化界面
void color(int c);//颜色设置
void InitSnake();//初始化蛇
void RandFood();//随机生成食物
void JudgeFunc(int x, int y);//判断得分与结束
void DrawSnake(int flag);//打印蛇与覆盖蛇
void MoveSnake(int x, int y);//移动蛇
void run(int x, int y);//执行按键
void Game();//游戏主体逻辑函数
void mainn();
void tcs(); //第二界面在界面这块我就用到了函数嵌套(函数一层套一层),该贪吃蛇游戏不只是界面,后续蛇的运动也是用到函数的嵌套
void w();//3.1界面
void ww();//3.2界面
void www();//3.3界面
void Jiem2();//返回第二界面(通用)
int grade; //全局变量
int main()
{
char ch;
printf("开始游戏(请输入Y or N)n");
printf("n");
printf("更多设置(请输入W)n");
printf("n");
scanf_s("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
printf("准备进入游戏...");
Sleep(1000);
mainn();
}
else if (ch == 'N' || ch == 'n')
{
printf("正在退出程序中,请等待...");
Sleep(500);
system("cls");
}
else if (ch == 'W' || ch == 'w')
{
system("cls");
tcs();
}
else
{
printf("输入错误,请重新输入");
Sleep(300);
system("cls");
main();
}
return 0;
}
//十三:第二界面
void tcs()
{
printf("选择蛇的颜色(按1)n");
printf("n");
printf("设置难度(按2)n");
printf("n");
int ch;
scanf_s("%d", &ch);
if (ch == 1)
{
system("cls");
w();//3.1函数
}
else if (ch == 2)
{
system("cls");
ww();//3.2函数
}
else
{
system("cls");
printf("输入错误nn返回第一界面(1)nn退出游戏(按0)nn");
www();//3.3函数
}
}
//3.1界面
void w()
{
printf("蓝色,绿色,红色(输入1 2 3进行选择)n");
int ch;
int a;//先这样,后面在搞指针指向颜色
//我之所以定义个int a,是为了验证我代码输入,输出是否正确。后续有时间了我在进行优化,加入颜色和难度)
scanf_s("%d", &ch);
if (ch == 1)
{
a = 1;
printf("%d", a);
}
else if (ch == 2)
{
a = 2;
printf("%d", a);
}
else if (ch == 3)
{
a = 3;
printf("%d", a);
}
else
{
printf("输入错误,请重新输入n");
printf("返回第二界面(Y)n退出游戏(N)");
Jiem2();
}
}
//3.2界面
void ww()
{
printf("难度:1 2 3 (输入1 2 3进行选择)n");
int ch;
int a;//先这样,后面在搞指针指向void run()里面的t
scanf_s("%d", &ch);
if (ch == 1)
{
a = 1;
printf("%d", a);
}
else if (ch == 2)
{
a = 2;
printf("%d", a);
}
else if (ch == 3)
{
a = 3;
printf("%d", a);
}
else
{
printf("输入错误,请重新输入n");
printf("返回第二界面(Y)n退出游戏(N)");
Jiem2();
}
}
//3.3界面
void www()
{
int ch;
scanf_s("%d", &ch);
if (ch == 1)
{
main();
}
else if (ch == 0)
{
printf("正在退出程序中,请等待...");
Sleep(500);
system("cls");
}
else
{
printf("输入错误,请重新输入");
printf("返回第二界面(Y)n退出游戏(N)");
Jiem2();
}
}
//返回第二界面(通用)
void Jiem2()
{
printf("返回第二界面(Y)n退出游戏(N)");
char ch;
scanf_s("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
tcs();
}
else if (ch == 'N' || ch == 'n')
{
printf("正在退出程序中,请等待...");
Sleep(500);
system("cls");
}
else
{
printf("输入错误,请重新输入");
Sleep(300);
system("cls");
Jiem2();
}
}
//十二:第一界面
void mainn()
{
#pragma warning(disable:4996)//消除警告
grade = 0;
system("title 贪吃蛇");//cmd窗口名字
system("mode con cols=84 lines=23 ");//cmd窗口大小
HideCursor();//一
InitInterface();//三
InitSnake();//五
srand((unsigned int)time(NULL)); //设置随机数生成起点好像可以去除
RandFood();//六
DrawSnake(1);//七
Game();//九
}
//一隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
curInfo.bVisible = FALSE; //将光标设置为不可见
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}
//二:光标跳转
void CursorJump(int x, int y)
{
COORD pos; //定义光标位置的结构体变量
pos.X = x; //横坐标
pos.Y = y; //纵坐标
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
SetConsoleCursorPosition(handle, pos); //设置光标位置
}
//三:初始化界面
void InitInterface()
{
color(6); //颜色设置为土黄色
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (j == 0 || j == COL - 1)
{
face[i][j] = WALL; //标记该位置为墙
CursorJump(2 * j, i);
printf("■");
}
else if (i == 0 || i == ROW - 1)
{
face[i][j] = WALL; //标记该位置为墙
CursorJump(2 * j, i);
printf("■");
}
else
{
face[i][j] = KONG; //标记该位置为空
}
}
}
color(7); //颜色设置为白色
CursorJump(0, ROW);
printf("当前得分:%d", grade);
}
//颜色设置
void color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
}
//五:初始化蛇
void InitSnake()
{
snake.len = 2; //蛇的身体长度初始化为2
snake.x = COL / 2; //蛇头位置的横坐标
snake.y = ROW / 2; //蛇头位置的纵坐标
//蛇身坐标的初始化
body[0].x = COL / 2 - 1;
body[0].y = ROW / 2;
body[1].x = COL / 2 - 2;
body[1].y = ROW / 2;
//将蛇头和蛇身位置进行标记
face[snake.y][snake.x] = HEAD;
face[body[0].y][body[0].x] = BODY;
face[body[1].y][body[1].x] = BODY;
}
//六:食物生成(随机)
void RandFood()
{
int i, j;
do
{
//随机生成食物的横纵坐标
i = rand() % ROW;
j = rand() % COL;
} while (face[i][j] != KONG); //确保生成食物的位置为空,若不为空则重新生成
face[i][j] = FOOD; //将食物位置进行标记
color(12); //颜色设置为红色
CursorJump(2 * j, i); //光标跳转到生成的随机位置处
printf("●"); //打印食物●
}
//七:打印蛇体(头,身),与覆盖
void DrawSnake(int flag)
{
if (flag == 1) //打印蛇
{
color(9); //颜色设置为蓝色
CursorJump(2 * snake.x, snake.y);
printf("■"); //打印蛇头
for (int i = 0; i < snake.len; i++)
{
CursorJump(2 * body[i].x, body[i].y);
printf("□"); //打印蛇身
}
}
else //覆盖蛇
{
if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
{
//将蛇尾覆盖为空格即可
CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
printf(" ");
}
}
}
//八:移动蛇//,然后再打印移动后的蛇
void MoveSnake(int x, int y)
{
DrawSnake(0); //先覆盖当前所显示的蛇
face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空*************** 否则蛇再次碰到原来路径结束游戏
face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
//蛇移动后各个蛇身位置坐标需要更新
for (int i = snake.len - 1; i > 0; i--)
{
body[i].x = body[i - 1].x;
body[i].y = body[i - 1].y;
}
//蛇移动后蛇头位置信息变为第0个蛇身的位置信息
body[0].x = snake.x;
body[0].y = snake.y;
//蛇头的位置更改
snake.x = snake.x + x;
snake.y = snake.y + y;
DrawSnake(1); //打印移动后的蛇
}
//九:游戏主体逻辑函数
void Game()
{
int n = RIGHT; //开始游戏时,默认向后移动
int tmp = 0; //记录蛇的移动方向
goto first; //第一次进入循环先向默认方向前进
while (1)
{
n = getch(); //读取键值
//在执行前,需要对所读取的按键进行调整
n = getch(); //读取键值
//在执行前,需要对所读取的按键进行调整
switch (n)
{
case UP:
if (tmp != LEFT && tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
{
if (tmp == DOWN)//如果上一次蛇的移动方向是“下”
{
n = DOWN; //那么下一次蛇的移动方向设置为“下”
JudgeFunc(0, 1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, 1); //移动蛇
}
else
{
n = UP;
JudgeFunc(0, -1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, -1); //移动蛇
}
}
break;
case DOWN: //如果敲击的是“上”
if (tmp != LEFT && tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
{
if (tmp == UP)//那么下一次蛇的移动方向设置为“上”
{
n = UP;
JudgeFunc(0, -1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, -1); //移动蛇
}
else
{
n = DOWN;
JudgeFunc(0, 1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, 1); //移动蛇
}
}
break;
case LEFT:
if (tmp != UP && tmp != DOWN)
{
if (tmp == RIGHT)
{
n = RIGHT;
JudgeFunc(1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(1, 0); //移动蛇
}
else
{
n = LEFT;
JudgeFunc(-1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(-1, 0); //移动蛇
}
}
break;
case RIGHT:
if (tmp != UP && tmp != DOWN)
{
if (tmp == LEFT)
{
n = LEFT;
JudgeFunc(-1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(-1, 0); //移动蛇
}
else
{
n = RIGHT;
JudgeFunc(1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(1, 0); //移动蛇
}
}
break;
case SPACE:
case ESC:
case 'r':
case 'R':
break; //这四个无需调整
default:
n = tmp; //其他键无效,默认为上一次蛇移动的方向
break;
}
first: //第一次进入循环先向默认方向前进
switch (n)
{
case UP: //方向键:上
run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
tmp = UP; //记录当前蛇的移动方向
break;
case DOWN: //方向键:下
run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
tmp = DOWN; //记录当前蛇的移动方向
break;
case LEFT: //方向键:左
run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
tmp = LEFT; //记录当前蛇的移动方向
break;
case RIGHT: //方向键:右
run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
tmp = RIGHT; //记录当前蛇的移动方向
break;
case SPACE: //暂停
system("pause>nul"); //暂停后按任意键继续
break;
case ESC: //退出
system("cls"); //清空屏幕
color(7); //颜色设置为白色
CursorJump(COL - 8, ROW / 2);
printf(" 游戏结束 ");
CursorJump(COL - 8, ROW / 2 + 2);
exit(0);
case 'r':
case 'R': //重新开始
system("cls"); //清空屏幕
main(); //重新执行主函数
}
}
}
//十判断得分与结束
void JudgeFunc(int x, int y)
{
//若蛇头即将到达的位置是食物,则得分
if (face[snake.y + y][snake.x + x] == FOOD)
{
snake.len++; //蛇身加长
grade += 10; //更新当前得分
color(7); //颜色设置为白色
CursorJump(0, ROW);
printf("当前得分:%d", grade); //重新打印当前得分
RandFood(); //重新随机生成食物
}
//若蛇头即将到达的位置是墙或者蛇身,则游戏结束
else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
{
Sleep(1000); //留给玩家反应时间
system("cls"); //清空屏幕
color(7); //颜色设置为白色
CursorJump(2 * (COL / 3), ROW / 2);
printf("GAME OVER");
while (1) //询问玩家是否再来一局
{
char ch;
CursorJump(2 * (COL / 3), ROW / 2 + 3);
printf("再来一局?(y/n):");
scanf("%c", &ch);
if (ch == 'y' || ch == 'Y')
{
system("cls");
mainn();
}
else if (ch == 'n' || ch == 'N')
{
CursorJump(2 * (COL / 3), ROW / 2 + 5);
exit(0);
}
}
}
}
//十一:自动移动
void run(int x, int y)
{
int t = 0;
while (1)
{
if (t == 0)
t = 3000; //这里t越小,速度越快
//解释 : 因为下面的while(--t),当t=5000,自减到0,系统也是要点时间,并不是瞬间完成。
// 故我们可以利用t来设置游戏难度。(根据大一的学习内容,应该使用指针来解决,同理,界面文字的颜色也可以用指针)
while (--t)
{
if (kbhit() != 0) //若键盘被敲击,则退出循环
break;
}
if (t == 0) //键盘未被敲击
{
JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
MoveSnake(x, y); //移动蛇
}
else //键盘被敲击
{
break; //返回Game函数读取键值
}
}
}
bug:
while (1)
{
n = getch(); //读取键值
//在执行前,需要对所读取的按键进行调整
switch (n)
{
case UP:
case DOWN: //如果敲击的是“上”或“下”
if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
{
n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
}
break;
case LEFT:
case RIGHT: //如果敲击的是“左”或“右”
if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
{
n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
}
case SPACE:
case ESC:
case 'r':
case 'R':
break; //这四个无需调整
default:
n = tmp; //其他键无效,默认为上一次蛇移动的方向
break;
}
————————————————
版权声明:本文为CSDN博主「2021dragon」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chenlong_cxy/article/details/119765659
bug解决:
解决思路:
为什么我们长按方向键会停止呢?
首先明白为什么不按方向键蛇可以自动移动?
就在于 run(int x,int y)函数
当键盘未敲击时,反复进行 if(t==0)语句,JudgeFunc(x, y)和MoveSnake(x, y)函数,
MoveSnake(x, y)就是移动蛇,从而蛇自动移动
当键盘敲击时,就返回Game函数。这时如果我们长按方向键,我们只反复进行Game 和run函数
并没有进行MoveSnake(x, y)函数,故蛇不会移动。
所以我只要在进行Game函数时加入个MoveSnake(x, y)函数即可解决bug。
为什么长按会加速,我觉得是kbhit()的原因,长按方向键系统就快速反复运行Game 和run函数(相较于间断按)(比喻:我们键盘是输入A,长按1s——AAAAAAAAAAA,间断按1s——AAAA)
(这时有的人会问为什么我长按后会加速一段时间才停止,而不是立即停止以及蛇为什么会加速
对于此,我没有解决,我只解决了长按方向键会停止的bug)
void run(int x, int y)
{
int t = 0;
while (1)
{
if (t == 0)
t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
while (--t)
{
if (kbhit() != 0) //若键盘被敲击,则退出循环
break;
}
if (t == 0) //键盘未被敲击
{
JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
MoveSnake(x, y); //移动蛇
}
else //键盘被敲击
{
break; //返回Game函数读取键值
}
}
}
Bug解决代码:
n = getch(); //读取键值
//在执行前,需要对所读取的按键进行调整
switch (n)
{
case UP:
if (tmp != LEFT && tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
{
if (tmp == DOWN)//如果上一次蛇的移动方向是“下”
{
n = DOWN;//那么下一次蛇的移动方向设置为“下”
JudgeFunc(0, 1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, 1); //移动蛇
}
else
{
n = UP;
JudgeFunc(0, -1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, -1); //移动蛇
}
}
break;
case DOWN: //如果敲击的是“上”
if (tmp != LEFT && tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
{
if (tmp == UP)那么下一次蛇的移动方向设置为“上”
{
n = UP;
JudgeFunc(0, -1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, -1); //移动蛇
}
else
{
n = DOWN;
JudgeFunc(0, 1); //判断到达该位置后,是否得分与游戏结束
MoveSnake(0, 1); //移动蛇
}
}
break;
case LEFT:
if (tmp != UP && tmp != DOWN)
{
if (tmp == RIGHT)
{
n = RIGHT;
JudgeFunc(1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(1, 0); //移动蛇
}
else
{
n = LEFT;
JudgeFunc(-1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(-1, 0); //移动蛇
}
}
break;
case RIGHT:
if (tmp != UP && tmp != DOWN)
{
if (tmp == LEFT)
{
n = LEFT;
JudgeFunc(-1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(-1, 0); //移动蛇
}
else
{
n = RIGHT;
JudgeFunc(1, 0); //判断到达该位置后,是否得分与游戏结束
MoveSnake(1, 0); //移动蛇
}
}
}
相关知识
C/C++快速入门图形化教学法简介(目录)
小朋友玩的贪吃蛇的游戏有哪些 受人欢迎的贪吃蛇手游排行榜
解决《The Isle》游戏卡Bug问题的方法,《The Isle》游戏常见Bug问题及其解决策略 ...
贪吃蛇怎么复活不了 贪吃蛇大作战复活技巧
找传奇世界bug:揭秘与解决策略
原神游戏出现bug怎么解决
各位玩家自己解决游戏BUG的新方法~实用
dnf游戏使者小游戏怎么玩 地下城5.7新增小游戏玩法 已解决
测试:bug的生命周期、bug的等级、如何描述一个bug
贪吃蛇大作战怎么打好排位赛 贪吃蛇大作战打好排位赛攻略
推荐资讯
- 1老六爱找茬美女的烦恼怎么过- 5070
- 2博德之门3黄金雏龙法杖怎么得 4901
- 3《大侠立志传》剿灭摸金门任务 4362
- 4代号破晓官方正版角色介绍 4058
- 5赛马娘锻炼到底的伙伴支援卡事 3845
- 6闪烁之光11月兑换码大全20 3814
- 7爆梗找茬王厕所特工怎么通关- 3585
- 8原神原海异种刷怪路线-原神原 3577
- 9《我的世界》领地删除指令是什 3531
- 10原神开局星落湖怎么出去 原神 3461