C语言结课实战项目_贪吃蛇小游戏

2024-04-20 15:36

本文主要是介绍C语言结课实战项目_贪吃蛇小游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

最终实现效果:

实现基本的功能:

根据游戏进程解释代码:

游戏初始化:

首先进入游戏,我们应该将窗口名称改为 “贪吃蛇” 并将光标隐藏掉。再在中间打印游戏信息。

 之后我们要把地图打印出来:

然后我们将贪吃蛇创建出来,将蛇有关的信息用结构体和枚举类型封装起来,将蛇身用链表维护。

创建食物

 游戏开始:

用dowhile循环对主体进行不断刷新

打印相关信息

按键检测

蛇的移动

判断下一位置是否为食物:

 下一位置是食物:

下一位置不是食物:

 检测是否撞墙:

检测是否撞到自己:

 蛇移动一步的总函数:

 游戏结束:

后续改进:


游戏源代码链接:function/贪吃蛇 · 钦某/c-language-learning - 码云 - 开源中国 (gitee.com)

最终实现效果:

实现基本的功能:

void set_pos(short x, short y);//定位光标位置void Game_Start(pSnake ps);//初始化void WelcomeToGame(void);//打印欢迎界面void CreateMap(void);//创建地图void InitSnake(pSnake ps);//初始化蛇身void CreateFood(pSnake ps);//创建食物void Game_Run(pSnake ps);//游戏运行逻辑void SnakeMove(pSnake ps);//蛇的移动bool NextIsFood(pSnakeNode pn,pSnake ps);//判断下一位置是否为食物void EatFood(pSnakeNode pn, pSnake ps);//吃掉食物void NoFood(pSnakeNode pn, pSnake ps);//下一个位置不是食物void KillByWall(pSnake ps);//检测撞墙void KillBySelf(pSnake ps);//检测撞自己void Game_End(pSnake ps);//游戏善后

• 贪吃蛇地图绘制
• 蛇吃⻝物的功能(上、下、左、右⽅向键控制蛇的动作)
• 蛇撞墙死亡
• 蛇撞⾃⾝死亡
• 计算得分
• 蛇⾝加速、减速
• 暂停游戏

运用到的知识:C语⾔函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32API等

根据游戏进程解释代码:

这里分为下面几个函数对游戏进行实现:

system("cls");
//创建贪吃蛇
pSnakeNode pSnake = NULL;Snake snake = { 0 };//初始化游戏
Game_Start(&snake);//运行游戏
Game_Run(&snake);结束游戏
Game_End(&snake);

游戏初始化:

void Game_Start(pSnake ps)//初始化
{//0.设置窗口大小/名字system("mode con cols=100 lines=30");system("title 贪吃蛇");//1.隐藏光标HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(houtput,&CursorInfo);CursorInfo.bVisible = false;SetConsoleCursorInfo(houtput,&CursorInfo);//2.打印欢迎界面,介绍功能WelcomeToGame();//3.绘制地图CreateMap();//4.创建蛇InitSnake(ps);//5.创建食物CreateFood(ps);
}

首先进入游戏,我们应该将窗口名称改为 “贪吃蛇” 并将光标隐藏掉。再在中间打印游戏信息。

这里用到的函数有:

(1)system("mode con cols=100 lines=30");

将窗口设置为100列,30行

(2)system("title 贪吃蛇");

将title设置为贪吃蛇

(3)system("pause");

暂停程序,按下任意键继续

(4)system("cls");

清理屏幕

/*******************************************/
//0.设置窗口大小/名字
system("mode con cols=100 lines=30");
system("title 贪吃蛇");
/*******************************************/
void set_pos(short x, short y)
{//获得标准输出设备的句柄HANDLE houtput = NULL; houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x,y };SetConsoleCursorPosition(houtput,pos);
}
/*******************************************/
void WelcomeToGame()
{set_pos(40, 12);wprintf(L"欢迎来到贪吃蛇小游戏");set_pos(42,18);system("pause");system("cls");set_pos(30, 12);wprintf(L"用上下左右控制蛇的移动,按 {[ 加速, ]} 减速\n");set_pos(38, 13);wprintf(L"加速可以得到更高的分数\n");set_pos(42, 18);system("pause");system("cls");
}
/********************************************/

 之后我们要把地图打印出来:

分别打印上下左右的墙,将墙宏定义为WALL,字符为’□‘

void CreateMap()
{//上int i = 0;for (i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//下set_pos(0,25);for (i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//左for (i = 1; i < 25; i++){set_pos(0, i);wprintf(L"%lc", WALL);}//右for (i = 1; i < 25; i++){set_pos(56, i);wprintf(L"%lc", WALL);}
}

然后我们将贪吃蛇创建出来,将蛇有关的信息用结构体和枚举类型封装起来,将蛇身用链表维护。

蛇头指针:指向链表头节点的指针,方便对蛇身进行维护

食物指针:从开发角度来说,其实食物也是蛇的一个节点,当蛇头的下一个位置为食物时,将食物的节点头插到蛇身上面。

方向:对蛇的方向进行枚举

游戏状态:方便判断蛇的状态:(1)正常(2)撞墙(3)撞到自己(4)正常退出每一次while循环后判断游戏状态

食物权重:每次加速食物权重+2,减速-2。

总成绩:每吃掉一个食物,蛇身长度+1,分数+=食物权重。

每走一步的缓冲时间:缓冲时间越短,蛇走得越快;反之越慢。

#define POS_X 24
#define POS_Y 5#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'
typedef struct SnakeNode
{//坐标int x;int y;//下一个节点struct SnakeNode* next;
}SnakeNode,*pSnakeNode;enum DRECCTION//方向
{UP = 1,DOWN,LEFT,RIGHT
};//蛇的状态
enum GAME_STATUS
{OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞自己END_NORMAL//正常退出
};typedef struct Snake
{pSnakeNode _pSnake;//指向蛇头的指针pSnakeNode _pFood;//指向食物节点的指针enum DRECCTION _dir;//蛇的方向enum GAME_STAYUS _status;//游戏状态int _food_weight;//一个食物都分数int _score;//总成绩int _sleep_time;//休息时间}Snake,*pSnake;
/************************************/
void InitSnake(pSnake ps)//初始化蛇
{int i = 0;pSnakeNode cur = NULL;for (int i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc()");return;}cur->next = NULL;cur->x = POS_X + 2 * i;cur->y = POS_Y;//头插插入if (ps->_pSnake == NULL)ps->_pSnake = cur;else{cur->next = ps->_pSnake;ps->_pSnake = cur;}}cur = ps->_pSnake;while (cur){set_pos(cur->x,cur->y);wprintf(L"%lc", BODY);cur = cur->next;}//设置蛇的属性ps->_dir = RIGHT;//默认ps->_score = 0;ps->_food_weight = 10;ps->_sleep_time = 200;//单位为msps->_status = OK;
}

创建食物

生成随机数,赋给x,y。

这里x和y都有范围,不能超出地图边界,并且不能与蛇身重合。

void CreateFood(pSnake ps)//创建食物
{int x = 0;int y = 0;
again:do{x = rand() % 52 + 2;//2~54y = rand() % 24 + 1;//1~25} while (x % 2 != 0);//x为2的倍数//不能和蛇身的坐标相同pSnakeNode cur = ps->_pSnake;while (cur){if (x == cur->x && y == cur->y)goto again;cur = cur->next;}//创建食物节点pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("CreateFood()::malloc");return;}pFood->x = x;pFood->y = y;pFood->next = NULL;set_pos(x, y);wprintf(L"%lc", FOOD);ps->_pFood = pFood;
}

 游戏开始:


void Game_Run(pSnake ps)//游戏运行逻辑
{//打印帮助信息PrintHelpInfo();do{//打印分数,食物权重set_pos(64, 8);printf("总分数:%d\n",ps->_score);set_pos(64, 9);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_OEM_6)){//加速if (ps->_sleep_time > 80){ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(VK_OEM_4)){//减速if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}SnakeMove(ps);//蛇走一步的过程Sleep(ps->_sleep_time);} while (ps->_status == OK);
}

用dowhile循环对主体进行不断刷新

每次循环后让系统暂停一段时间(初始为200ms)

打印相关信息

蛇每走一步,分数都有可能变化,每次循环都打印一次。

按键检测

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)

(1)用虚拟键值检测是否按下上下左右键,按下相应键并且蛇的当前方向不能与之相反。

(2)检测是否按下空格,按下就进函数:

void Pause()
{while (1){	Sleep(200);if (KEY_PRESS(VK_SPACE))break;}
}

 再次按下空格退出函数。

(3)检测加速减速,按下加速键就将缓冲时间变短,食物权重增加;反之变长,食物权重减少。(这里也是有范围的,食物权重不能为负数,也不能过大)

蛇的移动

进入函数,创建蛇头的下一个位置所在的节点,并根据方向算出所在位置。

判断下一位置是否为食物:
bool NextIsFood(pSnakeNode pn, pSnake ps)//判断下一位置是否为食物
{return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);
}
 下一位置是食物:

先将新节点头插进蛇身,打印蛇身在屏幕上,总分数加上食物权重。

再次创建食物。

void EatFood(pSnakeNode pn, pSnake ps)
{//头插ps->_pFood->next = ps->_pSnake;ps->_pSnake = ps->_pFood;//释放下一个位置的节点free(pn);pn = NULL;//打印pSnakeNode cur = ps->_pSnake;while (cur){set_pos(cur->x, cur->y);wprintf(L"%lc",BODY);cur = cur->next;}ps->_score += ps->_food_weight;//重新创建食物CreateFood(ps);
}
下一位置不是食物:

创建下一位置的节点,也是头插,但是在打印蛇身之后,将蛇尾位置打印两个空格(不打印空格蛇身就不会清除一直留在屏幕上:拖尾),将蛇尾的节点释放掉。(cur->next一定要置空,不能让它为野指针)

void NoFood(pSnakeNode pn, pSnake ps)//下一个位置不是食物
{//头插pn->next = ps->_pSnake;ps->_pSnake = pn;pSnakeNode cur = ps->_pSnake;while (cur->next->next){set_pos(cur->x,cur->y);wprintf(L"%lc",BODY);cur = cur->next;}//把最后一个节点打印空格set_pos(cur->next->x,cur->next->y);printf("  ");//将最后一个节点释放free(cur->next);//将倒数第二个节点置为空cur->next = NULL;
}
 检测是否撞墙:

判断蛇头坐标位置是否超出范围,若超出范围,将蛇的状态改为KILL_BY_WALL

void KillByWall(pSnake ps)//检测撞墙
{if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||ps->_pSnake->y == 0 || ps->_pSnake->y == 26){ps->_status = KILL_BY_WALL;}
}
检测是否撞到自己:

遍历蛇身链表,若坐标重合,将蛇的状态改为KILL_BY_SELF,并且跳出循环。

void KillBySelf(pSnake ps)//检测撞自己
{pSnakeNode 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(pSnake ps)//蛇的移动
{//创建蛇头的下一个节点pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("SnakeMove()::malloc()");return;}switch (ps->_dir){case UP:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y-1;break;case DOWN:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y + 1;break;case LEFT:pNextNode->x = ps->_pSnake->x - 2;pNextNode->y = ps->_pSnake->y;break;case RIGHT:pNextNode->x = ps->_pSnake->x + 2;pNextNode->y = ps->_pSnake->y;break;}if (NextIsFood(pNextNode,ps))//检测下一个位置是否为食物{EatFood(pNextNode, ps);}else{NoFood(pNextNode, ps);}//检测是否撞墙KillByWall(ps);//检测是否撞自己KillBySelf(ps);
}

 游戏结束:

判断游戏结束的原因,并打印。

释放蛇身链表

void Game_End(pSnake ps)//游戏善后
{set_pos(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;}//释放蛇身链表pSnakeNode cur = ps->_pSnake;while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

后续改进:

1.穿墙

2.食物分类

3.多个食物

4.双人游戏

……

 本期博客到这里就结束了,如果有什么错误,欢迎指出,如果对你有帮助,请点个赞,谢谢!

这篇关于C语言结课实战项目_贪吃蛇小游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920645

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧