游戏开发者的成长之路:C++经典项目控制台贪吃蛇(在GitHub热门项目上增添功能及修改bug)
前言
原项目GitHub下载地址:https://github.com/silence1772/GreedySnake
原项目CSDN教程:
https://blog.csdn.net/silence1772/article/details/55005008
博主在原项目的基础上,增加了如下功能:
- 全屏化显示:涉及不同分辨率下的自适配。
- (直至2020/11/10)原来的项目在游戏二级循环过程中存在逻辑上的bug:原项目将是否拾取限时食物的判断放在了移动判断之后进行,而拾取到限时食物又会导致蛇体以蛇头向前延伸一格的方式增长,这会导致玩家朝着墙体方向移动去拾取挨着墙体的食物时,会在下一次方向判断前直接蛇头增长而撞墙,直接导致游戏失败。(强烈建议看不懂这段话的同学去上面给的网址下载原项目试玩体验)
而博主在改写项目代码时将限时食物拾取的逻辑改到移动判断之前进行,当玩家拾取限时食物时,将不再进行普通的移动(蛇头朝移动方向增长一格,蛇尾消失一格),而是采取蛇头向移动方向增长一格的方式替代(类似于拾取普通食物)。
博主改写项目的下载地址:
https://github.com/siming0202/GreedySnake
README
游戏的入口是 controller 类中的 Game 接口,为该游戏的一级循环。函数内使用 while(true) 构成了一个死循环,直到退出游戏时才会通过 break 语句退出循环结束游戏。函数内还调用了 Start 和 Select 等接口通过输出字符的方式绘制开始动画和选择界面,调用 DrawGame 绘制游戏界面,然后调用 PlayGame 接口进入游戏的二级循环,通过该接口的返回值判断玩家是否选择继续进行游戏。
游戏的二级循环中,首先动态创建 Snake 和 Food 对象,然后进入以 蛇是否撞墙或撞自己的身体 为条件的内置循环,若撞击成立则进入游戏失败的逻辑,调用 GameOver 接口绘制失败界面,根据玩家选择返回整型值,作为PlayGame的返回值传递至一级循环用以判断玩家做出的选择是重新开始还是退出游戏;若蛇并未撞墙或身体,则进入内置循环语句,调用 Snake->ChangeDirection() 来改变(或保持)移动方向,此时若玩家点击 ESC键 ,调用 Menu 接口绘制菜单,暂停游戏,随后根据该接口返回值决定继续游戏、重新开始、退出游戏。玩家点击其它按键将继续游戏,判断蛇是否吃到食物/限时食物(两者得分不同),吃到食物会进行得分更新、蛇体增长,若没有则进行普通移动,然后进入下一轮循环。
游戏的主要逻辑在两层循环中体现。
关键API
- GetStdHandle() :
获取指定的标准设备(包括输入设备、输出设备或显示错误的设备)的句柄。该函数的参数指定了要获取句柄的标准设备,返回值是指定标准设备的句柄。
//获取控制台输出窗口句柄
HANDLE handle_console = GetStdHandle(STD_OUTPUT_HANDLE);
if(INVALID_HANDLE_VALUE == handle_console)
{std::cerr("……"); /* 输出错误信息 */
}
- <windows.h>内定义类型成员COORD 表示字符在控制台窗口的坐标
- <windows.h>内提供SetConsoleCursorPosition(HANDLE hOut, COORD
pos)接口来定位光标的位置。 - <windows.h>内提供SetConsoleTextAttribute(HANDLE hOut, WORD
wAttributes)设定颜色 - <Windows.h> Sleep函数:执行挂起一段时间
接口:unsigned Sleep(unsigned seconds)
注意:标准C中是sleep,所以在Linux下不要大写首字母。在VC中,Sleep()里面的单位是以毫秒为单位。
<conio.h> int _kbhit() : 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0;属于非阻塞函数。
<conio.h> int _getch() : 阻塞函数,检测用户按下的键,如果不按键则该函数不返回,也不进行下一步操作。
注意:如果用户在键盘上输入上下左右(方向键)或F1~F12或Delete功能键,需要两次_getch才能得到真实的键码值参考资料:https://blog.csdn.net/weixin_43919932/article/details/102234892?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
//控制台窗口全屏显示
void FullScreen() {//获取当前工作的窗口的句柄(控制台)HWND hwnd = GetForegroundWindow();int x = GetSystemMetrics(SM_CXSCREEN); /* 屏幕宽度 像素 */int y = GetSystemMetrics(SM_CYSCREEN); /* 屏幕高度 像素*/LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); /* 获取窗口信息 *//* 设置窗口信息 最大化 取消标题栏及边框 */SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);SetWindowPos(hwnd, HWND_TOP, 0, 0, x, y, 0);
}
- HWND hwnd = GetForegroundWindow() : 获得当前工作的窗口的句柄,本代码中获取的是控制台窗口的句柄。
- GetSystemMetrics(SM_CXSCREEN) : 获取屏幕的宽度,若参数为SM_CYSCREEN则改为获取屏幕的高度
- LONG GetWindowLong(HWND hWnd, int nIndex) :
该函数获得有关指定窗口的信息,nIndex可取宏定义的值来指定需要获取的信息类型,nIndex取值如下:
宏定义的值 | 获取的信息类型 |
---|---|
GWL_EXSTYLE | 得到扩展的窗口风格 |
GWL_STYLE | 得到窗口风格 |
GWL_WNDPROC | 得到窗口回调函数的地址,或者句柄。得到后必须使用CallWindowProc函数来调用 |
GWL_HINSTANCE | 得到应用程序运行实例的句柄 |
GWL_HWNDPARENT | 得到父窗口的句柄 |
GWL_ID | 得到窗口的标识符 |
GWL_USERDATA | 得到和窗口相关联的32位的值 |
成功时,返回一个请求的32位的值;失败时,返回0,可使用GetLastError来取得错误信息。
- LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong) :
该函数改变指定窗口的属性,nIndex可设置为以下的宏定义值:
宏定义的值 | 获取的信息类型 |
---|---|
GWL_EXSTYLE | 设定一个新的扩展风格 |
GWL_STYLE | 设定新的窗口风格 |
GWL_WNDPROC | 为窗口过程设定一个新的地址 |
GWL_HINSTANCE | 设置一个新的应用程序运行实例的句柄 |
GWL_USERDATA | 设置与窗口有关的32位值 |
//举例:禁止主窗口拉伸LONG l_WinStyle = GetWindowLong(GetHWND(), GWL_STYLE)l_WinStyle &= ~WS_THICKFRAME; /* 将窗口STYLE里WS_THICKFRAME置零*/l_WinStyle |= WS_POPUP;SetWindowLong(GetHWND(), GWL_STYLE, l_WinStyle);
- 在讲解如何设置控制台窗口的字符大小之前,我们来了解CONSOLE_FONT_INFO结构:
typedef struct _CONSOLE_FONT_INFO{DWORD nFont;COORD dwFontSize;
} CONSOLE_FONT_INFO, *PCONSOLE_FONT_INFO;其中nFont是当前控制台字体在系统控制台字体表中的索引;dwFontSize是COORD结构的对象,用来保存当前控制台字体的字符宽度和高度。我们可以结合GetCurrentConsoleFont函数来获取当前控制台字体的信息://GetCurrentConsoleFont的声明
BOOL WINAPI
GetCurrentConsoleFont(HANDLE hConsoleOutput,BOOL bMaximumWindow, /* TRUE表示获取窗口最大化时字体的信息,一般设置为FALSE表示获取当前窗口字体信息 */PCONSOLE_FONT_INFO lpConsoleCurrentFont
);……//通过GetCurrentConsoleFont函数获取当前控制台字体
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_FONT_INFO consoleCurrentFont;
GetCurrentConsoleFont(hOutput, FALSE, &consoleCurrentFont);
printf("通过GetCurrentConsoleFont()函数获取到的控制台当前字体长度为%d, 高度是%d。\n"
, consoleCurrentFont.dwFontSize.X
, consoleCurrentFont.dwFontSize.Y
);
- 接下来研究如何通过SetCurrentConsoleFontEx()函数设置当前字体大小。
/* CONSOLE_FONT_INFOEX结构包含了控制台字体的扩展信息 */
typedef struct _CONSOLE_FONT_INFOEX {ULONG cbSize; /* 结构的大小,以字节为单位,该成员变量必须设置为sizeof(CONSOLE_FONT_INFOEX) */DWORD nFont; /* 要设置的字体在控制台字体表中的索引 */COORD dwFontSize; /* COORD结构的对象,指定了字体的尺寸 */UINT FontFamily; /* 指定了字体的间距(pitch)和族(family) */UINT FontWeight; /* 指定了字体的重量,正常字体重量为400,加粗字体重量为700,该值取值范围在100-1000 */WCHAR FaceName[LF_FACESIZE]; /* 指定使用何种字体 */
} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;/* SetCurrentConsoleFontEx声明 */
BOOL WINAPI SetCurrentConsoleFontEx(HANDLE hConsoleOutput /*类似于GetCurrentConsoleFont */,BOOL bMaximumWindow /*类似于GetCurrentConsoleFont */,PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx
);/* 设置当前字体大小代码 */
CONSOLE_FONT_INFOEX ConsoleCurrentFontEx;
ConsoleCurrentFontEx.cbSize = sizeof(CONSOLE_FONT_INFOEX);
ConsoleCurrentFontEx.nFont = 0;
ConsoleCurrentFontEx.dwFontSize.X = 32;
ConsoleCurrentFontEx.dwFontSize.Y = 64;
ConsoleCurrentFontEx.FontFamily = TMPF_VECTOR; /* 使用向量字体 */
ConsoleCurrentFontEx.FontWeight = 200;
wcscpy(ConsoleCurrentFontEx.FaceName, _T("Courier"));
SetCurrentConsoleFontEx(hOutput, FALSE, &ConsoleCurrentFontEx);
- LoadKeyboardLayout接口 给系统中装入一种新的键盘布局。
函数原型:HKL LoadKeyboardLayout(LPCTSTR pwszKLID, UINT Flags);
其中pwszKLID为键盘布局名称,名称是由语言标识符(低位字)和设备标识符(高位字)组成的十六进制值串,例如U.S.英语键盘布局命名为“0000409”。
Flags:指定如何装入键盘布局,参数详见参考资料。
参考资料:https://blog.csdn.net/wbryfl/article/details/5295020
参考资料
C语言控制台窗口图形界面编程:https://blog.csdn.net/liluo_2951121599/article/details/66474233
C/C++console(控制台)编程详解:
https://www.cnblogs.com/flowingwind/p/8159035.html
游戏开发者的成长之路:C++经典项目控制台贪吃蛇(在GitHub热门项目上增添功能及修改bug)相关推荐
- 【C++】经典项目控制台贪吃蛇小游戏详细教程
[小游戏]贪吃蛇GreedySnake 本文将讲解如何使用c++面向对象方法编写控制台版贪吃蛇小游戏 项目github地址:游戏源码链接 游戏下载:GreedySnake 本人属初学者,水平所限,难免 ...
- 十年风雨,一个普通程序员的成长之路(八)不想做技术总监的项目经理,不是好程序员...
目录 十年风雨,一个普通程序员的成长之路(八)不想做技术总监的项目经理,不是好程序员 01 技术总监写不写代码? 02 面试的坎坷与杯具 03 新的开始 & 旧的结束 十年风雨,一个普通程序员 ...
- java毕业设计——基于java+J2ME的贪吃蛇游戏设计与实现(毕业论文+程序源码)——贪吃蛇游戏
基于java+J2ME的贪吃蛇游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+J2ME的贪吃蛇游戏设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦.需要下载开题报告P ...
- 【C语言项目】贪吃蛇游戏(上)
00. 目录 文章目录 00. 目录 01. 开发背景 02. 功能介绍 03. 欢迎界面设计 3.1 常用终端控制函数 3.2 设置文本颜色函数 3.3 设置光标位置函数 3.4 绘制字符画(蛇) ...
- #进击的贪吃蛇-----将贪吃蛇,飞机大战,坦克大战功能融合形成新的游戏
#进击的贪吃蛇-----将贪吃蛇,飞机大战,坦克大战功能融合形成新的游戏. 想必很多大一新生在刚学C语言时和我们遇到过同样的问题,就是如何去做第一个C语言大作业. 我们小组内部在讨论大作业时,并不打算 ...
- 计算机软件实习项目二 —— 贪吃蛇游戏 (实验准备)
目录 一.实验目的 二.编程语言和平台 三.实验难点: 四.参考资料 一.实验目的 1.实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子 2.上下左右控制"蛇 ...
- c/c++游戏编程之控制台贪吃蛇(一)
c/c++游戏编程之控制台贪吃蛇(一) c/c++游戏编程之控制台贪吃蛇(二) 欢迎你开启了c++的游戏编程世界之旅 如果你还未学过c++基本语法,请先学习基本语法再来学习游戏编程噢~. 对这样的&q ...
- c/c++游戏编程之控制台贪吃蛇(二)
c/c++游戏编程之控制台贪吃蛇(一) c/c++游戏编程之控制台贪吃蛇(二) 为了解决"闪屏"问题,我们不再使用system("cls")进行清屏,而是直接用 ...
- 如何用python写游戏_一步步教你怎么用python写贪吃蛇游戏
目录 0 引言 1 环境 2 需求分析 3 代码实现 4 后记 0 引言 前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Pytho ...
最新文章
- 洛谷 P3302 [SDOI2013]森林 主席树+启发式合并
- OSPF高级设置实现全网互通
- centos7 安装mysql 解决:Failed to restart mysqld.service: Unit not found
- hashCode之一--两个对象值相同,有相同的hash code
- delphi7下安装TMS component
- ARM开发板上iconv调用失败的解决方法
- Linux下的重要目录
- java中介者模式例子_Java中介者模式(Mediator Pattern)
- 计算机web程序开发,基于WEB的计算机应用基础考试系统的开发与设计
- 项目部署到服务器后字符编码,将UTF-8编码的数据发布到服务器会丢失某些字符...
- CJOI 05新年好 (最短路+枚举)
- java下cmyk图片读取和转换rgb,以及图片压缩
- 【笔记】第2章 向量
- 翻转数组,将数组倒序输出
- 中国的开源之夏来了!
- 计算机专业裁合词英语,计算机专业英语的构词方法
- java怎么判断文件大小_java判断文件大小
- 2022年京东618店庆活动优惠力度怎么样?
- 程序员的键盘使用指南
- mybatisplus sql 改写2
热门文章
- SSM中mybtis报错### The error may involve defaultParameterMap ### The error occurred while setting param
- 微信图片转换成文字的方法
- 京东某被裁员工:虽然公司裁掉了我,但我不能裁掉我的未来!
- AD中板内挖空的方法
- 一分钟搞懂X86架构
- 【python机器学习】线性回归--梯度下降实现(基于波士顿房价数据集)
- 关于公网摄像机直播公网视频直播的基本思考方法
- go语言的控制台输入
- [CVPR2020-best](unsup3d)Unsupervised Learning of Probably Symmetric Deformable 3D Objects from Image
- xampp php5.6,XAMPP for Linux