贪吃蛇(上)Win32API

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步

个人主页:LaNzikinh-CSDN博客

文章目录

  • 前言
  • 一、Win32 API
  • 二、地图的绘制和初始化
  • 总结

前言

贪吃蛇(也叫做贪食蛇)游戏是一款休闲益智类游戏,有PC和手机等多平台版本。既简单又耐玩。该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长,使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇。


在实现这次贪吃蛇小游戏的时候,我们需要应用到一些关于Win32 API的一些知识,所以我们把贪吃蛇游戏的实现分为上下两篇来讲,商品,我们主要就是讲解Win32 API的知识

一.Win32 API

什么是Win32 API:Win32 API Windows 这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,它同时也是⼀个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序(Application), 所以便称之为 Application

Programming Interface,简称 API 函数。WIN32 API也就是Microsoft Windows 32位平台的应用程序编程接口。

控制台程序

我们平常运行起来的黑框,他就是控制台程序,但是其实很多人都不是用的控制台程序,用的是终端,这两个是有区别的,控制台程序,它可以利用一些命令来操控终端就不可以啊,这次的贪吃蛇游戏就必须要应用控制台程序,如果还是终端操控的话,可以在设置里调回控制台程序

然后我们接下来讲一些控制指令

1.1窗口与命名

在说之前我们还要引入一个函数system 函数可以用来执行系统的命令,我们之前写的关机指令就是用的这个,这个在一些游戏项目的实现上,非常有用。

设置窗口mode con cols=100 lines=30,title 贪吃蛇重命名,这两个就是很简单的命令,利用system,可以用来改变控制台窗口的大小和控制台的名字

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

用之前记得停一下,不然看不到效果,可以用getchar获取字符来停,也可以直接用命令去暂停

1.2控制台屏幕上的坐标COORD

其实,控制台可以看成一个数轴,居然是一个数轴的话,那么数轴里面每个点就是坐标,控制台有一个特性就是竖着的坐标是横着坐标的两倍,所以说我们要完成这个游戏的话,我们肯定要想办法得知每个点的坐标,计算机中专门定义了一个关于这个坐标的结构体,我们直接用就可以了。

typedef struct _COORD {SHORT X;SHORT Y;
} COORD, * PCOORD;int main()
{COORD pos1 = { 0,0 };COORD pos2 = { 20,30 };return 0;
}

1.3 GetStdHandleGetStdHandle 函数 - Windows Console | Microsoft Learn

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

你可以把它抽象的理解成,如果我要炒饭,我炒饭的锅子前我先要获得一个句柄,这样我才能进行炒,我干什么事情之前都需要先引用下这个函数

int main()
{HANDLE hOutput = NULL;//获取标准输出的句柄hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
return 0;
}

这个主要是和别的函数绑定一起用,我们来看下一个

1.4GetConsoleCursorInfoGetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn

这个他主要是可以获取句柄相关的控制台上的光标信息,就是我通过这个函数,我可以获取到一个光标的信息,那我要怎么拉到这个光标的信息,这里这个系统已经提前给我们定义了一个关于光标的结构体CONSOLE_CURSOR_INFO

typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
int main()
{//获取标准输出的句柄HANDLE hOutput = NULL;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//这个结构体,包含有关控制台光标的信息CONSOLE_CURSOR_INFO CursorInfo;//获取和hOutput句柄相关的控制台上的光标信息,存放在CursorInfo去GetConsoleCursorInfo(hOutput, &CursorInfo);printf("%d", CursorInfo.dwSize);//不可以直接改,因为只看到了台屏幕缓冲区的光标⼤⼩和可⻅性的信息return 0;
}

这时候我们会打印出一个结果25是什么意思呢?

dwSize:由光标填充的字符单元格的百分比。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的水平线条。 bVisible:游标的可见性。 如果光标可见,则此成员为TRUE。

因为贪吃蛇这个游戏总体下来肯定是不需要看见光标的,所以为了游戏的美观,我们要将光标进行隐藏,但是不可以直接改,因为只看到了台屏幕缓冲区的光标大小和可见性的信息所以。

1.5SetConsoleCursorInfoSetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn

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

int main()
{//获取标准输出的句柄HANDLE hOutput = NULL;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//这个结构体,包含有关控制台光标的信息CONSOLE_CURSOR_INFO CursorInfo;//获取和hOutput句柄相关的控制台上的光标信息,存放在CursorInfo去GetConsoleCursorInfo(hOutput, &CursorInfo);//修改光标信息CursorInfo.bVisible = false;//设置和hOutput句柄相关的控制台上的光标信息SetConsoleCursorInfo(hOutput, &CursorInfo); return 0;
}

这样就完美的将光标隐藏了

1.6SetConsoleCursorPosition SetConsoleCursorPosition 函数 - Windows Console | Microsoft Learn

光标除了被隐藏,那么我们可不可以把光标设置到指定位置去呢?比如说我的贪吃蛇游戏标题我要把它放到中间去,那么我该如何去做呢?

开始首先我们先必须获得标准输出的句柄,然后我就要定位光标的位置先输入,我需要光标的位置,再把光标位置和句柄传入到这个函数中就可以了

int main()
{//获取标准输出的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos= { 10,30 };SetConsoleCursorPosition(houtput, pos);	return 0;
}

但是在后续我们写贪吃蛇的时候,我们需要大量定位,我们不可能每次都去获取句柄在定位光标,所以我们可以直接把它包装成一个函数,需要的时候就直接调用

void SetPos(short x, short y)
{//获取标准输出的句柄HANDLE hOutput = NULL;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posCOORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);
}int main()
{//获取标准输出的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置/*COORD pos= { 10,30 };SetConsoleCursorPosition(houtput, pos);*/SetPos(10, 30);return 0;
}

1.7GetAsyncKeyStategetAsyncKeyState 函数 (winuser.h) - Win32 apps | Microsoft Learn

我们在玩贪吃蛇小游戏的时候,键盘点击左贪吃蛇就会向左转,他是如何感应到的呢?就和这个函数有关,这个函数它可以判断一个键是否被摁过,我们键盘上的每个键其实都有一个虚拟键代码

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

我们可以通过这个函数和虚拟间代码来判断一个键是是否按过没有,将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

如果我们要判断一个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.那这样子的话,我们就可以通过一个宏来直接定义

 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

这也是后面会用到的

然后贪吃蛇关于Win32 API的内容就没有了

二.地图的绘制和初始化

我先看看我们这次完成什么

2.1宽字符的打印

在开始前,我们还需要讲一下宽字符的打印,如图三可得地图周围的墙食物还有蛇这些符号都是宽字符,那在四中我们该如何打印宽字符呢?宽字符之所以较宽字符,是因为它跟C语言最初假定的字符不一样C语言最初假定的字符都是单字节的宽字符,他是占两个字节的过去,C语言并不适合非英语地方国家使用,所以关于宽字符的打印和C语言的国际化特性是有关系的,这里我就不多讲了,我们这里要引入一个头文件的概念就可以了

<locale.h>本地化 提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分。

然后用里面的setlocale 函数,setlocale 函数用于修改当前地区,可以针对一个类项修改,也可以针对所有类项。setlocale 的第一个参数可以是前⾯说明的类项中的一个,那么每次只会影响一个类项,如果第一个参 数是LC_ALL,就会影响所有的类项。 C标准给第⼆个参数仅定义了2种可能取值:"C"(正常模式)和" "(本地模式)。

宽字符的打印那如果想在屏幕上打印宽字符,怎么打印呢?

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

2.2初始化

我们开始先把游戏进行初始化,贪吃蛇的整个身体,我们肯定要用链表的形式来构建吃一个食物,就头插一下链表,所以我们先创造一个蛇的节点的结构体,肯定要有坐标,还有指向下一个节点的指针。

//蛇身的节点类型
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 _food_weight;//一个食物的分数int _score;      //总成绩int _sleep_time; //休息时间,时间越短,速度越快,时间越长,速度越慢
}Snake, * pSnake;

蛇的方向和游戏状态,我们有两个枚举变量,方向很好理解就是上下左右

//蛇的方向
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};

状态的划分好几种,比如说正常,那就是蛇没有事撞墙撞到墙撞到自己,还有正常退出游戏这几种

//蛇的状态
//正常、撞墙、撞到自己、正常退出
enum GAME_STATUS
{OK, //正常KILL_BY_WALL, //撞墙KILL_BY_SELF, //撞到自己END_NORMAL //正常退出
};

2.3地图的绘制

地图的绘制之前,我们首先要打印前面两页东西,欢迎来到贪吃蛇小游戏和游戏规则的讲解,我们直接用下标定位函数来定位注意,我现在已经切换模式了,所以我的打印要用宽字符去打印,欢迎来到游戏之后直接清屏幕,这样子就可以做到一页两页的效果了。欢迎来到游戏之后直接清屏幕

void WelcomeToGame()
{SetPos(40, 14);wprintf(L"欢迎来到贪吃蛇小游戏\n");SetPos(42, 20);system("pause");system("cls");SetPos(25, 14);wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速\n");SetPos(25, 15);wprintf(L"加速能够得到更高的分数\n");SetPos(42, 20);system("pause");system("cls");
}

先用#define来定义一个墙的宽字符

#define WALL L'□'

然后通过上下左右的顺序来打印,注意横着是竖着的两倍,所以打印左右的时候,特别是右一定要注意是两倍的关系.

void CreateMap()
{//上int i = 0;for (i = 0; i < 29; i++){wprintf(L"%lc ", WALL);}//下SetPos(0, 26);for (i = 0; i < 29; i++){wprintf(L"%lc ", WALL);}//左for (i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", WALL);}//右for (i = 1; i <= 25; i++){SetPos(56, i);wprintf(L"%lc", WALL);}}

总结

这是主要讲解了Win32 API的使用,后面在讲解贪吃蛇的实现

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

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

相关文章

深入学习Redis(1):Redis内存模型

Redis的五个对象类型 字符串&#xff0c;哈希&#xff0c;列表&#xff0c;集合&#xff0c;有序集合 本节有关redis的内存模型 1.估算redis的内存使用情况 目前内存的价格比较的高&#xff0c;如果对于redis的内存使用情况能够进行计算&#xff0c;就可以选用合适的设备进…

基于MSOGI的交叉对消谐波信号提取网络MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介&#xff1a; 此模型利用二阶广义积分器&#xff08;SOGI&#xff09;对基波电流和相应次的谐波电流进行取 &#xff0c;具体是通过多个基于二阶广义积分器的正交信号发生器 &#xff08; S&#xf…

JavaScript入门:用JS点亮你的第 1 个网页圣诞树!

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

JVM笔记1--Java内存区域

1、运行时数据区域 从上图可以看出来&#xff0c;Java虚拟机运行时数据区域整体上可以分成5大块&#xff1a; 1.1、程序计数器 程序计数器是一块较小的内存空间。它可以看做当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里&#xff0c;字节码解释器工作时就是…

vue实现滚动条联动(一个滚动条控制两个或多个)

两个表格需要进行比对&#xff0c;两个表格是互相独立的&#xff0c;如果滚动条不能同步&#xff0c;用户就要操作两个两次&#xff0c;体验不是太好&#xff0c;如下图&#xff1a; 因此想使两个滚动条同步&#xff0c;思路如下&#xff1a; 给两个表格定义ref&#xff08;便…

环形链表的判断方法与原理证明

&#xff08;题目来源&#xff1a;力扣&#xff09; 一.判读一个链表是否是环形链表 题目&#xff1a; 解答&#xff1a; 方法&#xff1a;快慢指针法 内容&#xff1a;分别定义快慢指针&#xff08;fast和slow&#xff09;&#xff0c;快指针一次走两步&#xff0c;慢指…

机器学习的指标评价

之前在学校的小发明制作中&#xff0c;在终期答辩的时候&#xff0c;虽然整个项目的流程都答的很流畅。 在老师提问的过程中&#xff0c;当老师问我recall,precision,accuracy等指标是如何计算的&#xff0c;又能够表示模型的哪方面指标做得好。我听到这个问题的时候&#xff…

如何选购骨传导耳机?精选五大拔尖宝藏骨传导耳机,闭眼入也不踩雷!

尽管目前市面上的骨传导耳机热度非常高&#xff0c;一度成为当下最热门的耳机款式&#xff0c;但作为有着资深工作经验的数码测评师&#xff0c;我仍然要提醒大家&#xff1a;在选择骨传导耳机的时候&#xff0c;不要盲目选择网红品牌&#xff0c;因为市场上的许多骨传导耳机过…

用LM Studio搭建微软的PHI3小型语言模型

什么是 Microsoft Phi-3 小语言模型&#xff1f; 微软Phi-3 模型是目前功能最强大、最具成本效益的小型语言模型 &#xff08;SLM&#xff09;&#xff0c;在各种语言、推理、编码和数学基准测试中优于相同大小和更高大小的模型。此版本扩展了客户高质量模型的选择范围&#x…

golang判断通道chan是否关闭的2种方式

chan通道在go语言的办法编程中使用频繁&#xff0c;我们可以通过以下2种方式来判断channel通道是否已经关闭&#xff0c;1是使用 for range循环&#xff0c;另外是通过 for循环中if 简短语句的 逗号 ok 模式来判断。 示例代码如下&#xff1a; //方式1 通过for range形式判断…

现代循环神经网络(GRU、LSTM)(Pytorch 14)

一 简介 前一章中我们介绍了循环神经网络的基础知识&#xff0c;这种网络 可以更好地处理序列数据。我们在文本数据上实现 了基于循环神经网络的语言模型&#xff0c;但是对于当今各种各样的序列学习问题&#xff0c;这些技术可能并不够用。 例如&#xff0c;循环神经网络在…

centos7 openresty lua 自适应webp和缩放图片

目录 背景效果图准备安装cwebp等命令&#xff0c;转换文件格式安装ImageMagick&#xff0c;压缩文件下载Lua API 操控ImageMagick的依赖包 代码参考 背景 缩小图片体积&#xff0c;提升加载速度&#xff0c;节省流量。 效果图 参数格式 &#xff1a; ?image_processformat,…

PyVista 3D数据可视化 Python 库 简介 含源码

Pyvista是一个用于科学可视化和分析的Python库 &#xff1b;我认为它适合做一些网格数据的处理&#xff1b; 它封装了VTK&#xff08;Visualization Toolkit&#xff09;之上&#xff0c;提供了一些高级接口&#xff0c; 3D数据可视化变得更加简单和易用。 1.安装 pyvista&…

蓝桥杯-路径之谜

题目描述 小明冒充X星球的骑士&#xff0c;进入了一个奇怪的城堡。城堡里面什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡的地面时n*n个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或者纵向移动&#xff0c;但是不能斜着走&#x…

【设计模式】函数式编程范式工厂模式(Factory Method Pattern)

目录标题 定义函数式接口函数式接口实现类工厂类封装实际应用总结 定义函数式接口 ISellIPad.java /*** 定义一个函数式接口* param <T>*/ FunctionalInterface public interface ISellIPad<T> {T getSellIPadInfo();}函数式接口实现类 HuaWeiSellIPad.java pu…

JAVA系列 小白入门参考资料 接口

目录 接口 接口的概念 语法 接口使用 接口实现用例 接口特性 实现多个接口和实现用例 接口间的继承 接口 接口的概念 在现实生活中&#xff0c;接口的例子比比皆是&#xff0c;比如&#xff1a;笔记本上的 USB 口&#xff0c;电源插座等。 电脑的 USB 口上&am…

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

WPF之绑定验证(错误模板使用)

1&#xff0c;前言&#xff1a; 默认情况下&#xff0c;WPF XAML 中使用的绑定并未开启绑定验证&#xff0c;这样导致用户在UI上对绑定的属性进行赋值时即使因不符合规范内部已抛出异常&#xff08;此情况仅限WPF中的数据绑定操作&#xff09;&#xff0c;也被程序默认忽略&…

C#调用skiasharp操作并绘制图片

之前学习ViewFaceCore时采用Panel控件和GDI将图片及识别出的人脸方框和关键点绘制出来&#xff0c;本文将其修改为基于SKControl和SKCanvas实现相同的显示效果并支持保存为本地图片。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装一下SkiaSharp和ViewFaceCore…

AD单通道/多通道

1.AD单通道&#xff08;单次转换&#xff0c;非扫描模式&#xff09; 1.1 接线图 1.2 AD.c #include "stm32f10x.h" // Device headervoid AD_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA时钟RCC_APB2PeriphCl…