无引擎游戏开发(2):最简游戏框架 | EasyX制作井字棋小游戏I
一、EasyX中的坐标系
不同于数理中的坐标系,EasyX中的y轴是竖直向下的

二、渲染缓冲区
之前的程序添加了这三个函数改善了绘图时闪烁的情况:

小球在"画布“上移动的过程就是我们在调用绘图函数,这个”画布“就是渲染缓冲区,先绘制的内容可能被后面绘制的内容覆盖掉,所以调用cleardevice()函数,相当于用当前背景颜色覆盖画布实现”清屏“。小球”闪烁“的原因在于,绘制过程并不是一瞬间,而是”逐渐“发生的,先前绘制的小球被不断清除,而人眼同时观察到”绘制“与”清除“,所以有种”闪烁“的感觉。
而在调用BeginBatchDraw函数后,EasyX会为我们新建一个画布(渲染缓冲区):

这个缓冲区是默认不可见的,后面进行的所有绘图操作都会绘制在新的画布上:

而在调用FlushBatchDraw或EndBatchDraw函数时EasyX会将前后两个缓冲区进行快速对调:

所以我们将不会再看见在一张画布上由于绘图频繁而导致的”闪烁“。
三、游戏框架
在之前程序中我们运用了死循环来阻塞程序来防止窗口退出,同时在循环中不断执行清屏和绘制操作,这其实已经完成了游戏框架最核心的部分”主循环“,在主循环中不断读取玩家鼠标操作并将这些操作翻译成对数据的处理逻辑,最后再根据现有的逻辑将画面内容绘制出来。也即读取操作——处理数据——绘制画面,在循环中不断执行这些操作便可以完成几乎所有游戏的逻辑设计:

先前代码如下;
#include<graphics.h>int main() {initgraph(1280, 720);int x, y;//BeginBatchDraw();while (1) {ExMessage msg;while (peekmessage(&msg)) {if (msg.message == WM_MOUSEMOVE) {x = msg.x;y = msg.y;}}cleardevice();solidcircle(x, y, 100);//FlushBatchDraw();}//EndBatchDraw();return 0;}
peekmessage这部分就是读取操作:

将读取到的鼠标坐标赋值给先前定义的x、y就是处理数据:

然后solidcircle便是绘制画面。
还有,一开始需要初始化,最后需要释放资源。
四、设计本地环境的井字棋
井字棋共九格,X/O双方先后落子,同一颗棋子斜或竖连成一线则一方获胜,如果棋盘被填满则平局。
依据框架确定设计思路:①读取操作:双方只使用鼠标游玩,所以只需要读取鼠标消息。 ②处理数据:根据数据检测输赢平局情况,以弹窗形式告知,然后退出主循环。③绘制画面:使用line函数绘制直线将画布分割为3 x 3棋盘,绘制X棋子时只需要使用line函数绘制对应网格的对角线即可完成;绘制O棋子时只需要使用circle函数绘制以对应网格中心为圆心的圆即可。除此之外,在棋盘上方给出当前落子类型给予玩家提示。
接下来便是组织游戏的数据结构,可以使用二维数组表示棋盘

数组默认值为‘-’,代表没有棋子。
游戏结束的条件:①某玩家获胜,可能情况有8种—竖行三种、横行三种、斜角两种 ②平局,在没有玩家获胜前提下,数组中没有‘-’默认值。
开始写代码:首先是读取操作创建ExMessage存储消息/peekmessage获取消息
#include<graphics.h>int main() {initgraph(600, 600);ExMessage msg;bool running = true;//加一个控制变量控制循环,想要结束时改成false即可终止循环while (running) {while (peekmessage(&msg)) {}}return 0;}
接下来是数据处理:把数据拿过来进行判断指定棋子玩家是否获胜—创建CheckWin函数,这个函数接受char类型变量并返回布尔值用来检测对应棋子玩家是否满足获胜条件;创建CheckDraw函数,不传入参数返回布尔值来判断是否平局。当检测到玩家获胜,我们调用MessageBox函数弹窗,同时修改主循环控制条件使游戏结束运行。
小插曲:对MessageBox函数的解释
MessageBox函数接收4个参数,分别为父窗口句柄(相当于win系统中用于指向窗口对象的指针,可使用GetHWnd函数获取用来获取当前绘图窗口的句柄)、提示内容、弹窗标题、弹窗样式

接下来就可以编写获胜情况了:
if(CheckWin('X')){MessageBox(GetHwnd(), _T("X玩家获胜"), _T("游戏结束"), MB_OK);running = false;
}
else if(CheckWin('O'){MessageBox(GetHWnd(), _T("O玩家获胜"), _T("游戏结束"), MB_OK);running = false;
}
else if(CheckDraw()){MessageBox(GetHWnd(), _T("平局!"), _T("游戏结束"), MB_OK);running = false;
}
GetHwnd() 获取当前绘图窗口的句柄,_T(" ")决定显示界面输出内容,MB_OK决定显示界面只出现OK按钮。
接下来便是游戏画面的绘制,首先确保开启了批量绘图BeginBatchDraw() + EndBatchDraw(),并且在每次绘图后刷新批量绘图的渲染缓冲区FlushBatchDraw()。随后在每次绘图前清空画面cleardevice()。定义DrawBoard函数绘制棋盘网格。定义DrawPiece函数绘制棋子。定义DrawTipText函数定义当前棋子的文本提示信息。
全局大致框架如下:
#include<graphics.h>bool CheckWin(char c) {}bool CheckDraw() {}void DrawBoard() {}void DrawPiece() {}void DrawTipText() {}
int main() {initgraph(600, 600);ExMessage msg;BeginBatchDraw();bool running = true;while (running) {while (peekmessage(&msg)) {}if (CheckWin('X')) {MessageBox(GetHWnd(), _T("X玩家获胜"), _T("游戏结束"), MB_OK);running = false;}else if (CheckWin('O')) {MessageBox(GetHWnd(), _T("O玩家获胜"), _T("游戏结束"), MB_OK);running = false;}else if (CheckDraw()) {MessageBox(GetHWnd(), _T("平局!"), _T("游戏结束"), MB_OK);running = false;}cleardevice();DrawBoard();DrawPiece();DrawTipText();FlushBatchDraw();}EndBatchDraw();return 0;}
相关文章:
无引擎游戏开发(2):最简游戏框架 | EasyX制作井字棋小游戏I
一、EasyX中的坐标系 不同于数理中的坐标系,EasyX中的y轴是竖直向下的 二、渲染缓冲区 之前的程序添加了这三个函数改善了绘图时闪烁的情况: 小球在"画布“上移动的过程就是我们在调用绘图函数,这个”画布“就是渲染缓冲区,先绘制的内…...
排书 IDA*
原题链接 题目描述 给定 n 本书,编号为 1∼n。 在初始状态下,书是任意排列的。在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。我们的目标状态是把书按照 1∼n 的顺序依次排列。求最少需要多少次操作。 输…...
playwright录制脚本原理
Paywright录制工具UI 在上一篇博客中介绍了如何从0构建一款具备录制UI测试的小工具。此篇博客将从源码层面上梳理playwright录制原理。当打开playwright vscode插件时,点击录制按钮,会开启一个新浏览器,如下图所示,在新开浏览器页…...
awk脚本监控
awk脚本监控 使用脚本监控内存,cpu和硬盘的根目录,超过80%提示用户,写成函数库的行,每天早上 的8.50分,执行一次脚本 现在脚本中写需要的内容 cpuu () {aa$(top -b -n 1 |awk NR3 {printf "%.F",$2$4})if …...
Python高压电容导电体和水文椭圆微分
🎯要点 🎯二维热传导二阶偏微分方程 | 🎯调和函数和几何图曲率 | 🎯解潮汐波动方程 | 🎯解静止基态旋转球体流体运动函数 | 🎯水文空间插值 | 🎯流体流动模拟求解器 | 🎯随机算法解…...
微信小程序 引入MiniProgram Design失败
这tm MiniProgramDesign 是我用过最垃圾的框架没有之一 我按照官网的指示安装居然能安装不成功,牛! 这里说明我是用js开发的 到以上步骤没有报错什么都没有,然后在引入组件的时候报错 Component is not found in path “./miniprogram _npm/vant/weapp/button/index” (using…...
Java 8 Date and Time API
Java 8引入了新的日期和时间API,位于java.time包下,旨在替代旧的java.util.Date和java.util.Calendar类。新API更为简洁,易于使用,并且与Joda-Time库的一些理念相吻合。以下是Java 8 Date and Time API中几个核心类的简要概述&…...
pyppeteer模块经常使用的功能,相关操作案例
官方仓库地址:https://github.com/miyakogi/pyppeteer 官方文档地址:API Reference — Pyppeteer 0.0.25 documentation Selenium环境的相关配置比较繁琐,此外,有的网站会对selenium和webdriver进行识别和反爬,因此在…...
nginx+keepalived+tomcat集群实验
如遇星河 | nginx+keepalived高可用集群实验 木子87 | Keepalived+Nginx+Tomcat 实现高可用Web集群 环境 192.168.40.204 tomcat-1 192.168.40.138 tomcat-2 安装tomcat [root@bogon local]# vim /etc/profile 添加环境变量 JAVA_HOME=/usr/local/java PATH=$J…...
vue脚手架 axios的二次封装
目录 01 路由懒加载(重要) 02 axios在脚手架中的使用 03.axios的二次封装 04 组件缓存 01 路由懒加载(重要) 一次性导入会出现严重的问题 : 首屏卡顿 因为main.js中引入了router/index.js router/index.js又使用了import语句 静态的引入了每一个组件 导致了首屏卡顿 所以我…...
人机恋爱新趋势:与AI男友谈恋爱的甜蜜与挑战
"我曾经把ChatGPT当成工具,从未追过星,也没有嗑过CP。没想到,到了36岁,我竟然嗑上了AI男友。Open AI,你赢了。你不仅是最好的AI公司,还是乙女游戏公司。" 转行大龄互联网人,走遍20国…...
文生视频开源产品的一些调研(一)
笔者尝试AI视频生成的几个特点: 玄学prompt,每个视频的prompt可能也需要微调很多次,需要找到使用模型的最佳prompt词组合,不恰当的比喻,骑自行车,座位高度等都是人与车彼此熟悉玄学生成,因为需…...
一切前端概念,都是纸老虎
4、listener可以通过 store.getState() 得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。 function listerner() { let newState store.getState(); component.setState(newState); } 对比 Flux 和 Flux 比较一下:Flux 中 Store 是…...
使用自签名 TLS 将 Dremio 连接到 MinIO
Dremio 是一个开源的分布式分析引擎,为数据探索、转换和协作提供简单的自助服务界面。Dremio 的架构建立在 Apache Arrow(一种高性能列式内存格式)之上,并利用 Parquet 文件格式实现高效存储。有关 Dremio 的更多信息,…...
嵌入式系统软件开发环境_2.一般架构
1.Eclipse框架 嵌入式系统软件开发环境是可帮助用户开发嵌入式软件的一组工具的集合,其架构的主要特征离不开“集成”问题,采用什么样的架构框架是决定开发环境优劣主要因素。Eclipse框架是当前嵌入式系统软件开发环境被普遍公认的一种基础环境框架。目…...
单门户上集成多种数据库查询入口
(作者:陈玓玏) 开源项目,欢迎star哦,https://github.com/tencentmusic/cube-studio 在一家公司,我们通常会有多种数据库,每种数据库因为其特性承担不同的角色,比如mysql这种轻量…...
华芯微特SWM34-使用定时器捕获快速解码EV1527编码
在无线应用领域,很多433Mhz和315Mhz的遥控器,红外探测器,门磁报警器,无线门铃等都使用EV1527编码格式来发射数据。发射和接收均有对应的RF芯片完成,而且成本极低(目前市场价3毛钱不到)。接收芯片…...
小程序安卓手机点击uni-data-select 下拉框选择器会出现蓝色阴影
解决方法:在导入的包中找到uni-data-select.vue,接着找到.uni-stat__select样式,把cursor: pointer去掉。 如果出现穿透问题,uni-select__selector的z-index加高,默认是2。...
playwright vscode 插件源码解析
Playwright vscode插件主要功能 Playwright是微软开发的一款主要用于UI自动化测试的工具,在vscode中上安装playwright vscode插件,可以运行,录制UI自动化测试。 playwright vscode插件主要包括两块功能,功能一是在Test Explorer中…...
Mysql: SQL-DDL
一.SQL通用语法 1.SQL可以单行或者多行书写,以分号结尾。 2.SQL语句可以使用空格/缩进来增强语句的可读性。 3.MySQL数据库的SQL语句不区分大小写,关键字建议用大写。 4.注释: 单行注释:注释内容或#注释内容(Mysql特有) 多行注释:/*注释内容*/ 二.SQL分类 1.D…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
