【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.mfbz.cn/a/552153.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Free day2

1.总结串口的发送和接收功能使用到的函数 发送函数&#xff1a;HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout) 参数1&#xff1a;指定要使用的串口 参数2&#xff1a;要发送的数据字节数&#xff…

VIT论文阅读

论文地址&#xff1a;https://arxiv.org/pdf/2010.11929.pdf VIT论文阅读 摘要INTRODUCTION结论RELATEDWORKMETHOD1.VISIONTRANSFORMER(VIT)整体流程消融实验HEAD TYPE AND CLASSTOKENpoisitional embedding 整体过程公式Inductive biasHybrid Architecture 2.FINE-TUNINGANDH…

阿里面试:DDD中的实体、值对象有什么区别?

在领域驱动设计&#xff08;DDD&#xff09;中&#xff0c;有两个基础概念&#xff1a;实体&#xff08;Entity&#xff09;和值对象&#xff08;Value Object&#xff09;。 使用这些概念&#xff0c;我们可以把复杂的业务需求映射成简单、明确的数据模型。正确使用实体和值对…

AI大模型日报#0417:国产音乐SOTA、AI评标师上岗、北大ASC全球超算冠军

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。 标题: 首个国产音乐SOTA模型来了&#xff01;专为中文优化&#xff0c;免费用&#xff0c;不限曲风 摘要: 昆仑万维发布「天工3.0」和…

华为 2024 届实习招聘——硬件-电源机试题(四套)

华为 2024 届实习招聘——硬件-电源机试题&#xff08;四套&#xff09; 部分题目分享&#xff0c;完整版带答案(有答案&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09;&#xff08;共四套&#xff09; 获取&#xff08;WX:didadidadidida313&…

LeetCode———100——相同的树

目录 ​编辑 1.题目 2.解答 1.题目 . - 力扣&#xff08;LeetCode&#xff09; 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&…

两台服务器如何超快速互传文件/文件夹【xshell详细版 速度真的超快!】

如果您需要将一台服务器上的资料传到另一台服务器上&#xff0c;您如果老实地先下载文件到本地或者另外一个地方&#xff0c;再上传到另外一台服务器上&#xff0c;那这样也太浪费您宝贵的时间了吧&#xff01;在这里&#xff0c;只需要使用一个简单的命令&#xff0c;即可实现…

Python 物联网入门指南(八)

原文&#xff1a;zh.annas-archive.org/md5/4fe4273add75ed738e70f3d05e428b06 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第三十章&#xff1a;制作机械臂 最后&#xff0c;我们终于到达了大多数人自本书开始以来就想要到达的地方。制作一个机械臂&#xff01;在…

【MATLAB源码-第25期】基于matlab的8QAM调制解调仿真,手动实现未调用内置函数,星座图展示。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 8QAM调制&#xff08;8 Quadrature Amplitude Modulation&#xff09;是一种数字调制技术&#xff0c;它可以在有限带宽内传输更多的信息比特。在8QAM调制中&#xff0c;每个符号可以携带3个比特的信息。QAM调制是将数字信号…

充电桩--OCPP 充电通讯协议介绍

一、OCPP协议介绍 OCPP的全称是 Open Charge Point Protocol 即开放充电点协议&#xff0c; 它是免费开放的协议&#xff0c;该协议由位于荷兰的组织 OCA&#xff08;开放充电联盟&#xff09;进行制定。Open Charge Point Protocol (OCPP) 开放充电点协议用于充电站(CS)和任何…

同城O2O系统开发实战:外卖送餐APP的技术架构与实现

今天&#xff0c;我们将深入探讨同城O2O系统开发实战中&#xff0c;外卖送餐APP的技术架构与实现。 一、概述 外卖送餐APP是一种典型的O2O应用&#xff0c;通过移动互联网技术&#xff0c;将用户与商家连接起来&#xff0c;实现用户在线订餐&#xff0c;商家配送服务的模式。…

JVM 方法调用之方法分派

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

4.1 返回JSON数据

1. 默认实现方式 JSON是目前主流的前后端数据传输方式&#xff0c;Spring MVC中使用消息转换器HttpMessageConverter对JSON的转换提供了很好的支持&#xff0c;在Spring Boot中更进一步&#xff0c;对相关配置做了更进一步的简化。 默认情况下&#xff0c;当开发者新创建一个S…

4.17号驱动

中断子系统 1. 中断工作原理 1.1 异常处理流程 保存现场(cpu自动完成) 保存cpsr寄存器中的值&#xff0c;到spsr_寄存器中 修改cpsr寄存器中的值 修改状态位(T位) 根据需要禁止相应的中断位(I/F) 修改对应模式位 保存函数的返回地址到lr寄存器中 修改pc指向异常向量表 …

【测试开发学习历程】python常用的模块(下)

目录 8、MySQL数据库的操作-pymysql 8.1 连接并操作数据库 9、ini文件的操作-configparser 9.1 模块-configparser 9.2 读取ini文件中的内容 9.3 获取指定建的值 10 json文件操作-json 10.1 json文件的格式或者json数据的格式 10.2 json.load/json.loads 10.3 json.du…

React 快速入门:掌握前端开发的核心技能

React 快速入门&#xff1a;掌握前端开发的核心技能 一、React 简介1.1 React 的历史1.2 React 的概念1.3 React 的特点1.4 React 的官网地址 二、开发环境搭建三、React 基础3.1 JSX3.2 组件3.3 Props3.4 State3.5 props 和 state 的区别3.6 Hook 四、React 生命周期五、添加样…

OceanBase 4.3 列存存储格式和列存索引存储格式

以 t1 表和索引为例子&#xff0c;下面两张图说明了存储层如何存储数据。 create table t1 (id1 int, id2 int, name varchar(10), salary int, primary key(id1, id2)) with column group (each column);create index idx (name) storing(salary) with column group(each co…

代码随想录算法训练营第三十七天| LeetCode 738.单调递增的数字、总结

一、LeetCode 738.单调递增的数字 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html 状态&#xff1a;已解决 1.思路 如何求得小于等于N的最大单调递增的整数&#xff1f;98&am…

【云计算】云数据中心网络(六):私网连接

云数据中心网络&#xff08;六&#xff09;&#xff1a;私网连接 1.什么是私网连接2.私网连接的组成3.私网连接的优势4.私网连接的主要应用场景 前面讲到 VPC 网络具有隔离性&#xff0c;VPC 之间无法通信。当一个 VPC 中的终端需要访问部署在另一个 VPC 中的服务时&#xff0c…

C++奇迹之旅:构造函数和析构函数

文章目录 &#x1f4dd;类的6个默认成员函数&#x1f320; 构造函数&#x1f309; 概念&#x1f309;特性&#x1f309;三种默认构造函数 &#x1f320; 特性&#x1f6a9;总结 &#x1f4dd;类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真…
最新文章