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

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.mzph.cn/news/662536.shtml

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

相关文章

云原生之可观测性-APM概念及选型

导读&#xff1a;为了让大家更好的 APM 系统的设计实现&#xff0c;我将在公众号编写《云原生之可观测性》系列文章&#xff0c;深入讲解 APM 系统的产品设计、架构设计和基础技术。APM是一个庞杂的技术体系&#xff0c;涉及到很多开源技术&#xff0c;欢迎大家留言讨论。 本文…

python中的异步实践与tornado应用

最近项目中由于在python3中使用tornado,之前也有用过&#xff0c;是在python2中&#xff0c;由于对于协程理解不是很透彻&#xff0c;只是套用官方文档中的写法&#xff0c;最近比较细致的看了下协程的用法&#xff0c;也将tornado在python3中异步的实践了一下。 异步基础 要…

[香橙派开发系列]使用蓝牙和手机进行信息的交换

文章目录 前言一、HC05蓝牙模块1.HC05概述2.HC05的连接图3.进入HC05的命令模式4.常用的AT指令4.1 检查AT是否上线4.2 重启模块4.3 获取软件版本号4.4 恢复默认状态4.5 获取蓝牙的名称4.6 设置蓝牙模块的波特率4.7 查询蓝牙的连接模式4.8 查询模块角色 5.连接电脑6.通过HC05发送…

2024美赛MCM Problem A: Resource Availability and Sex Ratios资源可用性和性别比例 完整代码以及思路分享

虽然一些动物物种存在于通常的雄性或雌性性别之外&#xff0c;但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1&#xff1a;1&#xff0c;但其他物种的性别比例并不均匀。这被称为适应性性别比例的变化。例如&#xff0c;美洲短吻鳄孵化卵的巢穴的温度会影…

sql指南之null值用法

注明&#xff1a;参考文章&#xff1a; SQL避坑指南之NULL值知多少&#xff1f;_select null as-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞7次&#xff0c;收藏21次。0 引言 SQL NULL&#xff08;UNKNOW&#xff09;是用来代表缺失值的术语&#xff0c;在表中的NULL值是显示…

Git解决分支合并冲突的问题:分支合并提交出现了不同分支同一个文件的修改的冲突解决

有些时候我们合并分支的时候&#xff0c;会出现冲突&#xff0c;原因就是我们修改了分支A 的第一行代码&#xff0c;我们还修改了主分支上面同一个文件中的第一行代码&#xff08;分支A已经提交过一次&#xff09;&#xff0c;此时我们在次合并的时候就会出现冲突&#xff0c;需…

智慧养老成关键力量,全视通智慧机构养老解决方案来助力

近日&#xff0c;国新办举行国务院政策例行吹风会&#xff0c;国家发展改革委、工业和信息化部、民政部、商务部有关负责人介绍《关于发展银发经济增进老年人福祉的意见》有关情况并答记者问。 会上&#xff0c;民政部养老服务司负责人李永新透露&#xff0c;将发展智慧养老服务…

tidb节点重启后,服务无法重连

大家好&#xff0c;我是烤鸭&#xff1a; 前几天遇到tidb节点重启后服务无法重连&#xff0c;确切地说是两个服务&#xff0c;一个可以正常重连&#xff0c;一个不行。 问题复现 由于线上执行慢SQL&#xff0c;导致TiDB 单个节点宕机重启。 其中A服务的3个节点和B服务的1个节…

Sketch使用手册:从入门到精通的完整教程

Sketch软件是Mac平台上流行的矢量图形编辑软件&#xff0c;旨在帮助用户创建网站、移动应用、图标等各种设计原型。Sketch软件的设计风格简洁明了&#xff0c;界面操作简单易用&#xff0c;非常适合UI/UX设计师、平面设计师等数字创意人员。本文将根据如何使用Sketch&#xff0…

IEPE数据采集卡的作用说明

IEPE传感器是一种特殊的加速度传感器&#xff0c;其特点是自带电量放大器或电压放大器。这种传感器产生的电信号非常微弱&#xff0c;很容易受到噪声干扰&#xff0c;因此需要使用灵敏的电子器件进行放大和信号调理。为了实现更好的抗噪声性能和更方便的封装&#xff0c;IEPE传…

xmind思维导图 for mac v24.01中文版

mac电脑上思维导图软件哪个好呢&#xff1f; xmind for mac一个功能强大、易于使用的思维导图软件&#xff0c;够帮助你更好地组织思维、管理信息、规划项目和解决问题&#xff0c;提高个人和团队的工作效率。 软件下载&#xff1a;xmind思维导图 for mac v24.01中文版 XMind f…

数据可视化工具选择指南:六款主流工具的综合评测

随着大数据时代的来临&#xff0c;数据可视化已成为各行业不可或缺的工具。本文将为您介绍市面上六款主流数据可视化工具&#xff0c;包括山海鲸可视化、Echarts、D3.js、Tableau、Power BI和Funnel.io&#xff0c;帮助您更好地了解并选择适合您的工具。 山海鲸可视化 山海鲸…

【Django开发】美多商城项目第2篇:Django用户注册和登录开发(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现前后端…

C# .Net学习笔记—— 异步和多线程(异常处理)

一、异常处理 1、下面for循环20个线程&#xff0c;到11&#xff0c;12号的时候执行失败&#xff0c;这里我也用了try catch来捕获异常。 private void button11_Click(object sender, EventArgs e){TaskFactory taskFactory new TaskFactory();List<Task> taskList ne…

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于POA-LSSVM…

【数据结构与算法】之排序系列-20240201

【数据结构与算法】之排序系列-20240201 一、88. 合并两个有序数组二、169. 多数元素三、217. 存在重复元素四、242. 有效的字母异位词五、268. 丢失的数字六、349. 两个数组的交集七、350. 两个数组的交集 II 一、88. 合并两个有序数组 简单 给你两个按 非递减顺序 排列的整数…

【Uni-App】运行微信小程序时报错routeDone with a webviewId 2 that is not the current page

使用HBuilderX开发微信小程序&#xff0c;运行项目的时有可能会出现routeDone with a webviewId 1 that is not the current page的报错&#xff0c;但不影响运行。如果强迫症介意的话&#xff0c;可以考下面的方法进行修复。 产生原因 由于微信开发者工具的调试基础库处于灰度…

私域流量如何变现?一站式产品体系搭建与运营策略大公开

在互联网日新月异的今天&#xff0c;我们面临着人口红利逐渐消失的问题&#xff0c;各行各业的广告投放获客成本也在不断上涨。为了降低成本并提高用户转化和复购率&#xff0c;我们需要寻找新的解决方案。此时&#xff0c;私域流量的概念应运而生&#xff0c;成为了一个值得考…

【计算机二级考试C语言】C递归

目录 C 递归 数的阶乘 实例 斐波那契数列 实例 C 递归 递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从…

Python 的 pass 语句到底是什么?

Python 中的 pass 语句是一个简单的概念&#xff0c;即使没有编程经验的初学者也能很快掌握。官方文档提供了非常简单的介绍&#xff0c;下面的三个示例可以让我们快速了解如何使用它。 pass 本质上是一个空操作&#xff0c;除了允许解释器在跳过语法之前检查语法是否有效之外&…