200行代码写一个简易的C++小黑窗贪吃蛇游戏

分享一个简易的小黑窗贪吃蛇,一共就两百行代码左右(包含注释),很适合初学者巩固语法来练练手.

如果后续需要其他功能也可以再添加.

先小小展示一下:

源码在文末免费领取. 

使用工具:

  VS2019(不是用VS的也可以直接找出cpp和h文件复制到你们用的IDE,甚至是记事本都可以)

闲话(可跳过):

为什么要写贪吃蛇呢?首先是从这学期开始下定决心要走C++路线了,因此把C++又复习了一遍,基础语法又学了一遍,还写了C++primer的笔记(有专栏).

想着要来个项目练练手,但是目前就会个C++的基础语法和STL,甚至连QT都不熟练,然后我又想起了把我领进门的学长在我刚学C语言的时候就布置了一个"任务",学完C语言之后写个贪吃蛇出来 .

因此在复习完C++后我决定就写个小黑窗贪吃蛇出来,QT版本的以后也要做出来(等我再好好学学QT),然后大概是花了一个下午把贪吃蛇给写出来了,包括注释也就两百行左右,自己留着也是孤芳自赏,因此在此分享出来.

蛇:

 在写代码之前一定要构思好怎么写,绝不能想到一点写一点,否则以后加功能或是找bug的时候会很痛苦(深有体会)

贪吃蛇最主要的就是蛇,那么先给蛇写个类,类中需要的属性有

蛇的长度

蛇的头部

蛇的身子

蛇的运动方向

蛇应该要有的动作(函数):

沿着方向移动

改变方向

因此可以写出蛇类的大体框架:

class snack{friend class UI;    //将UI界面设为友元,使得界面可以接触到蛇的私有属性
private:vector<int>head = vector<int>(2);    //存放头的坐标vector<vector<int>>body;    //存放身子的坐标char direction;    //运动方向int size = 1;    //身长public:snack(int x=25, int y=10);		void crawl();	//爬行bool changeDirection(char newDirection);	//改变方向

界面:

刚才说贪吃蛇最主要的是蛇,其实最主要的是界面(至少在本项目中)

界面需要有边框,需要把蛇装起来,需要生成食物......

UI需要的属性有:

得分

食物

界面

 需要有的动作(函数):

展示画面

移动蛇

检测玩家操作

生成食物

检查蛇的移动是否合规

由上可以写出UI类的大体框架

class UI {friend void getDirection(UI& u);
private:int Width;		//宽int Height;		//高int score=0;	//得分snack role;		//蛇std::default_random_engine e;	//用于生成随机数,随机生成食物vector<int>food = vector<int>(2);	//存放食物坐标vector<vector<char>>UI_Cache;		//界面缓存,直接打印出来即可
public:UI(int width = 50, int height = 20);void show();	void moveSnack();	//移动蛇bool check(char c);	//检查输入字符void getDirection();	//获取玩家操作void createFood();	//生成食物void checkRole();	//检查蛇的移动是否合规void run();			//运行
};

生成界面

那么框架有了,我们只需要把里面的功能实现就行,那么界面该怎么生成呢?

我们直接把界面缓存打印出来,界面缓存是个二维vector,刚好是界面的长*宽,因此我们需要把二维vector的边界修改成游戏的边界,这一点可以在UI的构造函数里完成,说到构造函数,我们很多功能都可以在构造函数里完成,例如初始化蛇,初始化食物......

如下所示:

UI::UI(int width, int height) :Width(width), Height(height) {UI_Cache.resize(height, vector<char>(width, ' '));	//将画面大小重置role = snack(width/2,height/2);		//初始化蛇,传入参数使蛇在画面中间e.seed(time(0));	//给随机数引擎设置随机数种子//绘制边框for (int i = 0; i < height; i++) UI_Cache[i][0] = UI_Cache[i][width - 1] = '|';for (int j = 0; j < width; j++) UI_Cache[0][j] = UI_Cache[height - 1][j] = '-';UI_Cache[0][0] = UI_Cache[0][width - 1] = UI_Cache[height - 1][0] = UI_Cache[height - 1][width - 1] = '*';//打印开始界面for (int i = 0; i < Height / 2-1; i++) cout << endl;	//使得welcome在界面中央for (int j = 0; j < Width / 2; j++)cout << "  ";cout << "welcome" << endl;//初始化食物createFood();Sleep(1500);system("cls");
}

蛇的移动

界面有了之后,我们应该让蛇移动了,但是在游戏的一开始应该让蛇停在画面中间,直到玩家开始操作之后再开始移动,我们该怎么移动蛇呢?

蛇分为头部和身子,蛇的每次移动实际上是移动头部,而身子的大部分是不变的,我们可以找到规律,每次移动,身子的最后一节都会没有,而原本头所在的位置会变成最开始的一节身子,那么我们可以在蛇移动的时候,删去存放蛇身子的vetcor的最后一个(pop_back()),然后再把头插入到存放蛇身子的vector的第一位(insert(body.begin(),head));

void snack::crawl() {	//爬行,将头部变成第一节身子,然后将身子的末尾去掉.if (direction == ' ') return;	//开局先不动,方向默认是空字符,可以直接返回.body.insert(body.begin(), head);body.pop_back();//调增新头部的位置if (direction == 'w') head[1]--;		//向上,则是将y轴的值减一else if (direction == 's') head[1]++;	//向下,则是将y轴的值加一else if (direction == 'a') head[0]--;	//向左,则是将x轴的值减一else if (direction == 'd') head[0]++;	//向右,则是将x轴的值加一
}

说到移动,就不能忘记方向,因此我们需要有调整蛇方向的函数,要注意的是,蛇不能掉头(180°转弯),只能90°转.

bool snack::changeDirection(char newDirection) {//调整移动方向//这里需要做个小判断,比如正在向左走就不能转到右,不能直接180°转弯if (newDirection == 'w' && direction == 's') return false;if (newDirection == 's' && direction == 'w') return false;if (newDirection == 'a' && direction == 'd') return false;if (newDirection == 'd' && direction == 'a') return false;direction = newDirection;return true;
}

方向是需要玩家输入的,因此我们需要实现玩家输入的函数,本来我是想做成多线程的,这样子扫描玩家输入比较流畅,但是我查了半个下午,调试了四分之一个下午都没能搞定,因此还是就做成固定查询了.(如果有搞定了多线程扫描输入的好兄弟,可以评论告诉我)

#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)bool UI::check(char c) {//检测某个按键是否按下if (KEY_DOWN(c)) return true;return false;
}void UI::getDirection() {if (check('Q')) {		//按q则退出cout << "game is over!" << endl;cout << "your score is " << score << endl;exit(0);}//操纵蛇else if (check('A') || check(37)) role.changeDirection('a');else if (check('S') || check(40)) role.changeDirection('s');else if (check('D') || check(39)) role.changeDirection('d');else if (check('W') || check(38)) role.changeDirection('w');
}

 当然,我们移动蛇只是改变了蛇的属性,我们还需要在UI界面实现更新蛇坐标的功能:

void UI::moveSnack() {//将蛇的头部和身子分别映射到UI的缓存中,这里可以修改蛇头部和身子的字符cout << role.head[0] << ' ' << role.head[1] << endl;UI_Cache[role.head[1]][role.head[0]] = '@';for (auto b : role.body) {UI_Cache[b[1]][b[0]] = '*';}
}

 如果直接向上面那样的话会有一个问题,就是蛇会一直变长,这是因为在UI界面的缓存中,我们仍然缓存着上一次的蛇身子的坐标,而我们这里只是在UI缓存中将蛇的身体的坐标赋值成了特定的符合,而没有删除上一次的缓存,这就导致了因为移动而被我们删除的最后一节身子仍然留在缓存中,所以我们还需要清除缓存,但是如果因此而重置缓存的话,那么会影响运行效率,因此我们仅仅将移动前的身子最后一节的坐标映射在UI缓存中的位置置位空字符即可:

UI_Cache[role.body[role.size - 1][1]][role.body[role.size - 1][0]] = ' ';

食物

接下来就剩食物了,生成食物使用随机数引擎,在界面宽高的范围内生成随机的x,y坐标,还需要检查,如果生成在了蛇的位置上则需要重新生成.

void UI::createFood() {	//生成食物std::uniform_int_distribution<unsigned> u(1,Width-2);	//设置随机数生成范围int x, y;while (1) {x = u(e), y = u(e) % (Height - 2);	if (x <= 0 || y <= 0 || x >= Width - 1 || y >= Height - 1) continue;	//如果生成的食物坐标不在边界内则重新生成.if (x == role.head[0] && y == role.head[1]) continue;	//如果生成的食物坐标和蛇头一致则重新生成.for (auto a : role.body) {if (y == a[1] && x == a[0]) continue;	//如果生成的食物坐标与蛇身一致则重新生成.}break;}food[0] = x, food[1] = y;UI_Cache[y][x] = '$';		//这里可以更改食物的字符
}

我们还需要检测蛇是否吃到了食物,以及是否吃到了自己和是否撞墙.

撞到墙或吃到自己是毫无疑问结束游戏,而吃到了食物,我们则需要将玩家的得分增加,并且增长蛇的长度,我们可以将食物的位置变成新的蛇头位置,并且将老蛇头的坐标插入到蛇身体里,这样就完成了蛇身体的增长.

不要忘记生成新的食物:

void UI::checkRole() {	//检查移动是否合法int x = role.head[0], y = role.head[1];if (x <= 0 || y <= 0 || x >= Width-1 || y >= Height-1) {	//碰到边界则失败cout << "you are lose" << endl;cout << "your score is " << score << endl;exit(0);}for (auto a : role.body) {	//碰到自己的身体则失败if (a[0] == x && a[1] == y) {cout << "you are lose" << endl;cout << "your score is " << score << endl;exit(0);}}if (x == food[0] && y == food[1]) {	//吃到食物则分数增加,蛇身体长度增加.score++; role.size++;role.body.insert(role.body.begin(),role.head);role.head[0] = food[0], role.head[1] = food[1];	//将食物位置直接变成新蛇头的位置,达到身体增长的效果.createFood();	//重新生成食物}
}

代码领取:

免费领取完整代码可以关注我的公众号:折途想要敲代码,回复关键字"贪吃蛇"即可.

也可以直接在CSDN上下载,我也已经上传到CSDN了.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/2409.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【hadoop】hadoop的体系架构

hadoop的体系架构 HDFS的体系架构NameNodeedits文件&#xff08;客户端的操作日志&#xff09;fsimage文件&#xff08;元信息文件&#xff09; DataNodeSecondary NameNode Yarn的体系架构HBase主从架构的单点故障的问题 HDFS的体系架构 NameNode NameNode&#xff1a;主节点…

2023网络安全面试题汇总(附答题解析+配套资料)

随着国家政策的扶持&#xff0c;网络安全行业也越来越为大众所熟知&#xff0c;相应的想要进入到网络安全行业的人也越来越多&#xff0c;为了更好地进行工作&#xff0c;除了学好网络安全知识外&#xff0c;还要应对企业的面试。 所以在这里我归总了一些网络安全方面的常见面…

Vue中的事件处理

一&#xff0c;基本使用 1.使用v-on:事件名或者事件名绑定事件 常见的事件有&#xff1a; onclick, 鼠标单击事件&#xff1b; ondblclick, 鼠标双击事件&#xff1b;onmousedown,鼠标按下去的事件&#xff1b;onmouseup,鼠标弹起事件&#xff1b; onmouseover,onmouseente…

巧妙使用 CSS 渐变来实现波浪动画

目录 一、波浪的原理 二、曲面的绘制 三、波浪动画 四、文字波浪动画 五、总结一下 参考资料 之前看到coco[1]的这样一篇文章&#xff1a;纯 CSS 实现波浪效果&#xff01;[2]&#xff0c;非常巧妙&#xff0c;通过改变border-radius和不断旋转实现的波浪效果&#xff0c…

MiniGPT4系列之二推理篇命令行方式:在RTX-3090 Ubuntu服务器推理详解

MiniGPT4系列之一部署篇&#xff1a;在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式&#xff1a;在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI)&#xff1a;在RTX-309…

中国国债发行数据集(2002-2023)

国债是由国家发行的债券&#xff0c;由于国债的发行主体是国家&#xff0c;所以它具有最高的信用度&#xff0c;被公认为是最安全的投资工具。国债按照交易市场的不同分为三类&#xff0c;即银行间市场国债、交易所市场国债和柜台市场国债&#xff1b;按照交易方式的不同分为三…

git使用问题记录-权限

注意点&#xff1a; 1、在远程仓库中直接创建项目时&#xff0c;默认分支为main 2、git push报错 原因&#xff1a;即使是项目文件的创建者&#xff0c;但上层目录的权限为developer&#xff0c;无法push项目&#xff0c;找上层管理员修改权限为maintainer或owner可push代码…

电路分析 day01 一种使能控制电路

本次分析的电路为 一种使能控制电路 &#xff08;站在别人的肩膀上学习&#xff09; 资料来源 &#xff1a; 洛阳隆盛科技有限责任公司的专利 申请号&#xff1a;CN202022418360.7 1.首先查看资料了解本次电路 1.1 电路名称&#xff1a; 一种使能控制电路 1.2 电路功能…

C++基础算法高精度篇

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C算法 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要讲解了高精度算法的四种常用的计算 文章目录 Ⅲ. 高精度Ⅲ. Ⅰ . …

【外卖系统】环境的搭建

搭建数据库 1.创建数据库&#xff0c;名字叫reiggie 2.导入数据库 创建Maven项目 1.创建项目 2.检查项目新建的是否有问题 3.导入pom.xml文件 4.导入application.yml文件 在从gittee上down的代码的基础上&#xff0c;修改一下端口号&#xff0c;数据库的名称什么的 …

传输层协议—网络

文章目录 1.TCP1.1TCP协议段格式1.2可靠机制1.2.1确认应答机制1.2.2超时重传机制1.2.3连接管理机制1.2.4流量控制机制1.2.5拥塞控制机制 1.3效率机制1.3.1滑动窗口机制1.3.2延迟应答机制1.3.3捎带应答机制 1.4粘包问题&#xff08;tcp问题&#xff0c;应用层的数据包&#xff0…

【uni-app】常用图标、头像汇总

在做小程序的时候&#xff0c;有时候需要各种图标和头像素材&#xff0c;而百度一堆基本都是收费的。所以&#xff0c;整理一些免费好用的图标&#xff0c;头像库&#xff1a; 1、iconfont-阿里巴巴矢量图标库 基本上常用的矢量图标&#xff0c;在这儿都有&#xff0c;而且可…

rt-thread rtc设备驱动开发

基于pico rtc设备驱动开发 I/O设备框架RTC设备功能配置——启用Soft RTC功能配置——启用NTP时间自动同步功能配置——启用硬件RTC RT-Thread 的 RTC &#xff08;实时时钟&#xff09;设备为操作系统的时间系统提供了基础服务。应用层对于 RTC 设备一般不存在直接调用的 API &…

基于ZYNQ阵列涡流检测系统硬件设计(一)

为实现阵列涡流检测系统总体功能&#xff0c;需研制一套多通道信号采集硬件系统&#xff0c;以搭配 软件编程实现分时激励和分时采集。基于以上要求&#xff0c;本章介绍了阵列涡流检测系统的硬 件模块设计。 3.1 阵列涡流检测系统总体设计 阵列涡流检测系统需要利用 DA …

分布式软件架构——传输链路

传输链路 链路指无源的点到点的物理连接。链路是计算机网络中的一个重要概念&#xff0c;它指的是连接两个网络设备的物理或逻辑路径。简单来说&#xff0c;链路就是电信号或数据在网络中传输的路径。在计算机网络中&#xff0c;链路可以分为物理链路和逻辑链路两种。物理链路…

【UI自动化测试】appium+python+unittest+HTMLRunner

进阶Python接口自动化测试必备教程&#xff08;2023全网最详细&#xff09; 简介 获取AppPackage和AppActivity 定位UI控件的工具 脚本结构 PageObject分层管理 HTMLTestRunner生成测试报告 启动appium server服务 以python文件模式执行脚本生成测试报告 下载与安装 下载需要自…

举例说明基于线性回归的单层神经网络网络(以梯度下降算法来求解权重的过程)...

我们将通过一个简单的例子来说明基于线性回归的单层神经网络&#xff0c;以及如何使用梯度下降算法来求解权重。 假设我们有以下数据集&#xff0c;表示学生的学习时间&#xff08;小时&#xff09;与他们的考试分数&#xff1a; 学习时间&#xff08;X&#xff09;&#xff1a…

线程池学习(五)线程工厂、线程池工厂

Executors 定义的Executor, ExecutorService, ScheduledExecutorService, ThreadFactory和Callable类的工厂和实用程序方法&#xff0c;我们称为线程池工厂。ThreadFactory 为定制化创建新线程的对象,我们称为线程工厂 前面几期的学习中&#xff0c;我已经初步会使用线程池了&…

MySQL内置函数

内置函数从实现的功能角度可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数&#xff0c;获取MySQL信息函数、聚合函数等。 1.数值函数 来源&#xff1a; 版权声明&#xff1a;本文为CSDN博主「清风拂来水波不兴」的原创文章&#xff0c;遵循CC 4…

Ceph简介及部署

Ceph Ceph一、存储基础1、单机存储设备2、Ceph 简介3、Ceph 优势5、Ceph 架构6、Ceph 核心组件7、OSD 存储后端8、Ceph 数据的存储过程9、Ceph 版本发行生命周期10、Ceph 集群部署 二、部署ceph-deploy Ceph 集群前环境配置1、关闭 selinux 与防火墙2、根据规划设置主机名3、配…