【C语言】贪吃蛇项目(1) - 部分Win32 API详解 及 贪吃蛇项目思路

文章目录

  • 一、贪吃蛇项目需要实现的基本功能
  • 二、Win32 API介绍
    • 2.1 控制台
    • 2.2 部分控制台命令及调用函数
      • mode 和 title 命令
      • COORD 命令
      • GetStdHandle(获取数据)
      • GetConsoleCursorInfo(获取光标数据)
      • SetConsoleCursorInfo (设置光标属性)
      • GetAsyncKeyState(获取按键情况)
  • 三、贪吃蛇项目游戏设计和分析
    • 3.1 本地化中宽字符的概念及应用
    • 3.2 蛇身及食物等初始化结构设计
    • 3.3 游戏流程设计


一、贪吃蛇项目需要实现的基本功能

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

二、Win32 API介绍

本次实现贪吃蛇会使用到的一些Win32 API知识,接下来我们就学习一下。

Windows 这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,它同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序(Application),所以便称之为 Application Programming Interface,简称AP|函数。WIN32API也就是Microsoft Windows32位平台的应用程序编程接口。

2.1 控制台

我们这次的贪吃蛇项目需要用到控制台来运行,所以在什么空项目调用时应该出现的时调试控制台,就像下图这样
在这里插入图片描述
如果出现的不是 调试控制台 ,就右键调试栏,选择 属性 后找到终端,将 默认终端应用程序 改成 让Windows决定 然后确定,重新调试就行了
在这里插入图片描述

2.2 部分控制台命令及调用函数

mode 和 title 命令

我们平常调试出来的黑框其实就是控制台,对于这个控制台我们可以改变长宽,也可以改变名字,这就要用到 modetitle 命令,但是在调试时我们需要借用 system 来调用它们

//system 函数可以用来执行系统命令
int main()
{设置控制台相关属性system("mode con cols=100 lines=30");//cols-行,lines-列 system("title 贪吃蛇");//getchar(); 停止运行system("pause");//程序暂停return 0;
}

这里最后需要放上 getchar()system(“pause”) 来暂停程序,否则因为程序运行好就停止了,无法向控制台一样,直接更改控制台名字。调试结果如下
在这里插入图片描述

COORD 命令

COORD 是Windows API中定义的一个结构体,表示一个字符在控制台屏幕幕缓冲区上的坐标,坐标系(0,0)的原点位于缓冲区的顶部左侧单元格。关于控制台的坐标轴大家可以看成下图
在这里插入图片描述
这是 COORD 命令的声明,里面包含了坐标的x和y坐标,需要注意的是这里调用需要添加头文件 <Windows.h>

typedef struct _COORD {SHORT X;SHORT Y;
} COORD, * PCOORD;

GetStdHandle(获取数据)

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

//函数声明
HANDLE GetStdHandle(DWORD nStdHandle);

利用这个调用函数,我们可以设定某点的坐标,比如这样

int main()
{//获得标准输出设备的句柄HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);return 0;
}

我们观察 HANDlE ,发现它是重命名 void* 的一个指针变量,所以当我们不打算给某点一个确定的坐标值时,可以将它设为 NULL
在这里插入图片描述

GetConsoleCursorInfo(获取光标数据)

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

	//声明BOOL WINAPI GetConsoleCursorInfo(HANDLE hConsoleOutput,PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);//PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标

关于 CONSOLE_CURSOR_INFO ,这个结构体,包含有关控制台光标的信息,它里面的内容是这样

typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;BOOL bVisible;
} CONSOLE_CURSOR_INFO, * PCONSOLE_CURSOR_INFO;
  • dwSize,由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完全填充单元格到单元底部的水平线条。
  • bVisible,游标的可见性。如果光标可见,则此成员为 TRUE。我们也可以设置为 FALSE 让它变得不可见

举个例子,这样我们就可以获取和houtput句柄相关的控制台上的光标信息,存放在 cursor_info 中了

int main()
{//获得标准输出设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定义一个光标信息的结构体CONSOLE_CURSOR_INFO cursor_info = { 0 };//获取和houtput句柄相关的控制台上的光标信息,存放在cursor_info中GetConsoleCursorInfo(houtput, &cursor_info);return 0;
}

SetConsoleCursorInfo (设置光标属性)

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

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

比如我们将光标的填充变成100(默认为25)

int main()
{//获得标准输出设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定义一个光标信息的结构体CONSOLE_CURSOR_INFO cursor_info = { 0 };//获取和houtput句柄相关的控制台上的光标信息,存放在cursor_info中GetConsoleCursorInfo(houtput, &cursor_info);//修改光标占比cursor_info.dwSize = 100;//设置和houtput句柄相关的控制台上的光标信息SetConsoleCursorInfo(houtput, &cursor_info);system("pause");return 0;
}

我们可以发现确实发生了变化
在这里插入图片描述
既然这样,我们就可以设计一个封装方法来改变光标的位置,就不用每次改变光标位置时都写这么都行了

void set_pos(int x, int y)
{//获得标准输出的设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x,y };SetConsoleCursorPosition(houtput, pos);
}int main()
{set_pos(10, 20);printf("hehe\n");set_pos(10, 10);printf("hehe\n");return 0;
}

在这里插入图片描述

GetAsyncKeyState(获取按键情况)

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

关于虚拟键码,大家可以点击这个链接查看 link:虚拟键码

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

如果我们要判断一个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1。即将它与1按位与得出结果

因此我们可以设计出这样一个宏来实现它

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
//VK即按键的虚拟键码

中间一列就是虚拟键码
在这里插入图片描述

讲到这里差不多就是贪吃蛇项目所需的所有API了,但是控制台所拥有的API函数远不止如此,有兴趣的朋友可以点开下面链接 link:API函数


三、贪吃蛇项目游戏设计和分析

大家看,如果不进行处理我们地图根据坐标和宽度、高度打印出来就会是这样
在这里插入图片描述
很明显高大于宽,这样我们就不好确定贪吃蛇的每个节点,也不方便创建,
所以就需要用到宽字符

3.1 本地化中宽字符的概念及应用

简单来说,C语言设计时只考虑了英语,所以最开始只有8位二进制来存储数据,但是相对于中文的十万汉字等基数极大的语言就非常不够用,因此 本地化 应运而生,宽字符 就是其中之一。

关于本地化,也进行一个简单介绍
<locale.h>提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分。在标准中,依赖地区的部分有以下几项:

  • 数字量的格式
  • 货币量的格式
  • 字符集
  • 日期和时间的表示形式

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

  • LC COLLATE:影响字符串比较函数strcoll()和strxfrm()。
  • LC CTYPE:影响字符处理函数的行为。
  • LC MONETARY:影响货币格式。
  • LC_NUMERIC:影响 printf()的数字格式。
  • LC TIME:影响时间格式 strftime()和 wcsftime()。
  • LC ALL-针对所有类项修改,将以上所有类别设置为给定的语言环境。

更多: link

为了修改当前地区我们需要用到 setlocale函数

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

setlocale 的第一个参数可以是前面说明的类项中的一个,那么每次只会影响一个类项,如果第一个参数是LC ALL,就会影响所有的类项。
C标准给第二个参数仅定义了2种可能取值:“C”(正常模式)和""(本地模式)。也可以用”NULL“获取当前的默认模式

宽字符的打印

宽字符的字面量必须加上前缀“L”,否则C语言会把字面量当作窄字符类型处理。前缀“乚”在单引号前面,表示宽字符,对应 wprintf()的占位符为 %lc;在双引号前面,表示宽字符串,对应wprintf()的占位符为 %ls。

这样就能打印出来了

#include <stdio.h>
#include<locale.h>
int main() 
{setlocale(LC_ALL, "");wchar_t ch1 = L'●';wchar_t ch2 = L'比';wchar_t ch3 = L'特';wchar_t ch4 = L'★';printf("%c%c\n", 'a', 'b');wprintf(L"%lc\n", ch1);wprintf(L"%lc\n", ch2);wprintf(L"%lc\n", ch3);wprintf(L"%lc\n", ch4);return 0;
}

在这里插入图片描述

3.2 蛇身及食物等初始化结构设计

在游戏运行的过程中,蛇每次吃一个食物,蛇的身体就会变长一节,如果我们使用链表存储蛇的信息,那么蛇的每一节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行

关于蛇身我们只需要确定蛇头的位置坐标,然后利用链表就可以找到后面的蛇身直至蛇尾

不仅如此,我们对整条蛇还需要进行一个封装,来确定方向、状态等

所以我们先利用枚举罗列方向和状态

//蛇的方向
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};//蛇的状态
//正常、撞墙、撞到自己、正常退出
enum GAME_STATUS
{OK,//正常状态KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END_NORMAL//正常退出
};

然后再进行整条蛇的封装,为了方便去下一个节点的坐标及对整条蛇的实参进行改变,我们使用指针来重命名各个结构体

//蛇身的节点类型
typedef struct SnakeNode
{//坐标int x;int y;//指向下一个节点的指标struct SnakeNode* next;
}SnakeNode, * pSnakeNode;//将这个结构体重命名为一个指针//贪吃蛇
typedef struct Snake
{pSnakeNode _pSnake;//指向蛇头的指针pSnakeNode _pfood;//指向食物节点的指针enum DIRECTION _dir;//蛇的方向enum GAME_STATUS _status;//游戏的状态int _score;//总成绩int _sleep_time;//休息时间,时间越短,速度越快,时间越长,速度越慢
}Snake, * pSnake;

由此一来蛇的基本结构就设置完毕了

3.3 游戏流程设计

整体流程放在下面,具体游戏实现请看下回分解😊

在这里插入图片描述

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

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

相关文章

蓝桥杯(基础题)

试题 C: 好数 时间限制 : 1.0s 内存限制: 256.0MB 本题总分&#xff1a;10 分 【问题描述】 一个整数如果按从低位到高位的顺序&#xff0c;奇数位&#xff08;个位、百位、万位 &#xff09;上 的数字是奇数&#xff0c;偶数位&#xff08;十位、千位、十万位 &…

PhotoShop2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Adobe Photoshop是一款由Adobe Systems开发的图像编辑软件。它被广泛用于图像处理和数字艺术创作&#xff0c;是设计师、摄影师和艺术家们的首选工具之一。 主要功能&#xff1a; 图像编辑&#xff1a; Photoshop提供了丰富的编辑…

激光共聚焦显微镜测粗糙度,解读表面粗糙度的科技利器

激光共聚焦显微镜&#xff08;Laser Scanning Confocal Microscope&#xff0c;简称LSCM&#xff09;是一种光学显微镜&#xff0c;通过激光束的聚焦和散射技术&#xff0c;能够实现高分辨率的三维图像采集和表面测量。其在科学研究、工程领域等领域有着广泛的应用&#xff0c;…

【环境搭建】(五)Ubuntu22.04安装cuda_11.8.0+cudnn_8.6.0

一个愿意伫立在巨人肩膀上的农民...... 设备配置&#xff1a; 一、安装GCC 安装cuda之前&#xff0c;首先应该安装GCC&#xff0c;安装cuda需要用到GCC&#xff0c;否则报错。可以先使用下方指令在终端查看是否已经安装GCC。 gcc --version 如果终端打印如下则说明已经安装…

使用SpringBoot将中国地震台网数据保存PostGIS数据库实践

目录 前言 一、数据转换 1、Json转JavaBean 2、JavaBean与数据库字段映射 二、空间数据表设计 1、表结构设计 三、PostGIS数据保存 1、Mapper接口定义 2、Service逻辑层实现 3、数据入库 4、运行实例及结果 总结 前言 在上一篇博客中基于Java的XxlCrawler网络信息爬…

c++的学习之路:23、多态(2)

摘要 多态的抽象类、原理以及虚表。 目录 摘要 一、抽象类 1、 概念 2、接口继承和实现继承 二、多态的原理 1、虚函数表 2、动态绑定与静态绑定 三、单继承和多继承关系的虚函数表 1、单继承中的虚函数表 2、多继承中的虚函数表 四、思维导图 一、抽象类 1、 概…

消息中间件Kafka分布式数据处理平台

目录 一.Kafka基本介绍 1.定义 2.特点 &#xff08;1&#xff09;高吞吐量、低延迟 &#xff08;2&#xff09;可扩展性 &#xff08;3&#xff09;持久性、可靠性 &#xff08;4&#xff09;容错性 &#xff08;5&#xff09;高并发 3.系统架构 &#xff08;1&#…

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第五套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第五套 (共9套&#xff0c;有答案和解析&#xff0c;答案非官方&#xff0c;仅供参考&#xff09;&#xff08;共九套&#xff0c;每套四十个选择题&#xff09; 部分题目分享&#xff0c;完整版获取&#xff08;WX:didadida…

JVM 方法调用之方法分派

JVM 方法调用之方法分派 文章目录 JVM 方法调用之方法分派1.何为分派2.静态分派3.动态分派4.单分派与多分派5.动态分派的实现 1.何为分派 在上一篇文章《方法调用之解析调用》中讲到了解析调用&#xff0c;而解析调用是一个静态过程&#xff0c;在类加载的解析阶段就确定了方法…

ECharts:五大卓越在线示例库助力高效数据可视化开发

1. ECharts官方示例库 ECharts官网提供的示例库是最权威、最新的展示平台&#xff0c;涵盖了所有基础和高级图表类型&#xff0c;每个示例都配有详尽的代码解释和配置说明。开发者可以直接查看源代码&#xff0c;复制粘贴后稍加修改就能应用于实际项目中。 2. Make A Pie - EC…

【笔试训练】day4

不到5分钟写完&#xff0c;今天的题又又又难一点啦! 1.Fibonacci数列 思路&#xff1a; 直接模拟一遍斐波那契数列的递增过程&#xff0c;大于n就直接结束。因为后面只会越来越大&#xff0c;跟题目求的最小步数不符。在这个过程中用一个变量去维护这个当前的元素与目标n还差…

【编程TOOL】VC++6.0下载安装配置使用保姆式教程

目录 ​编辑 1.软件介绍 2.软件下载 3.软件安装 3.1.下载得到可执行文件并双击进行安装 3.2. 点击下一步 3.3. 选择安装位置 3.4. 勾选“创建桌面快捷方式”并点击下一步 5. 点击安装并等待 3.6. 先取消运行&#xff0c;后点击完成&#xff0c;软件即安装完毕 4.兼容性配置 4.1…

基于SpringBoot+Vue的疾病防控系统设计与实现(源码+文档+包运行)

一.系统概述 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对疾病防控信息管理的提升&a…

IoC与Spring

目录 IoC控制反转 现实案例 特点 目的 DI依赖注入 小总结 介绍Spring 狭义和广义上的Spring 传统编码方式的不足 需求引入 弊端分析 IoC控制反转 现实案例 1、买水果问老板各种水果的口感而不是自己去挨个尝试一遍。 2、买房子找中介而不是自己去花时间找房东。…

别找了,这35份Excel自动排班表真的好用!

别再自己做排班表了&#xff0c;调了半天不好看格式还不对。 看看自己需要的是哪些类型的排班表&#xff1f;是公司值班&#xff0c;还是直播排班&#xff0c;还是考勤汇总&#xff0c;总有一个适合你。 刚整理的35份办公常用的排班表&#xff0c;希望能帮到你&#xff01; …

基于Python的机器学习的文本分类系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

CentOS 7安装Redis

说明&#xff1a;本文介绍如何在CentOS 7操作系统下安装Redis 下载安装 首先&#xff0c;去官网上下载所需要安装的版本&#xff0c;官网地址&#xff1a;https://download.redis.io/releases/&#xff0c;我这里下载3.2.1版本的 下载完&#xff0c;上传到云服务器上&#xf…

<router-link>出现Error: No match for {“name“:“home“,“params“:{}}

在将<a></a>标签换到<router-link></router-link>的时候出现No match for {"name":"home","params":{}}这样的错误&#xff0c;其中格式并无错误&#xff0c; <router-link class"navbar-brand active" …

她在《繁花》大放异彩,“浪姐”暴瘦15斤,打脸了不看好她的观众

不知不觉&#xff0c;《浪姐》已经迎来第5季了。播到第4季的时候&#xff0c;改名成《乘风破浪2023》&#xff0c;这一季叫《乘风2024》&#xff0c;和前几季相比&#xff0c;热度依然不减。 都说3个女人一台戏&#xff0c;更何况这个节目&#xff0c;每次能请到30位姐姐&…

刷题。。。。。。

1.ezmd5 根据题目提示 我们知道应该是要上传两张md5值相同的图片 根据原文链接&#xff1a;cryptanalysis - Are there two known strings which have the same MD5 hash value? - Cryptography Stack Exchange 把保存下来的图片上传一下 得到flag 2.ezhttp 根据原文链接&…