2020博客之星年度总评选进行中:请为74号的狗子投上宝贵的一票!
我的投票地址:点击为我投票
写在前面:
大家好,我是 花狗Fdog ,来自内蒙古的一个小城市,目前在泰州读书。
很感谢能有这样一个平台让我能够在这里分享所学所感。
我喜欢编程,喜欢代码,喜欢去做一个程序员。
努力学习,争取多年后,给亲人更好的生活。
文章目录
- 0.前言
- 1.游戏效果
- 2.相关变量
- 3.定义链表
- 4.食物坐标
- 5.绘制初始界面和游戏地图
- 6.初始化蛇身
- 7.产生食物
- 8.游戏刷新和暂停 ,按回车可暂停游戏。
- 9.显示分数和难度,更新分数和难度。
- 10.移动光标
- 11.检测判断
- 12.游戏循环
- 13.释放内存
- 14.主函数内容
- 15.完整代码奉上,也可自行下载。
0.前言
这是我在博客写的第一篇文章,如果哪里有问题还请多多指教!!以前我写的贪吃蛇是全图刷新的,导致在窗口运行时,眼睛都快闪瞎了!今天为大家带来了不闪的贪吃蛇!!!废话不多说,上图,上代码!!
1.游戏效果
2.相关变量
int JudgeSum = 0; //判断是否加快
int Pause = 200000000; //暂停速度(移动速度)
int * PJ = &JudgeDirection; //用指针传值判断移动方向
nakebody *end = NULL; //尾节点
3.定义链表
贪吃蛇的身体如何保存是游戏的核心,所以我们需要用到链表来保存蛇的身体,这样就可以随时知道蛇身数据。
typedef struct Snakebody
{
int x, y; //蛇身的坐标
struct Snakebody *next;//保存下一个蛇身的地址
}Snakebody; //通过typedef将 Snakebody 替代 struct Snakebody
4.食物坐标
typedef struct Snakexy
{
int x;
int y;
}Snakexy; //记录食物坐标
5.绘制初始界面和游戏地图
#include<Windows.h>
#define HEIGHT 20 //设置地图高度
#define WIDTH 40 //设置地图宽度
#define PRINTF printf("■");
#define LINE printf("\n");
#define EMPTY printf(" "); //因为这三个语句经常用,所以我就定义成了宏
void Front(); //绘制初始界面
void DeawMap(); //绘制地图
void Front()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
MoveCursor(18, 11);
printf(" 贪 吃 蛇 ");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);//设置绿色
MoveCursor(18, 12);
printf("作者:花狗");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//设置蓝色
MoveCursor(18, 13);
printf("QQ:2506897252");
MoveCursor(18, 14);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
MoveCursor(18, 15);
printf("请等待......");
for (int i = 0; i <= 3000000000; i++){}
system("cls");//清屏处理
}
void DeawMap()
{
for (int i = 0; i < WIDTH; i++)PRINTF LINE //打印上边框
for (int i = 1; i < HEIGHT - 1; i++) //打印左右边框
{
for (int j = 0; j < WIDTH; j++)
{
if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
{
PRINTF
if (j == WIDTH - 1)LINE
}
else EMPTY
}
}
for (int i = 0; i < WIDTH; i++)PRINTF LINE //打印下边框
}
SetConsoleTextAttribute()函数是一个API设置字体颜色和背景色的函数。参数表中使用两个属性(属性之间用,隔开),不同于system(),SetConsoleTextAttribute()可以改变界面多种颜色,而system()只能修改为一种!。
6.初始化蛇身
刚开始蛇不应该只要一个头,所以我们必须创建几个身体。
Snakebody *Phead = NULL; //存储着整个蛇身 不可更改
Snakebody *Phead_1 = NULL; //指向蛇身
Snakebody *Pbady = NULL; //创建节点
void ISnake(); //初始化蛇身
void ISnake()
{
for (int i = 0; i < 5; i++)//初始化蛇身拥有五个长度
{
Pbady = (Snakebody*)malloc(sizeof(Snakebody));//创建节点
Pbady->x = 5 - i;
Pbady->y = 5;
if (Phead == NULL)
{
Phead = Pbady;
}
else
{
end->next = Pbady;
}
Pbady->next = NULL;
end = Pbady;
}
Phead_1 = Phead;
while (Phead_1->next != NULL)//打印蛇身
{
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
Phead_1 = Phead_1->next;
}
}
7.产生食物
随机产生食物,如果和蛇身体重合则再次随机产生食物。
#include<time.h>
int sum = 0; //计算得分
Snakexy * Food = NULL; //保存食物位置
void FoodRand(); //生成食物
void FoodRand()
{
srand((int)time(0));
int x = rand() % 27 + 2;//生成随机数
int y = rand() % 17 + 2;
Phead_1 = Phead;
for (int i = 0; i <= 200; i++)
{
if (Phead_1->x == x&&Phead_1->y == y)
{
x = rand() % 27 + 2;
y = rand() % 17 + 2;
}
else
{
Phead_1 = Phead_1->next;
}
if (Phead_1->next == NULL)
{
break;
}
}
MoveCursor(x, y);
PRINTF
Food = (Snakexy*)malloc(sizeof(Snakexy));
Food->x = x;
Food->y = y;
MoveCursor(33, 5);
printf(" ");
Showf();
sum++;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
}
rand函数功能为获取一个伪随机数,如要产生[m,n]范围内的随机数num,可用int num=rand()%(n-m+1)+m;
8.游戏刷新和暂停 ,按回车可暂停游戏。
int JudgeDirection = 4; //判断方向
void ControlMove(); //控制移动和暂停
void ControlMove()
{
if (GetAsyncKeyState(VK_UP) && 0x8000)
{
if (JudgeDirection == 2)
{
}
else
{
JudgeDirection = 1;
}
}
if (GetAsyncKeyState(VK_DOWN) && 0x8000)
{
if (JudgeDirection == 1)
{
}
else
{
JudgeDirection = 2;
}
}
if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
{
if (JudgeDirection == 3)
{
}
else
{
JudgeDirection = 4;
}
}
if (GetAsyncKeyState(VK_LEFT) && 0x8000)
{
if (JudgeDirection == 4)
{
}
else
{
JudgeDirection = 3;
}
}
if (GetAsyncKeyState(VK_RETURN) && 0x0D)//判断回车
{
while (1)
{
if (GetAsyncKeyState(VK_RETURN) && 0x0D)//再次回车退出死循环
{
break;
}
}
}
}
GetAsyncKeyState()确定用户当前是否按下了键盘上的一个键
9.显示分数和难度,更新分数和难度。
int sum = 0; //计算得分
int Hard = 0; //计算难度
void Showf(); //显分数以及难度
void Showf()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
MoveCursor(33, 5);
printf("得分:%d", sum);
MoveCursor(33, 6);
printf("难度:%d", Hard);
}
10.移动光标
游戏不闪的原因就是我们只绘制一次地图 然后用光标定点刷新目标点。
void MoveCursor(int x, int y); //移动光标
void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置)
{
COORD pos = { x * 2,y };
HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄
SetConsoleCursorPosition(output, pos); //设置光标位置
}
COORD是Windows API中定义的一种结构体
11.检测判断
检测是否吃到食物,是否撞墙,是否撞到自己。
void Jfood(); //检测是否吃到食物
void Jwall(); //检测蛇头是否撞墙
void Jsnake(); //检测蛇头是否撞到蛇身
void Jfood()
{
Phead_1 = Phead;
if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
{
FoodRand();
JudgeSum += 1;
if (JudgeSum == 5)
{
JudgeSum = 0;//如果JudgeSum等于5则从新判断
Hard += 1;
Pause -= 20000000;//每成立一次循环减少20000000
}
while (Phead_1->next != NULL)
{
Phead_1 = Phead_1->next;
}
Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
S->x = Food->x;
S->y = Food->y;
S->next = NULL;
Phead_1->next = S;
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
}
//获取食物的坐标和蛇头做对比
}
void Jwall()
{
if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
}
void Jsnake()
{
Phead_1 = Phead->next;
while (Phead_1->next != NULL)
{
if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
Phead_1 = Phead_1->next;
}
}
12.游戏循环
void Move(); //游戏运行
void Move()
{
while (1)
{
Phead_1 = Phead;
while (Phead_1->next->next != NULL)
{
Phead_1 = Phead_1->next;
}
Phead_1->next = NULL;
for (int i = 0; i < Pause; i++) {}
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
EMPTY
//上面为消除尾部
Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
if (*PJ == 1)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y - 1;
}
if (*PJ == 2)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y + 1;
}
if (*PJ == 3)
{
Phead_2->x = Phead->x - 1;
Phead_2->y = Phead->y;
}
if (*PJ == 4)
{
Phead_2->x = Phead->x + 1;
Phead_2->y = Phead->y;
}
Phead_2->next = Phead;
Phead = Phead_2;
MoveCursor(Phead_2->x, Phead_2->y);
PRINTF
Jfood();
Jwall();
Jsnake();
MoveCursor(40, 20);
}
}
13.释放内存
void Free(); //释放内存
void Free()
{
while (Phead->next != NULL)
{
Phead=Phead->next;
free(Phead);
}
free(Phead);
}
14.主函数内容
int main()
{
Front();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//绿
DeawMap();
Showf();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
MoveCursor(34, 10);
printf("↑");
MoveCursor(31, 11);
printf("使用←↓→来控制");
MoveCursor(31, 12);
printf("蛇的移动,撞墙游");
MoveCursor(31, 13);
printf("戏结束,每5分增 ");
MoveCursor(31, 14);
printf("一个难度(速度)");
ISnake();
FoodRand();
MoveCursor(40, 20);
Move();
Free();
return 0;
}
15.完整代码奉上,也可自行下载。
#include<stdio.h>
#include<time.h>
#include<Windows.h>
#define HEIGHT 20 //设置地图高度
#define WIDTH 40 //设置地图宽度
#define PRINTF printf("■");
#define LINE printf("\n");
#define EMPTY printf(" ");
typedef struct Snakebody
{
int x, y;//身体的坐标
struct Snakebody *next;//结构指针
}Snakebody;//先来创建保持身体的链表,贪吃蛇的核心代码就是该如何保存蛇的身体
typedef struct Snakexy
{
int x;
int y;
}Snakexy; //记录食物坐标
int sum = 0; //计算得分
int JudgeSum = 0; //判断是否加快
int Hard = 0; //计算难度
int Pause = 200000000; //暂停速度(移动速度)
int JudgeDirection = 4; //判断方向
int * PJ = &JudgeDirection; //用指针传值判断移动方向
Snakebody *Phead = NULL; //存储着整个蛇身 不可更改
Snakebody *Phead_1 = NULL; //指向蛇身
Snakebody *Pbady = NULL; //创建节点
Snakebody *end = NULL; //尾节点
Snakexy * Food = NULL; //保存食物位置
void Front(); //游戏开始页面1
void Jfood(); //检测是否吃到食物1
void Jwall(); //检测蛇头是否撞墙1
void Jsnake(); //检测蛇头是否撞到蛇身1
void ISnake(); //初始化蛇身1
void DeawMap(); //绘制地图1
void FoodRand(); //生成食物1
void ControlMove(); //控制移动和暂停1
void MoveCursor(int x, int y); //移动光标1
void Move(); //游戏运行1
void Showf(); //显分数以及难度1
void Free(); //释放内存
int main()
{
Front();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//绿
DeawMap();
Showf();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
MoveCursor(34, 10);
printf("↑");
MoveCursor(31, 11);
printf("使用←↓→来控制");
MoveCursor(31, 12);
printf("蛇的移动,撞墙游");
MoveCursor(31, 13);
printf("戏结束,每5分增 ");
MoveCursor(31, 14);
printf("一个难度(速度)");
ISnake();
FoodRand();
MoveCursor(40, 20);
Move();
return 0;
}
void Front()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
MoveCursor(18, 11);
printf(" 贪 吃 蛇 ");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);//设置绿色
MoveCursor(18, 12);
printf("作者:花狗");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//设置蓝色
MoveCursor(18, 13);
printf("QQ:2506897252");
MoveCursor(18, 14);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
MoveCursor(18, 15);
printf("请等待......");
for (int i = 0; i <= 3000000000; i++){}
system("cls");
}
void DeawMap()
{
for (int i = 0; i < WIDTH; i++)PRINTF LINE //上边框
for (int i = 1; i < HEIGHT - 1; i++) //打印左右边框
{
for (int j = 0; j < WIDTH; j++)
{
if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
{
PRINTF
if (j == WIDTH - 1)LINE
}
else EMPTY
}
}
for (int i = 0; i < WIDTH; i++)PRINTF LINE //下边框
}
void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置)
{
/* COORD是Windows API中定义的一种结构体
* typedef struct _COORD
* {
* SHORT X;
* SHORT Y;
* } COORD;
* */
COORD pos = { x * 2,y };
HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得 标准输出的句柄
SetConsoleCursorPosition(output, pos); //设置控制台光标位置
}
void FoodRand()
{
srand((int)time(0));
int x = rand() % 27 + 2;
int y = rand() % 17 + 2;
Phead_1 = Phead;
for (int i = 0; i <= 200; i++)
{
if (Phead_1->x == x&&Phead_1->y == y)
{
x = rand() % 27 + 2;
y = rand() % 17 + 2;
}
else
{
Phead_1 = Phead_1->next;
}
if (Phead_1->next == NULL)
{
break;
}
}
MoveCursor(x, y);
PRINTF
Food = (Snakexy*)malloc(sizeof(Snakexy));
Food->x = x;
Food->y = y;
MoveCursor(33, 5);
printf(" ");
Showf();
sum++;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
}
void ControlMove()
{
if (GetAsyncKeyState(VK_UP) && 0x8000)
{
if (JudgeDirection == 2)
{
}
else
{
JudgeDirection = 1;
}
}
if (GetAsyncKeyState(VK_DOWN) && 0x8000)
{
if (JudgeDirection == 1)
{
}
else
{
JudgeDirection = 2;
}
}
if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
{
if (JudgeDirection == 3)
{
}
else
{
JudgeDirection = 4;
}
}
if (GetAsyncKeyState(VK_LEFT) && 0x8000)
{
if (JudgeDirection == 4)
{
}
else
{
JudgeDirection = 3;
}
}
if (GetAsyncKeyState(VK_RETURN) && 0x0D)
{
while (1)
{
if (GetAsyncKeyState(VK_RETURN) && 0x0D)
{
break;
}
}
}
}
void ISnake()
{
for (int i = 0; i < 5; i++)
{
Pbady = (Snakebody*)malloc(sizeof(Snakebody));
Pbady->x = 5 - i;
Pbady->y = 5;
if (Phead == NULL)
{
Phead = Pbady;
}
else
{
end->next = Pbady;
}
Pbady->next = NULL;
end = Pbady;
}
Phead_1 = Phead;
while (Phead_1->next != NULL)
{
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
Phead_1 = Phead_1->next;
}
}
void Move()
{
while (1)
{
Phead_1 = Phead;
while (Phead_1->next->next != NULL)
{
Phead_1 = Phead_1->next;
}
Phead_1->next = NULL;
for (int i = 0; i < Pause; i++) {}
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
EMPTY
//上面为消除尾部
Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
if (*PJ == 1)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y - 1;
}
if (*PJ == 2)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y + 1;
}
if (*PJ == 3)
{
Phead_2->x = Phead->x - 1;
Phead_2->y = Phead->y;
}
if (*PJ == 4)
{
Phead_2->x = Phead->x + 1;
Phead_2->y = Phead->y;
}
Phead_2->next = Phead;
Phead = Phead_2;
MoveCursor(Phead_2->x, Phead_2->y);
PRINTF
Jfood();
Jwall();
Jsnake();
MoveCursor(40, 20);
}
}
void Jfood()
{
Phead_1 = Phead;
if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
{
FoodRand();
JudgeSum += 1;
if (JudgeSum == 5)
{
JudgeSum = 0;
Hard += 1;
Pause -= 20000000;
}
while (Phead_1->next != NULL)
{
Phead_1 = Phead_1->next;
}
Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
S->x = Food->x;
S->y = Food->y;
S->next = NULL;
Phead_1->next = S;
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
}
//获取食物的坐标和蛇头做对比
}
void Jwall()
{
if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
}
void Jsnake()
{
Phead_1 = Phead->next;
while (Phead_1->next != NULL)
{
if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
Phead_1 = Phead_1->next;
}
}
void Showf()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
MoveCursor(33, 5);
printf("得分:%d", sum);
MoveCursor(33, 6);
printf("难度:%d", Hard);
}
void Free()
{
while (Phead->next != NULL)
{
Phead=Phead->next;
free(Phead);
}
free(Phead);
}
如有不对,欢迎指出,期待我的下一篇文章。
每文一句:梦是心灵的思想,是我们的秘密真情。