C语言实现贪吃蛇小游戏
✅博客主页:爆打维c-CSDN博客 🐾
🔹分享c语言知识及代码 🐾
目录
游戏展示视频
一、项目准备工作
二、功能实现分析
1.游戏开始
a.设置本地化、创建窗口、标题
b.隐藏光标,封装定位光标的函数
c.打印欢迎界面及提示信息
d.打印游戏地图及右侧的提示信息
e.创建蛇
f.创建食物
2.游戏运行
a.打印右侧帮助信息
b.按键判断
c.蛇移动过程中
3.游戏结束
a.打印游戏结束原因
b.销毁蛇结点
c.销毁食物结点
三、游戏代码实现如下
snack.h
snack.cpp
test.cpp
游戏展示视频
c语言 贪吃蛇游戏实现
一、项目准备工作
1. 打开VS新建一个空项目
2. 创建三个文件
- snack.h 写游戏需要的数据类型和函数声明
- snack.cpp 写函数的定义
- test.cpp 写游戏的测试逻辑
准备工作就绪,现在我们来分析需要实现的功能
二、功能实现分析
1.游戏开始
a.设置本地化、创建窗口、标题
需要用到的函数:
int system (const char* command); //头文件为<stdlib.h> 用于执行系统指令
char *setlocale(int category, const char *locale);
//头文件为<locate.h> 用于设置或查询程序的本地化信息
//设置为本地模式 支持宽字符(汉字)的输出
setlocale(LC_ALL, "");
//设置标题
system("title 贪吃蛇");
//设置窗体长和宽
system("mode con cols=100 lines=30");
b.隐藏光标,封装定位光标的函数
//获取句柄
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursor_info; //包含有关控制台游标的信息的结构体
GetConsoleCursorInfo(houtput, &cursor_info); //获取控制台光标信息
cursor_info.bVisible = false; //设置光标可见度
SetConsoleCursorInfo(houtput, &cursor_info); //设置光标
//定位光标的函数
void Setpos(short x,short y) {
//获取句柄
HANDLE handle = NULL;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
//设置光标位置
COORD pos = { x,y };
SetConsoleCursorPosition(handle, pos);
}
对于Win32 API控制台函数 的使用不清楚的同学可以去查阅官方文档,里面给出了详细的解释
控制台函数 - Windows Console | Microsoft Learnhttps://learn.microsoft.com/zh-cn/windows/console/console-functions
c.打印欢迎界面及提示信息
实现效果如图
void WelcomeToGame() {
Setpos(40, 15);
printf("欢迎来到贪吃蛇游戏!\n");
Setpos(40, 25); //设置按任意键继续的位置
system("pause");
system("cls");
Setpos(25, 13);
printf("使用按键“↑↓←→”控制蛇的移动,按F3加速,F4减速\n");
Setpos(40, 25); //设置按任意键继续的位置
system("pause");
system("cls");
}
d.打印游戏地图及右侧的提示信息
void PrintMap() {
int i = 0;
//打印上横排
for (i = 0; i < 58; i+=2) { //0~56
Setpos(i, 0);
wprintf(L"%c",WALL);
}
//打印下横排
for (i = 0; i < 58; i+=2) { //0~56
Setpos(i, 26);
wprintf(L"%c", WALL);
}
//打印左竖排
for (i = 1; i < 26; i++) {
Setpos(0, i);
wprintf(L"%c", WALL);
}
//打印右竖排
for (i = 1; i < 26; i++) {
Setpos(56, i);
wprintf(L"%c", WALL);
}
}
e.创建蛇
先在snack.h中声明维护蛇的结构体
//蛇的方向
enum DIRECTTION {
UP = 1,
DOWN,
LEFT,
RIGHT
};
//蛇的状态
enum GAME_STATUS {
OK, //存活
KILL_BY_SELF, //撞到自己
KILL_BY_WALL, //撞墙死亡
END_NORMAL //正常退出
};
//蛇节点结构体 单链表
typedef struct SnackNode {
int x;
int y;
struct SnackNode* next;
}SnakcNode, * pSnackNode;
//蛇结构体 维护蛇的信息
typedef struct Snack {
int _sleep_time; //休眠时间越短 蛇越快
int _foodscore; //一个食物的得分
int _score; //总得分
enum DIRECTTION _dir;//蛇的方向
enum GAME_STATUS _status; //蛇的状态
pSnackNode _pfood; //指向食物的指针
pSnackNode _phead; //指向蛇头的指针
}Snack, * pSnack;
初始化蛇身
//初始化蛇身
void InitSnack(pSnack ps){
//蛇身初始化五个节点
int i = 0;
pSnackNode cur = NULL;
for (i = 0; i < 5; i++) {
cur = (pSnackNode)malloc(sizeof(SnackNode));
if (cur == NULL) {
perror("malloc cur fail");
return;
}
cur->next = NULL;
cur->x = SNACK_X + i * 2;
cur->y = SNACK_Y;
if (ps->_phead == NULL) { //空链表
ps->_phead = cur;
}
else {
//头插法
cur->next = ps->_phead;
ps->_phead = cur;
}
}
cur = ps->_phead;
while (cur) {
Setpos(cur->x, cur->y);
wprintf(L"%c",BODY);
cur = cur->next;
}
//设置其他属性
ps->_dir = RIGHT;
ps->_foodscore = 20;
ps->_score = 0;
ps->_sleep_time = 200;
ps->_status = OK;
}
f.创建食物
//创建食物
void CreateFood(pSnack ps) {
int x=0, y=0;
again:
do {
x = rand() % 53 + 2; //2~54
y = rand() % 25 + 1;//1~25
} while (x % 2 != 0);//x必须要是2的倍数
//x,y坐标不能与蛇身重合
pSnackNode cur = ps->_phead;
while (cur) {
if (cur->x == x && cur->y == y) {
goto again;
}
cur = cur->next;
}
//创建食物节点
pSnackNode pfood = (pSnackNode)malloc(sizeof(SnackNode));
if (!pfood) {
perror("malloc pfood fail");
return;
}
else {
pfood->x = x;
pfood->y = y;
ps->_pfood = pfood;
Setpos(x, y);
wprintf(L"%c",FOOD);
}
}
2.游戏运行
a.打印右侧帮助信息
b.按键判断
判断用户是否按键
- 按下上下左右则改变蛇的方向 f3则加速 f4则减速 暂停键 退出键
c.蛇移动过程中
判断蛇下一步往哪走 是否吃掉食物?
- 如果吃掉食物那么蛇身增长 加分
- 如果没吃掉食物 那么往前移动
判断是否存活
- 如果存活 继续游戏
- 如果撞到自己或撞到墙 游戏结束
3.游戏结束
a.打印游戏结束原因
b.销毁蛇结点
c.销毁食物结点
三、游戏代码实现如下
snack.h
#pragma once
#include<stdio.h>
#include<Windows.h>
#include<locale.h>
#include<stdbool.h>
#define BODY L'●'
#define FOOD L'★'
#define WALL L'□'
#define SNACK_X 26
#define SNACK_Y 5
//判断?个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1)?1:0)
//蛇的方向
enum DIRECTTION {
UP = 1,
DOWN,
LEFT,
RIGHT
};
//蛇的状态
enum GAME_STATUS {
OK, //存活
KILL_BY_SELF, //撞到自己
KILL_BY_WALL, //撞墙死亡
END_NORMAL //正常退出
};
//蛇节点结构体 单链表
typedef struct SnackNode {
int x;
int y;
struct SnackNode* next;
}SnakcNode, * pSnackNode;
//蛇结构体 维护蛇的信息
typedef struct Snack {
int _sleep_time; //休眠时间越短 蛇越快
int _foodscore; //一个食物的得分
int _score; //总得分
enum DIRECTTION _dir;//蛇的方向
enum GAME_STATUS _status; //蛇的状态
pSnackNode _pfood; //指向食物的指针
pSnackNode _phead; //指向蛇头的指针
}Snack, * pSnack;
void Setpos(short x, short y);
void WelcomeToGame();
void My_time();
void PrintMap();
void CreateFood(pSnack ps);
void GameStart(pSnack ps);
void GameRun(pSnack ps);
void GameOver(pSnack ps);
snack.cpp
//函数的声明
#include"snake.h"
void Setpos(short x,short y) {
//获取句柄
HANDLE handle = NULL;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
//设置光标位置
COORD pos = { x,y };
SetConsoleCursorPosition(handle, pos);
}
void WelcomeToGame() {
Setpos(40, 15);
printf("欢迎来到贪吃蛇游戏!\n");
Setpos(40, 25); //设置按任意键继续的位置
system("pause");
system("cls");
Setpos(25, 13);
printf("使用按键“↑↓←→”控制蛇的移动,按F3加速,F4减速\n");
Setpos(40, 25); //设置按任意键继续的位置
system("pause");
system("cls");
}
void PrintMap() {
int i = 0;
//打印上横排
for (i = 0; i < 58; i+=2) { //0~56
Setpos(i, 0);
wprintf(L"%c",WALL);
}
//打印下横排
for (i = 0; i < 58; i+=2) { //0~56
Setpos(i, 26);
wprintf(L"%c", WALL);
}
//打印左竖排
for (i = 1; i < 26; i++) {
Setpos(0, i);
wprintf(L"%c", WALL);
}
//打印右竖排
for (i = 1; i < 26; i++) {
Setpos(56, i);
wprintf(L"%c", WALL);
}
}
//初始化蛇身
void InitSnack(pSnack ps){
//蛇身初始化五个节点
int i = 0;
pSnackNode cur = NULL;
for (i = 0; i < 5; i++) {
cur = (pSnackNode)malloc(sizeof(SnackNode));
if (cur == NULL) {
perror("malloc cur fail");
return;
}
cur->next = NULL;
cur->x = SNACK_X + i * 2;
cur->y = SNACK_Y;
if (ps->_phead == NULL) { //空链表
ps->_phead = cur;
}
else {
//头插法
cur->next = ps->_phead;
ps->_phead = cur;
}
}
cur = ps->_phead;
while (cur) {
Setpos(cur->x, cur->y);
wprintf(L"%c",BODY);
cur = cur->next;
}
//设置其他属性
ps->_dir = RIGHT;
ps->_foodscore = 20;
ps->_score = 0;
ps->_sleep_time = 200;
ps->_status = OK;
}
//创建食物
void CreateFood(pSnack ps) {
int x=0, y=0;
again:
do {
x = rand() % 53 + 2; //2~54
y = rand() % 25 + 1;//1~25
} while (x % 2 != 0);//x必须要是2的倍数
//x,y坐标不能与蛇身重合
pSnackNode cur = ps->_phead;
while (cur) {
if (cur->x == x && cur->y == y) {
goto again;
}
cur = cur->next;
}
//创建食物节点
pSnackNode pfood = (pSnackNode)malloc(sizeof(SnackNode));
if (!pfood) {
perror("malloc pfood fail");
return;
}
else {
pfood->x = x;
pfood->y = y;
ps->_pfood = pfood;
Setpos(x, y);
wprintf(L"%c",FOOD);
}
}
void GameStart(pSnack ps) {
//设置窗体长和宽
system("mode con cols=100 lines=30");
//获取句柄
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursor_info;
GetConsoleCursorInfo(houtput, &cursor_info); //获取控制台光标信息
cursor_info.bVisible = false; //设置光标可见度
SetConsoleCursorInfo(houtput, &cursor_info); //设置光标
//1.打印欢迎窗口
//2.功能展示
WelcomeToGame();
//3.打印地图
PrintMap();
//4.创建蛇
InitSnack(ps);
//5.创建食物
CreateFood(ps);
}
void PrintHelpInfo() {
Setpos(62, 14);
printf("不能撞到墙,不能撞到自己");
Setpos(62,15);
printf("使用按键“↑↓←→”控制蛇的移动");
Setpos(62,16);
printf("按F3加速,F4减速");
Setpos(62, 17);
printf("按空格暂停游戏,按ESC退出游戏");
}
void Pause() {
while (1) {
Sleep(200);
if (KEY_PRESS(VK_SPACE)) {
break;
}
}
}
int NextisFood(pSnackNode psn,pSnack ps) {
return (psn->x == ps->_pfood->x) && (psn->y == ps->_pfood->y);
}
void EatFood(pSnackNode psn, pSnack ps) {
//头插法
psn->next = ps->_phead;
ps->_phead = psn;
//加分
ps->_score += ps->_foodscore;
//打印蛇身
pSnackNode cur = ps->_phead;
while (cur) {
Setpos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
//释放食物节点
free(ps->_pfood);
//创建新的⻝物
CreateFood(ps);
}
void NoFood(pSnackNode psn, pSnack ps){
psn->next = ps->_phead;
ps->_phead = psn;
pSnackNode cur = ps->_phead;
while (cur->next->next!=NULL) {
Setpos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
//最后一个节点空格覆盖
Setpos(cur->next->x, cur->next->y);
printf(" ");
//释放最后一个节点 并把此时的蛇尾next指针置为空
free(cur->next);
cur->next = NULL;
}
int KillbySelf(pSnack ps) {
//判断蛇身与蛇头是否重合
pSnackNode cur = ps->_phead->next;
while (cur) {
if (ps->_phead->x == cur->x && ps->_phead->y == cur->y) { //撞到自己
ps->_status = KILL_BY_SELF;
return 1;
}
cur = cur->next;
}
return 0;
}
int KillbyWall(pSnack ps) {
//判断蛇头是否撞墙
if (ps->_phead->x == 0 || ps->_phead->x == 56 || ps->_phead->y == 0 || ps->_phead->y == 26) {
ps->_status = KILL_BY_WALL;
return 1;
}
return 0;
}
void SnackMove(pSnack ps) {
//创建一个新的节点 表示蛇下一步到的位置
pSnackNode pNextNode = (pSnackNode)malloc(sizeof( SnackNode));
if (!pNextNode) {
perror("SnakeMove()::malloc()");
return;
}
//判断下一步的方向
switch (ps->_dir) {
case UP:
pNextNode->x = ps->_phead->x;
pNextNode->y = ps->_phead->y - 1;
break;
case DOWN:
pNextNode->x = ps->_phead->x;
pNextNode->y = ps->_phead->y + 1;
break;
case LEFT:
pNextNode->x = ps->_phead->x - 2;
pNextNode->y = ps->_phead->y;
break;
case RIGHT:
pNextNode->x = ps->_phead->x + 2;
pNextNode->y = ps->_phead->y;
break;
}
if (NextisFood(pNextNode,ps)) {
EatFood(pNextNode, ps);
}
else {
NoFood(pNextNode, ps);
}
KillbySelf(ps);
KillbyWall(ps);
}
void GameRun(pSnack ps) {
PrintHelpInfo(); //打印右侧帮助信息
//按键判断
do {
Setpos(62, 10);
printf("总得分:%d",ps->_score);
Setpos(62, 11);
printf("一个食物的分数:%d", ps->_foodscore);
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_RIGHT) && ps->_dir != LEFT) {
ps->_dir = RIGHT;
}
else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT) {
ps->_dir = LEFT;
}
else if (KEY_PRESS(VK_ESCAPE)) {
ps->_status = END_NORMAL; //正常退出
break;
}
else if (KEY_PRESS(VK_SPACE)) {
Pause(); //让页面暂停 再按一次空格恢复
}
else if (KEY_PRESS(VK_F3)) {
//加速
if (ps->_sleep_time < 320) {
ps->_sleep_time -= 30;
ps->_foodscore += 10;
}
}
else if (KEY_PRESS(VK_F4)) {
//减速
if (ps->_sleep_time>=80) {
ps->_sleep_time += 30;
ps->_foodscore -= 10;
}
}
SnackMove(ps);
Sleep(ps->_sleep_time);
} while (ps->_status==OK);
}
void GameOver(pSnack ps) {
pSnackNode cur = ps->_phead;
Setpos(30, 12);
switch (ps->_status)
{
case END_NORMAL:
printf("您主动退出游戏\n");
break;
case KILL_BY_SELF:
printf("您撞上自己了 ,游戏结束!\n");
break;
case KILL_BY_WALL:
printf("您撞墙了,游戏结束!\n");
break;
}
//释放蛇身的节点
while (cur)
{
pSnackNode del = cur;
cur = cur->next;
free(del);
}
}
test.cpp
void game() {
char c ='Y';
do {
Snack snack = { 0 };
GameStart(&snack);
GameRun(&snack);
GameOver(&snack);
Setpos(30, 13);
printf("是否再来一局:Y/N >");
c=getchar();
getchar();
} while (c == 'Y'||c=='y');
}
int main(){
setlocale(LC_ALL, ""); //设置为本地模式 支持宽字符(汉字)的输出等。
system("title 贪吃蛇"); //设置标题
srand((unsigned int)time(0));
game();
system("pause");
return 0;
}
如果这篇文章对你有帮助的话,请给博主一个免费的赞鼓励一下吧~ 💓
本文仅简单介绍了有关类和对象的一些基本概念和相关代码实现,以上个人拙见,若有错误之处,希望各位能提出宝贵的建议和更正,感谢您的观看!