贪吃蛇小游戏(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
贪吃蛇大作战怎么打好排位赛 贪吃蛇大作战打好排位赛攻略

网址: 贪吃蛇小游戏(c语言实现)解决bug http://www.hyxgl.com/newsview329887.html

推荐资讯