C语言·贪吃蛇游戏(上)

news2025/2/4 14:58:25

1. 游戏任务

        使用C语言在Windows环境的控制台中模拟实现小游戏贪吃蛇

        游戏中要包含以下功能:

        1. 贪吃蛇地图绘制

        2. 贪吃蛇上下左右移动和吃食物

        3. 蛇撞墙,或撞到自身死亡

        4. 计算得分

        5. 蛇身加速、减速

        6. 暂停游戏

2. Win32 API 介绍

        Windows是一种多作业的操作系统(同时进行多个任务进程),它除了协调应用程序的执行、分配内存、管理资源之外。它还是一个很大的服务中心,服务中心有很多函数接口,我们可以通过调用这些函数或者说是服务,来帮助应用程序达到开启视窗、描绘图形、使用周边设备等功能。由于这些服务的对象是应用程序,所以我们把这些服务称为 Application Programming Interface (应用程序编程接口) 简称API函数

2.1 控制台程序(Console)

        首先操作系统是win11的朋友们要注意了,我们运行程序的时候弹出的那个黑色窗口不是控制台,而是win11新提供的终端窗口,在终端窗口中是不能实现控制台程序中的一些功能的

        上面这个窗口就是终端窗口,下面我们讲解如何改成控制台窗口

        鼠标放到下箭头上,然后选择设置

        在启动中选择Windows控制台主机,并保存

        下次再运行起来的就是控制台窗口了

        下面我们介绍两个控制台程序命令:

2.1.1 设置控制台的大小

                        mode con cols=100 lines=30

        当我们把这段命令敲到cmd里头之后就会发现窗口的大小改变了

        cols 控制的是列,lines 控制的是行,现在我们就可以根据喜好控制游戏窗口的大小了

2.1.2 设置控制台的名字

                        title 贪吃蛇

        现在可以注意到,控制台窗口的名字变成了贪吃蛇

2.1.3 system()函数

        上面我们是在cmd中进行的操作,那么我们如何把这些操作写进C程序中呢,这时就用到了system函数,system函数就相当于帮你把内容输入到控制台中了,这个函数需要引用头文件<stdlib.h>

                                int system (const char* command);

        官网链接:system - C++ Reference

        上面我们展示了一下使用的效果,我们将窗口大小和名字都修改了,但是我在最后输入了一个pause暂停的语句,这是因为如果不暂停的话程序就直接结束了,紧接着刚刚输入的这些命令就失效了,那我们就看不到效果了。

2.2 控制台屏幕上的坐标 COORD

        COORD 是Windows API中定义的一个结构体,表示一个字符在控制台屏幕缓冲区上的坐标,坐标系原点(0,0)位于缓冲区的顶部左侧单元格

        COORD的类型声明差不多长这个样子:

                        

        为啥说差不多呢,因为在真正的声明中short是大写的,因为它前面给重命名了,但是其实这些我们都不必关注。

        下面说一下如何使用这个结构体,首先要引用头文件<Windows.h>,然后搞一个 COORD 类型变量赋值就行

                

        

2.3 控制台的操作以及光标控制

2.3.1 GetStdHandle

        GetStdHandle 是一个Windows API函数,它用于从一个特定的标准设备 (标准输入、标准输出或标准错误) 中取得一个句柄(用来标识不同设备的数值),使用对应的句柄可以操作对应的设备。下面我们展示一下这个函数的声明:

                HANDLE GetStdHandle( DWORD nStdHandle );

        官网资料:GetStdHandle 函数 - Windows Console | Microsoft Learn

        这个参数的类型 DWORD 看起来很迷,但其实这个参数就3种输入情况

        本节我们主要是用这个函数来获取控制台的标准输出句柄,以此来控制控制台上输出的东西,其实说白了就是把光标隐藏掉,因为如果不隐藏的话,光标在那里一直闪,很影响游戏画面的美观性。

        当我们使用这个函数的时候要先定义一个HANDLE类型的参数,其实HANDLE就是一个被typedef了的 void* 类型名。当然像这种API函数都要引用头文件<Windows.h>,后面再有用到API函数的时候我就不赘述引用头文件了。        

2.3.2 GetConsoleCursorInfo

        检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息

                        BOOL WINAPI GetConsoleCursorInfo(
                                    HANDLE               hConsoleOutput,
                                    PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
                        );

        官网资料:GetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn 

        PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该指针接收有关主机游标(光标)的信息。

2.3.3 CONSOLE_CURSOR_INFO

        这个结构体包含有关控制台光标的信息

                        typedef struct _CONSOLE_CURSOR_INFO {
                                DWORD dwSize;
                                BOOL bVisible;
                        } CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

        官网资料:CONSOLE_CURSOR_INFO 结构 - Windows Console | Microsoft Learn

        dwSize 是由光标填充的字符单元格的百分比。此值介于1-100之间。光标外观会发生变化,从0到100是光标从最下面一直上长到最上面,最后填充满整个字符单元格。

        bVisible 是游标的可见性,一个布尔类型变量。如果光标可见,此成员为true,不可见为false

        我们现在用一下GetConsoleCursorInfo函数,把控制台中的光标信息存放到cursor_info结构体中,观察光标信息cursor_info中的值,其中dwSize是25,对应着光标占25%的字符单元格,bVisible是1,对应着 true 可见的。后面那个圈出来的光标是我自己点出来的,像那样的光标就是dwSize=100的光标。

2.3.4 SetConsoleCursorInfo

        设置指定控制台屏幕缓冲区的光标的大小和可见性

                BOOL WINAPI SetConsoleCursorInfo(
                        HANDLE hConsoleOutput,
                        const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
                );

        官网资料:SetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn 

         我们可以用访问并改变结构体cursor_info的成员,然后再用SetConsoleCursorInfo,把改变后的信息交给程序。像这里我就把光标的dwSize改成了50,现在它看起来比25的时候高了不少。

        但是我们的主要任务是要隐藏光标,所以我们要修改光标的可见性,当然,在使用布尔类型时要注意引用头文件<stdbool.h>

2.3.5 SetConsoleCursorPosition

        设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的光标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。

                BOOL WINAPI SetConsoleCursorPosition(
                        HANDLE hConsoleOutput,
                        COORD pos
                );

         官网资料:SetConsoleCursorPosition 函数 - Windows Console | Microsoft Learn

        一顿操作之后你就会发现,hello world并不是从左上角的(0,0)开始打印了,而是从我设置好的光标位置开始打印的了。

        当然为了后续方便使用,我们可以把这一坨封装到一个函数里头去。

2.3.6 GetAsyncKeyState

        获取按键情况,GetAsyncKeyState的函数原型如下:

                SHORT GetAsyncKeyState(
                        int vKey
                );

        官网资料:GetAsyncKeyState function (winuser.h) - Win32 apps | Microsoft LearnGetAsyncKeyState 函数 (winuser.h) - Win32 apps |Microsoft 学习GetAsyncKeyState function (winuser.h) - Win32 apps | Microsoft Learn

        将键盘上每个键的虚拟值(vKey)传递给函数,函数通过返回值来分辨按键的状态。

        GetAsyncKeyState的返回值时short类型,在上一次调用GetAsyncKeyState函数后,如果返回的16位short型数据中。如果最高位是1,说明按键的状态是按下;如果最高位是0,说明按键的状态是抬起;如果最低位是1,说明按键被按过;如果最低位是0,说明按键没被按过。

        所以我们只需要判断返回值最低位是否为1就能知道这个按键是否被按过。

        虚拟键值表:虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn       

        下面我们实现检测数字键的功能,就是说我按下哪个数字键,就打印出哪个数字:

        解释一下,首先,我定义的这个宏 KEY_PRESS(vk) 让得到的虚拟键的反馈值按位与 1 就能知道最后返回值的最后一位是不是1了,也就是说,能够检测到有没有按这键。然后写一个死循环,一直判断这些虚拟键有没有被按过,如果按过,就把它打印出来。

        既然我们能够判断数字键有没有被按过,那么我们就能判断上下左右键有没有被按过,我们想监测哪个键就把对应的码值写上去就好了,如此说来,蛇的移动问题就解决了一半了

3. 贪吃蛇游戏设计与分析

3.1 地图

        我们最终的贪吃蛇游戏大概是这个样子的,那我们的地图该如何布置呢?

        欢迎界面

        操作介绍界面

        游戏界面

        在游戏地图上,我们打印墙体使用宽字符:,打印蛇身使用宽字符:,打印食物使用宽字符:

        普通的字符是占一个字节的,但是宽字符占两个字节,而且这些宽字符在视觉效果上也是一个普通字符的二倍

        这里简单讲一下C语言的国际化特性相关的知识,过去C语言并不适合非英语国家使用,C语言最初是美国人发明的,他们的语言中就26个字母,所以可能要使用到的字符非常少,但是其他用语言国家就不一定够用了。所以后来为了使C语言国际化,C语言的标准中不断加入了国际化的支持。比如宽字符的类型 wchar_t 和宽字符的输入和输出函数,加入了<locale.h> 头文件,其中提供了允许程序员针对特定地区调整程序行为的函数。

3.1.1 <locale.h>本地化

        <locale.h>提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分,标准中依赖地区的部分有以下几项:

        1. 数字量的格式

        2. 货币量的格式

        3. 字符集

        4. 日期和时间的表示形式

3.1.2 类项

        通过修改地区,程序可以改变它的行为来适应世界的不同区域。但地区的改变可能会影响库的许多部分,其中一部分可能是我们不希望修改的。所以C语言支持针对不同的类项进行修改,下面的每一个宏,指定一个类项:

        LC_COLLATE:影响字符串比较函数 strcoll()strxfrm()

        LC_CTYPE:影响字符处理函数的行为

        LC_MONETARY:影响货币的格式

        LC_NUMERIC:影响 printf() 的数字格式

        LC_TIME:影响时间格式 strftime()wcsftime()

        LC_ALL:针对所有类项修改,将以上所有类别设置为给定的语言环境

        每个类项的详细说明:setlocale,_wsetlocale | Microsoft Learn

3.1.3 setlocale函数

        setlocale 函数用于修改当前的地区,可以针对一个类项修改,也可以LC_ALL修改所有

        char* setlocale (int category, const char* locale);

         官网资料:setlocale - C++ Reference (cplusplus.com)

        setlocale 的第一个参数可以是前面讲到的任何一个类项,区别就是影响哪个类项,或者是全都影响

        setlocale 的第二个参数仅定义了两种可能取值:"C" (正常模式) 和 "" (本地模式)。本地模式就是一个空字符串就行了,然后你的Windows是哪国版本就给你上那个地区的模式

        当没有专门调用setlocale来控制模式的话,默认是正常模式启动

        setlocale的返回值是一个字符串指针,表示已经设置好的格式。如果调用失败,则返回空指针NULL

        setlocale() 可以用来查询当前地区,这时第二个参数设置为NULL就可以了

        setlocale() 在我们贪吃蛇项目中的用处就是把程序本地化,然后来让我们使用宽字符

3.1.4 宽字符的打印

        宽字符的字面量必须加上前缀 L ,否则C语言会把字面量当作普通字符处理。前缀 L 在单引号前面,表示宽字符,宽字符的打印使用 wprintf() ,打印格式前面也要加上 L ,对应宽字符的占位符是 %lc ,宽字符串占位符是 %ls 。汉字也是宽字符

        现在我们就很明显看出来宽字符的宽了,它真的占了两个字符的位置。

        那么在控制台的坐标系中一个普通的字符是占一列位置的,那么一个宽字符事实上要占两列位置,但是它们所占的行是一样的,都只占一行

3.1.5 地图坐标

        我们假设要实现一个27行58列的棋盘,再围绕它画出墙,如图:

                

        棋盘大小可以根据自己喜好设定,列数最好是行数的两倍,这样差不多能是一个正方形,然后列数最好设计成双数的,因为棋盘的墙还有里头的蛇和食物都是宽字符

3.1.6 蛇身和食物

        初始化状态,假设蛇身长度是5,蛇身的每个节点是 ● ,蛇头出现在一个固定的坐标处,比如(24,5) 处开始出现蛇,连续5个节点。

        注意:蛇的每一个节点的x坐标必须是2的倍数,否则蛇撞墙的判定会很迷

        关于食物,就是在墙体内随机生成一个坐标(列也同样必须是2的倍数),坐标不能和蛇身重合,然后打印★

4. 未完待续······

        到此,我们贪吃蛇游戏的前置知识就学完了,下节我们将着手写出这个游戏

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

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

相关文章

【MATLAB源码-第131期】基于matlab的淘金优化算法(GRO)机器人栅格路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 淘金优化算法&#xff08;GoldRush Optimizer&#xff0c;简称GRO&#xff09;是一种启发式优化算法&#xff0c;它受到淘金过程的启发。在淘金过程中&#xff0c;淘金者在河流或矿区中寻找金矿&#xff0c;通过筛选沙砾来寻…

病历管理系统

技术架构&#xff1a; StrutsSpringHibernate 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 企业财务管理系统主要用于电子病历来提高医院各项工作的效率和质量&#xff0c;促进医学科研、教学&#xff1b;减轻各类事务性工作的劳动强度&#xff0c;使他们…

2024美赛选题建议+ABCDEF思路分享

选题建议 A题 题目见&#xff1a; https://zxljhy.blog.csdn.net/article/details/135985116https://zxljhy.blog.csdn.net/article/details/135985116建议&#xff1a; A题是机理分析&#xff0c;需要去找到合适的数据对物种发展规律进行研究&#xff0c;结合生态学原理和动…

Windows SDK(三)GDI图形绘制基础

GDI介绍 GDI:图形设备接口&#xff1a;Windows的子系统&#xff0c;负责在视讯显示器和打印机上显示图形。 GDI常见名词解释&#xff1a; 直线 曲线 填入区域&#xff1a;直线或曲线构成的封闭区域&#xff0c;可以用画刷进行填充 位图&#xff1a;点阵图像 文字 映…

DEV-C++ ege.h库 绘图教程合集

一、目录 DEV-C ege.h库 绘图教程&#xff08;一&#xff09;——基本绘图函数 DEV-C ege.h库 绘图教程&#xff08;二&#xff09;——高级绘图函数&#xff08;上&#xff09; DEV-C ege.h库 绘图教程&#xff08;三&#xff09;——高级绘图函数&#xff08;下&#xff09;时…

探索自然语言处理在改善搜索引擎、语音助手和机器翻译中的应用

文章目录 每日一句正能量前言文本分析语音识别机器翻译语义分析自然语言生成情感分析后记 每日一句正能量 努力学习&#xff0c;勤奋工作&#xff0c;让青春更加光彩。 前言 自然语言处理&#xff08;NLP&#xff09;是人工智能领域中与人类语言相关的重要研究方向&#xff0c…

day07-CSS高级

01-定位 作用&#xff1a;灵活的改变盒子在网页中的位置 实现&#xff1a; 1.定位模式&#xff1a;position 2.边偏移&#xff1a;设置盒子的位置 left right top bottom 相对定位 position: relative 特点&#xff1a; 不脱标&#xff0c;占用自己原来位置 显示模…

最长的指定瑕疵度的元音子串 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 开头和结尾都是元音字母&#xff08;aeiouAEIOU&#xff09;的字符串为元音字符串&#xff0c;其中混杂的非元音字母数量为其瑕疵度。比如: “a” 、 “aa” 是元…

视频怎么加水印?分享两个简单的加水印的方法

在数字媒体时代&#xff0c;视频已经成为信息传播的重要方式。许多人在创作视频是会加上自己独特的水印&#xff0c;防止视频被盗用。水印作为数字版权保护技术的一种&#xff0c;可以有效地防止视频被非法复制、传播或篡改&#xff0c;从而保护创作者的权益和利益。下面我分享…

小程序中picker多列选择器

需求&#xff1a;实现类似省市联动的效果&#xff0c;选择第一列后&#xff0c;第二列数据变化 html部分: <view class"section"><view>多列选择器</view><picker mode"multiSelector" bindchange"bindMultiPickerChange"…

动环系统断电告警的防误报

机房一般接入的市电为三相380伏特&#xff0c;也有用单向220伏特的。UPS本身提供断电告警的功能&#xff0c;这个告警在各种种类的UPS中都是提供的&#xff0c;不同电压的市电输入都支持&#xff1b;三相电另外有缺相告警事件。但这些告警事件存在抖动或者误判。 瞬间的低压或…

C语言标准库所有字符串操作库函数汇总

以下是C语言标准库中字符串操作相关的API列表&#xff0c;这些函数通常在 <string.h> 头文件中定义&#xff1a; 1. strlen - 计算字符串长度&#xff0c;不包括结尾的空字符\0&#xff1a; size_t strlen(const char *str); 2. strcpy - 复制字符串&#xff1a; c…

网络编程套接字(3)

网络编程套接字 简单的TCP英译汉服务器地址转换函数字符串IP转整数IP整数IP转字符串IP关于inet_ntoa函数并发场景下的inet_ntoa函数绑定失败问题TCP协议通讯流程数据传输的过程数据交互四次挥手的过程端口连接 简单的TCP英译汉服务器 之前我们是以回调的方式处理任务的&#x…

再谈启动一个Activity大致时序图

太多了&#xff0c;笔者不想写&#xff0c; 读者可通过PlantUML插件查看如下PUML文件生成的时序图。 补充说明下&#xff0c;Android31版本。 startuml https://plantuml.com/sequence-diagram skinparam dpi 800 scale 15000 width scale 5000 heightautonumber Launcher La…

AJAX-入门

定义 概念&#xff1a;AJAX是浏览器与服务器进行数据通信的技术 使用 1.先使用axios库&#xff0c;与服务器进行数据通信 1&#xff09;基于XMLHttpRequest封装、代码简单、月下载量在14亿次 2&#xff09;Vue、React项目中都会用到axios 2.再学习XMLHttpRequest对象的使用…

学习日志以及个人总结 (16)

共用体 共用体 union 共用体名 { 成员列表&#xff1b; }&#xff1b;//表示定义一个共用体类型 注意&#xff1a; 1.共用体 初始化 --- 只能给一个值&#xff0c;默认是给到第一个成员变量 2.共用体成员变量辅助 3.可以判断大小端 ----※&#xff01;&#xff01; 实际用途…

Flask框架开发学习笔记《5》简易服务器代码

Flask框架开发学习笔记《5》 Flask是使用python的后端&#xff0c;由于小程序需要后端开发&#xff0c;遂学习一下后端开发。 简易服务器代码 接口解析那一块很关键&#xff0c;学后端服务器这一块&#xff0c;感觉主要就是学习相应地址的接口怎么处理。 然后写清楚每个地址…

第96讲:MySQL高可用集群MHA的核心概念以及集群搭建

文章目录 1.MHA高可用数据库集群的核心概念1.1.主从复制架构的演变1.2.MHA简介以及架构1.3.MHA的软件结构1.4.MHA Manager组件的启动过程1.5.MHA高可用集群的原理 2.搭建MHA高可用数据库集群2.1.环境架构简介2.2.搭建基于GTID的主从复制集群2.2.1.在三台服务器中分别搭建MySQL实…

C#验证字符串是正整数还是负整数,正则表达式vs用Char.IsDigit 方法遍历字符数组

目录 一、使用的方法 1.正则表达式 2.Char.IsDigit 方法 二、源码 1.源代码 2.生成效果 一、使用的方法 1.正则表达式 使用正则表达式Regex类的IsMatch方法&#xff0c;可以有效地判断用户输入的信息是否为有符号整数。 用于判断字符串是否有符号整数的正则表达式…

Maya------显示隐藏提取复制刺破面

alth<--->ctrlshifth 补洞后刺破面&#xff0c;防止多边面的产生&#xff01;