🌟作者介绍:友友们好我是钓鱼的猫猫,可以叫我小猫💕
⏳作者主页:钓鱼的猫猫-CSDN博客🎉
👀项目专栏:项目_钓鱼的猫猫的博客-CSDN博客🎉
贪吃蛇需要提前知识
🎈 1. 从零拿捏链表(一)—— 无头单向不循环链表(单链表)-CSDN博客
🎈 2.动态内存管理-CSDN博客
🎈 3.Windows控制台编程核心API详解:从入门到应用-CSDN博客
🎉小猫和友友们一样喜欢代码🤭。很荣幸能向大家分享我的所学所感,和大家一起进步,成为合格的卷王。✨如果文章有错误,欢迎在评论区✏️指正。那么开始今天的学习吧!😘
目录
总游戏思路
思路大概是这个思路
一.游戏的各种初始化
1.1 游戏的设置(大小,光标,提示,地图)
1.2 游戏的信息(游戏的状态,贪吃蛇的信息,食物的信息)
1.3 食物的初始化
二.游戏的运行
2.1 打印固定的信息(打印什么按键对于蛇的移动)
2.2 检测按键状态
2.3 蛇的移动(蛇的移动,是否结束的判断)
三.游戏的结束
3.1 游戏结束的提示信息
3.2 释放内存空间
这个时候有读者会问为什么要食物初始化,因为吃了食物之后,这个节点会变成蛇的节点,因此初始了蛇的节点,也要初始食物的节点
一. 初始化
1.1 游戏的设置
1.1.1 光标定位
void SetPos(int x, int y)
{//获得句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);//光标移动到x,y位置
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
void SetPos(int x, int y)
{
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
1.1.2 窗口设置
控制这个窗口的大小,还有左上角的信息
system("mode con lines=30 cols=100 "); //30行,100列
system("title CSDN 钓鱼的猫猫"); //左上角信息
system("mode con lines=30 cols=100 ");
system("title CSDN 钓鱼的猫猫");
1.1.3 光标的隐藏
光标的隐藏
界面无光标闪烁
//获得句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);//设置光标隐藏信息
CONSOLE_CURSOR_INFO cur_info = { 0 };
GetConsoleCursorInfo(houtput, &cur_info);
cur_info.bVisible = false;
SetConsoleCursorInfo(houtput,&cur_info);
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cur_info = { 0 };
GetConsoleCursorInfo(houtput, &cur_info);
cur_info.bVisible = false;
SetConsoleCursorInfo(houtput,&cur_info);
1.1.4 开始界面
两个页面信息的打印
void WelcomeToGame(void)
{
SetPos(40, 14);
printf("欢迎来到贪吃蛇游戏\n");
SetPos(42, 20);system("pause");
system("cls");
SetPos(35, 14);
printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
SetPos(35, 15);
printf("加速更高分数\n");
SetPos(39, 16);
system("pause");
system("cls");}
void WelcomeToGame(void)
{
SetPos(40, 14);
printf("欢迎来到贪吃蛇游戏\n");
SetPos(42, 20);
system("pause");
system("cls");
SetPos(35, 14);
printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
SetPos(35, 15);
printf("加速更高分数\n");
SetPos(39, 16);
system("pause");
system("cls");
}
1.1.5 地图创建
地图的贪吃蛇的边框打印
void CreateMap(void)
{
//上
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}//下
SetPos(0, 26);
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}//左
for (int i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", L'□');
}
//右
for (int i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", L'□');
}
}
void CreateMap(void)
{
//上
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//下
SetPos(0, 26);
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//左
for (int i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", L'□');
}
//右
for (int i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", L'□');
}
}
1.2 游戏的信息
1.2.1 贪吃蛇的初始化
ps->_pSnake = NULL;
//一.贪吃蛇的初始化
//①.申请五个节点
SnakeSingleNode* newnode = NULL;
for (int i = 0; i < 5; i++)
{
newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}//设置节点信息
newnode->x = POS_X + 2 * i;
newnode->y = POS_Y;
newnode->next = NULL; // 关键修复:初始化 next 指针
if (ps->_pSnake == NULL)//如果只有一个节点
{
ps->_pSnake = newnode;
}
else//如果很多节点
{
//头插
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
}
}//②插入完要打印
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
//光标定位
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
ps->_pSnake = NULL;
//一.贪吃蛇的初始化
//①.申请五个节点
SnakeSingleNode* newnode = NULL;
for (int i = 0; i < 5; i++)
{
newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
//设置节点信息
newnode->x = POS_X + 2 * i;
newnode->y = POS_Y;
newnode->next = NULL; // 关键修复:初始化 next 指针
if (ps->_pSnake == NULL)//如果只有一个节点
{
ps->_pSnake = newnode;
}
else//如果很多节点
{
//头插
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
}
}
//②插入完要打印
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
//光标定位
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
1.2.2 其他信息初始化
//二.其他信息初始化
//①.开始方向为向右
ps->_dir = RIGHT;//②开始分数为0
ps->_score = 0;//③每个食物的分数
ps->_food_weight = 10;//④蛇的速度
ps->_sleep_time = 100;//⑤游戏的状态
ps->status = OK;
//二.其他信息初始化
//①.开始方向为向右
ps->_dir = RIGHT;
//②开始分数为0
ps->_score = 0;
//③每个食物的分数
ps->_food_weight = 10;
//④蛇的速度
ps->_sleep_time = 100;
//⑤游戏的状态
ps->status = OK;
1.3 食物的初始化
1.3.1 生成食物的坐标
//一.生成食物的坐标
int x = 0; int y = 0;
again:
do
{
//①生成随机的坐标
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//让坐标必须是x为2的倍数//③x 和 y 不能是蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur != NULL)
{
if ((x == cur->x) && (y == cur->y))
{
goto again;
}
cur = cur->next;
}
//一.生成食物的坐标
int x = 0; int y = 0;
again:
do
{
//①生成随机的坐标
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//让坐标必须是x为2的倍数
//③x 和 y 不能是蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur != NULL)
{
if ((x == cur->x) && (y == cur->y))
{
goto again;
}
cur = cur->next;
}
1.3.2 创建食物节点
//二.此时已经创建好节点
//创建食物的节点
SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}newnode->x = x;
newnode->y = y;newnode->next = NULL;
//二.此时已经创建好节点
//创建食物的节点
SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
newnode->x = x;
newnode->y = y;
newnode->next = NULL;
1.3.3 打印食物
//三.创建完节点应该打印食物信息
SetPos(x, y);
wprintf(L"%lc", L'★');
//四.记录该信息
ps->_pfood = newnode;
//三.创建完节点应该打印食物信息
SetPos(x, y);
wprintf(L"%lc", L'★');
//四.记录该信息
ps->_pfood = newnode;
二. 游戏的运行
2.1 固定信息打印
void PrintfHelpInit(void)
{
SetPos(64, 13);
wprintf(L"%ls", L"1. 不能穿墙\n");
SetPos(64, 14);
wprintf(L"%ls", L"2. ↑ ↓ ← → 控制蛇的移动\n");
SetPos(64, 15);
wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
SetPos(64, 16);
wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
SetPos(64, 18);
wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}
void PrintfHelpInit(void)
{
SetPos(64, 13);
wprintf(L"%ls", L"1. 不能穿墙\n");
SetPos(64, 14);
wprintf(L"%ls", L"2. ↑ ↓ ← → 控制蛇的移动\n");
SetPos(64, 15);
wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
SetPos(64, 16);
wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
SetPos(64, 18);
wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}
2.2 按键检测
//二.检测按键
do
{
//①打印总分数和食物的分值
SetPos(64, 10);
printf("总分数:%d\n", ps->_score);
SetPos(64,11);
printf("当前食物分数:%2d\n",ps->_food_weight);//②检测按键
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_SPACE) )//判断空格按键
{
Pause();
}
else if (KEY_PRESS(VK_ESCAPE))//判断是否退出按键
{
ps->status = END_NORMAL;
}
else if (KEY_PRESS(VK_F3))//判断F3按键
{
//加速=》时间变短
if (ps->_sleep_time > 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_F4))//判断F4按键
{
//减速=》时间变长
if (ps->_food_weight > 2)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;
}
}
//蛇的移动
SnakeMove(ps);
Sleep(ps->_sleep_time);} while (ps->status == OK);
//二.检测按键
do
{
//①打印总分数和食物的分值
SetPos(64, 10);
printf("总分数:%d\n", ps->_score);
SetPos(64,11);
printf("当前食物分数:%2d\n",ps->_food_weight);
//②检测按键
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_SPACE) )
{
Pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->status = END_NORMAL;
}
else if (KEY_PRESS(VK_F3))
{
//加速=》时间变短
if (ps->_sleep_time > 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
//减速=》时间变长
if (ps->_food_weight > 2)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;
}
}
//蛇的移动
SnakeMove(ps);
Sleep(ps->_sleep_time);
} while (ps->status == OK);
2.3 蛇的移动
2.3.1 蛇的移动
//蛇的移动函数
void SnakeMove(Snake* ps)
{
//1.创建一个节点
SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
//移动
switch (ps->_dir)
{
case UP:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y - 1;
break;
case DOWN:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y + 1;
break;
case LEFT:
newnode->x = ps->_pSnake->x-2;
newnode->y = ps->_pSnake->y;
break;
case RIGHT:
newnode->x = ps->_pSnake->x + 2;
newnode->y = ps->_pSnake->y;
break;
}//2.检测下一个节点是否是食物
if (NextIsFood(newnode, ps))
{
EatFood(newnode, ps);
}
else
{
NoFood(newnode, ps);
}//3.检测是否撞墙
KillByWall(ps);//4.检测是否撞自己
KillBySelf(ps);}
//蛇的移动函数
void SnakeMove(Snake* ps)
{
//1.创建一个节点
SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
//移动
switch (ps->_dir)
{
case UP:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y - 1;
break;
case DOWN:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y + 1;
break;
case LEFT:
newnode->x = ps->_pSnake->x-2;
newnode->y = ps->_pSnake->y;
break;
case RIGHT:
newnode->x = ps->_pSnake->x + 2;
newnode->y = ps->_pSnake->y;
break;
}
//2.检测下一个节点是否是食物
if (NextIsFood(newnode, ps))
{
EatFood(newnode, ps);
}
else
{
NoFood(newnode, ps);
}
//3.检测是否撞墙
KillByWall(ps);
//4.检测是否撞自己
KillBySelf(ps);
}
2.3.2 判断下一个节点是否是食物
int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
return 1;
else return 0;
}
int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
return 1;
else return 0;
}
2.3.3 下个节点是食物
void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
ps->_pfood ->next = ps->_pSnake;
ps->_pSnake = ps->_pfood;free(newnode);
newnode = NULL;SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}//加分
ps->_score += ps->_food_weight;
//重新创建食物
FoodInit(ps);
}
void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
ps->_pfood ->next = ps->_pSnake;
ps->_pSnake = ps->_pfood;
free(newnode);
newnode = NULL;
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
//加分
ps->_score += ps->_food_weight;
//重新创建食物
FoodInit(ps);
}
2.3.4 下个节点不是食物
void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
//把最后两个位置打印成两个空格
//最后一个节点释放掉
SnakeSingleNode* cur = ps->_pSnake;
while (cur->next->next != NULL)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
//把最后两个位置打印成两个空格
//最后一个节点释放掉
SnakeSingleNode* cur = ps->_pSnake;
while (cur->next->next != NULL)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
2.3.2 是否撞墙
//如果撞墙
void KillByWall(Snake* ps)
{
if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
{
ps->status = KILL_BY_WALL;
}
}
//如果撞墙
void KillByWall(Snake* ps)
{
if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
{
ps->status = KILL_BY_WALL;
}
}
2.3.4 是否撞自己
//如果撞自己
void KillBySelf(Snake* ps)
{
SnakeSingleNode* cur = ps->_pSnake->next;
while (cur)
{
if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
{
ps->status = KILL_BY_SELF;
break;
}
cur = cur->next;
}
}
//如果撞自己
void KillBySelf(Snake* ps)
{
SnakeSingleNode* cur = ps->_pSnake->next;
while (cur)
{
if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
{
ps->status = KILL_BY_SELF;
break;
}
cur = cur->next;
}
}
三. 游戏的结束
3.1 游戏的结束
//游戏的状态
void GameOver(Snake* ps)
{
SetPos(24, 12);
switch (ps->status)
{
case END_NORMAL:
printf("您的游戏正常结束\n");
break;
case KILL_BY_WALL:
printf("您的游戏撞上墙\n");
break;
case KILL_BY_SELF:
printf("您的游戏撞到了自己\n");
break;
}
//释放蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SnakeSingleNode* del = cur;
cur = cur->next;
free(del);
}
}
//游戏的状态
void GameOver(Snake* ps)
{
SetPos(24, 12);
switch (ps->status)
{
case END_NORMAL:
printf("您的游戏正常结束\n");
break;
case KILL_BY_WALL:
printf("您的游戏撞上墙\n");
break;
case KILL_BY_SELF:
printf("您的游戏撞到了自己\n");
break;
}
//释放蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SnakeSingleNode* del = cur;
cur = cur->next;
free(del);
}
}
四.总代码
4.1 Game.c
#include "Snake.h"
Snake message;
void game()
{
//一.游戏各种的初始化
Snake* ps = &message;
//①游戏的设置(大小,光标,提示,地图)
GameInit();
//②游戏的信息(游戏的状态,贪吃蛇的信息,食物的信息)
SnakeInit(ps);
//③食物的初始化
FoodInit(ps);
//二.游戏的运行
GameRun(ps);
//游戏的结束
GameOver(ps);
}
int main()
{
setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
game();
return 0;
}
4.2 Snake.h
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
#include <locale.h>
#include <stdbool.h>
#define POS_X 24
#define POS_Y 5
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1 ) ? 1 : 0 )
//游戏的状态
enum GAME_STATUS
{
OK, //正常
KILL_BY_WALL, //蛇撞墙
KILL_BY_SELF, //蛇撞自己
END_NORMAL //正常退出
};
//蛇的方向
enum Direction
{
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4
};
//蛇的节点信息
typedef struct SnakeSingleNode
{
int x;
int y;
struct SnakeSingleNode* next;
}SnakeSingleNode;
//游戏的各种信息
typedef struct Snake
{
//一.系统的信息
//①游戏的状态
enum GAME_STATUS status;
//二.蛇的信息
//①找到蛇头
SnakeSingleNode* _pSnake;
//②蛇的方向
enum Direction _dir;
//③蛇的速度
int _sleep_time;
//三.食物信息
//①食物的节点
SnakeSingleNode* _pfood;
//②当前食物的分数
int _food_weight;
//③吃掉食物的总分数
int _score;
}Snake;
void GameInit(void);
void WelcomeToGame(void);
void SetPos(int x, int y);
void CreateMap(void);
void SnakeInit(Snake* ps);
void FoodInit(Snake* ps);
void GameRun(Snake* ps);
void PrintfHelpInit(void);
void Pause(void);
int NextIsFood(SnakeSingleNode* newnode, Snake* ps);
void NoFood(SnakeSingleNode* newnode, Snake* ps);
void EatFood(SnakeSingleNode* newnode, Snake* ps);
void KillByWall(Snake * ps);
void KillBySelf(Snake* ps);
void GameOver(Snake *ps);
4.3 Snake.c
#include "Snake.h"
//光标定位
void SetPos(int x, int y)
{
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
void CreateMap(void)
{
//上
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//下
SetPos(0, 26);
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//左
for (int i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", L'□');
}
//右
for (int i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", L'□');
}
}
//环境界面的打印
void WelcomeToGame(void)
{
SetPos(40, 14);
printf("欢迎来到贪吃蛇游戏\n");
SetPos(42, 20);
system("pause");
system("cls");
SetPos(35, 14);
printf("用↑ ↓ ← →控制蛇的移动 F3加速 F4减速\n");
SetPos(35, 15);
printf("加速更高分数\n");
SetPos(39, 16);
system("pause");
system("cls");
}
//信息的初始化
void SnakeInit(Snake* ps)
{
ps->_pSnake = NULL;
//一.贪吃蛇的初始化
//①.申请五个节点
SnakeSingleNode* newnode = NULL;
for (int i = 0; i < 5; i++)
{
newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
//设置节点信息
newnode->x = POS_X + 2 * i;
newnode->y = POS_Y;
newnode->next = NULL; // 关键修复:初始化 next 指针
if (ps->_pSnake == NULL)//如果只有一个节点
{
ps->_pSnake = newnode;
}
else//如果很多节点
{
//头插
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
}
}
//②插入完要打印
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
//光标定位
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
//二.其他信息初始化
//①.开始方向为向右
ps->_dir = RIGHT;
//②开始分数为0
ps->_score = 0;
//③每个食物的分数
ps->_food_weight = 10;
//④蛇的速度
ps->_sleep_time = 100;
//⑤游戏的状态
ps->status = OK;
}
void GameInit(void)
{
//一.设置
//①大小
system("mode con lines=30 cols=100 ");
system("title CSDN 钓鱼的猫猫");
//②光标隐藏
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cur_info = { 0 };
GetConsoleCursorInfo(houtput, &cur_info);
cur_info.bVisible = false;
SetConsoleCursorInfo(houtput,&cur_info);
//二.提示信息
//①开始界面
WelcomeToGame();
//②地图创建
CreateMap();
}
void FoodInit(Snake* ps)
{
//一.生成食物的坐标
int x = 0; int y = 0;
again:
do
{
//①生成随机的坐标
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//让坐标必须是x为2的倍数
//③x 和 y 不能是蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur != NULL)
{
if ((x == cur->x) && (y == cur->y))
{
goto again;
}
cur = cur->next;
}
//二.此时已经创建好节点
//创建食物的节点
SnakeSingleNode * newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
newnode->x = x;
newnode->y = y;
newnode->next = NULL;
//三.创建完节点应该打印食物信息
SetPos(x, y);
wprintf(L"%lc", L'★');
//四.记录该信息
ps->_pfood = newnode;
}
void PrintfHelpInit(void)
{
SetPos(64, 13);
wprintf(L"%ls", L"1. 不能穿墙\n");
SetPos(64, 14);
wprintf(L"%ls", L"2. ↑ ↓ ← → 控制蛇的移动\n");
SetPos(64, 15);
wprintf(L"%ls", L"3. 按F3加速,按F4减速\n");
SetPos(64, 16);
wprintf(L"%ls", L"4. 按Esc退出,按空格暂停\n");
SetPos(64, 18);
wprintf(L"%ls", L"CSDN 钓鱼的猫猫\n");
}
//暂停函数
void Pause(void)
{
while (1)
{
Sleep(200);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
int NextIsFood(SnakeSingleNode* newnode, Snake* ps)
{
if (ps->_pfood->x == newnode->x && ps->_pfood->y == newnode->y)
return 1;
else return 0;
}
void EatFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
ps->_pfood ->next = ps->_pSnake;
ps->_pSnake = ps->_pfood;
free(newnode);
newnode = NULL;
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
//加分
ps->_score += ps->_food_weight;
//重新创建食物
FoodInit(ps);
}
//下一个是节点
void NoFood(SnakeSingleNode* newnode, Snake* ps)
{
//头插法
newnode->next = ps->_pSnake;
ps->_pSnake = newnode;
//把最后两个位置打印成两个空格
//最后一个节点释放掉
SnakeSingleNode* cur = ps->_pSnake;
while (cur->next->next != NULL)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", L'●');
cur = cur->next;
}
SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
//如果撞墙
void KillByWall(Snake* ps)
{
if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
{
ps->status = KILL_BY_WALL;
}
}
//如果撞自己
void KillBySelf(Snake* ps)
{
SnakeSingleNode* cur = ps->_pSnake->next;
while (cur)
{
if(cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
{
ps->status = KILL_BY_SELF;
break;
}
cur = cur->next;
}
}
//蛇的移动函数
void SnakeMove(Snake* ps)
{
//1.创建一个节点
SnakeSingleNode* newnode = (SnakeSingleNode*)malloc(sizeof(SnakeSingleNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
//移动
switch (ps->_dir)
{
case UP:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y - 1;
break;
case DOWN:
newnode->x = ps->_pSnake->x;
newnode->y = ps->_pSnake->y + 1;
break;
case LEFT:
newnode->x = ps->_pSnake->x-2;
newnode->y = ps->_pSnake->y;
break;
case RIGHT:
newnode->x = ps->_pSnake->x + 2;
newnode->y = ps->_pSnake->y;
break;
}
//2.检测下一个节点是否是食物
if (NextIsFood(newnode, ps))
{
EatFood(newnode, ps);
}
else
{
NoFood(newnode, ps);
}
//3.检测是否撞墙
KillByWall(ps);
//4.检测是否撞自己
KillBySelf(ps);
}
//游戏运行函数
void GameRun(Snake* ps)
{
//一.首先是固定的打印信息
PrintfHelpInit();
//二.检测按键
do
{
//①打印总分数和食物的分值
SetPos(64, 10);
printf("总分数:%d\n", ps->_score);
SetPos(64,11);
printf("当前食物分数:%2d\n",ps->_food_weight);
//②检测按键
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_SPACE) )
{
Pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->status = END_NORMAL;
}
else if (KEY_PRESS(VK_F3))
{
//加速=》时间变短
if (ps->_sleep_time > 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
//减速=》时间变长
if (ps->_food_weight > 2)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;
}
}
SnakeMove(ps);
Sleep(ps->_sleep_time);
} while (ps->status == OK);
}
void GameOver(Snake* ps)
{
SetPos(24, 12);
switch (ps->status)
{
case END_NORMAL:
printf("您的游戏正常结束\n");
break;
case KILL_BY_WALL:
printf("您的游戏撞上墙\n");
break;
case KILL_BY_SELF:
printf("您的游戏撞到了自己\n");
break;
}
//释放蛇的节点
SnakeSingleNode* cur = ps->_pSnake;
while (cur)
{
SnakeSingleNode* del = cur;
cur = cur->next;
free(del);
}
}
五. 最后
小猫尽力了,请见谅!😘
那么今天的学习就到这里了。友友们觉得不错的可以给个关注,点赞或者收藏哦!😘感谢各位友友们的支持。希望各位大佬们自行检验哦,毕竟亲手操作让记忆更加深刻。
你的❤️点赞是我创作的动力的源泉
你的✨收藏是我奋斗的方向
你的🙌关注是对我最大的支持
你的✏️评论是我前进的明灯
创作不易,希望友友们支持一下小猫吧😘