当前位置: 首页 > news >正文

无引擎游戏开发(3):数据结构设计|功能函数完善

为了简单起见,我们将棋盘的二维数组定义为全局变量。除此之外还要定义一个char类型的全局变量来识别当前的落子类型,我们将其初始化为‘O’。

char Board_data[3][3] = {{'-', '-', '-'},{'-', '-', '-'},{'-', '-', '-'},
};char Cur_piece = 'O';

现在回到“读取操作”部分,通过msg的x与y字段来获取鼠标点击的位置,但是现在需要将鼠标点击的位置映射到数组的索引中,一开始将数组初始化为600*600,九等分后每个格子都是200*200,单看水平方向的二维数组索引便是鼠标点击位置除以200的整数部分,竖直方向一样。

接下来便是尝试落子,也即尝试修改对应数组索引位置的值,记住二维数组是先行后列,也即先Y后X确定落子位置,并在落子后切换棋子类型。

然后是完善功能函数,首先是CheckWin函数,先前已经讨论了所有8种情况:

bool CheckWin(char c) {if (Board_data[0][0] == c && Board_data[0][1] == c && Board_data[0][2] == c) return true;if (Board_data[1][0] == c && Board_data[1][1] == c && Board_data[1][2] == c) return true;if (Board_data[2][0] == c && Board_data[2][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][0] == c && Board_data[2][0] == c) return true;if (Board_data[0][1] == c && Board_data[1][1] == c && Board_data[2][1] == c) return true;if (Board_data[0][2] == c && Board_data[1][2] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][2] == c && Board_data[1][1] == c && Board_data[2][0] == c) return true;return false;
}

任何条件被满足都会被短路,然后返回true并不再向下执行。所有条件检测失败后函数一直向下执行到返回false。

CheckDraw函数与CheckWin函数思想相似,遍历整个棋盘如果还有空则返回false,否则返回true:

bool CheckDraw() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {if (Board_data[i][j] == '-') return false;}}return true;
}

接下来便是绘制棋盘、棋子、提示信息:

棋盘被四条线切割为九份,使用line函数切割:

void DrawBoard() {line(0, 200, 600, 200);line(0, 400, 600, 400);line(200, 0, 200, 600);line(400, 0, 400, 600);
}

然后是DrawPiece函数,先遍历整个棋盘,然后用switch函数判断所有情况,i对应y,j对应x:

void DrawPiece() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {switch (Board_data[i][j]) {case 'O':circle(200 * j + 100, 200 * i + 100, 100);break;case 'X':line(200 * j, 200 * i, 200 * (j + 1), 200 * (i + 1));line(200 * (j + 1), 200 * i, 200 * j, 200 * (i + 1));break;case '-':break;}}}
}

最后是绘制提示信息DrawTipText:

void DrawTipText() {static TCHAR str[64];_stprintf_s(str, _T("当前棋子类型: %c"), Cur_piece);settextcolor(RGB(225, 175, 45));outtextxy(0, 0, str);
}

这里使用了_stprintf_s这个字符串格式化函数,并且定义了TCHAR的字符数组作为格式化的缓冲区,这与printf和sprintf很像,只不过是为了在更通用的编码环境下使用。

话接上文,EasyX可以使用outtextxy函数在窗口的指定坐标处绘制文本字符串,不过在绘制前我们使用了settextcolor函数将文本绘制颜色改为橙黄色使其醒目。settextcolor接受一个COLORREF类型的参数,我们可以通过RGB宏传入对应颜色分量组合出COLORREF类型的值。

至此,我们完成了所有代码。

测试:

最后一个棋子在绘制上去前就判断了胜负,有点影响美感,所以应该在绘制后再判断胜负。所以绘制函数应该在判断胜负之前:

改进后效果显著:

完善:发现井字棋程序占用CPU很大

这是因为计算机在执行while循环时非常快,主循环在顷刻间执行成千上万次,占用了大量的CPU时间片,对于机器是一种性能浪费。所以我们可以使用sleep函数来让程序执行完一次循环后休眠一小段时间从而减少计算资源的浪费。

那么该休眠多久呢?

随着游戏体量的增大,程序每次执行主循环所执行的计算任务可能是不同的,以及涉及到操作系统CPU计算资源的分配,这就导致每次执行主循环所消耗的实际时间可能是不一样的,所以我们要根据每一帧执行的实际耗时动态地计算在这之后要休眠多长的时间,所以引入函数GetTickCount,可以使用它获取程序自运行开始以来到现在的毫秒数:

所以我们在循环开头和结尾各调用一次,然后通过相减得出这次循环实际消耗的毫秒数。

/*#include<graphics.h>char Board_data[3][3] = {{'-', '-', '-'},{'-', '-', '-'},{'-', '-', '-'},
};char Cur_piece = 'O';bool CheckWin(char c) {if (Board_data[0][0] == c && Board_data[0][1] == c && Board_data[0][2] == c) return true;if (Board_data[1][0] == c && Board_data[1][1] == c && Board_data[1][2] == c) return true;if (Board_data[2][0] == c && Board_data[2][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][0] == c && Board_data[2][0] == c) return true;if (Board_data[0][1] == c && Board_data[1][1] == c && Board_data[2][1] == c) return true;if (Board_data[0][2] == c && Board_data[1][2] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][2] == c && Board_data[1][1] == c && Board_data[2][0] == c) return true;return false;
}bool CheckDraw() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {if (Board_data[i][j] == '-') return false;}}return true;
}void DrawBoard() {line(0, 200, 600, 200);line(0, 400, 600, 400);line(200, 0, 200, 600);line(400, 0, 400, 600);
}void DrawPiece() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {switch (Board_data[i][j]) {case 'O':circle(200 * j + 100, 200 * i + 100, 100);break;case 'X':line(200 * j, 200 * i, 200 * (j + 1), 200 * (i + 1));line(200 * (j + 1), 200 * i, 200 * j, 200 * (i + 1));break;}}}
}void DrawTipText() {static TCHAR str[64];_stprintf_s(str, _T("当前棋子类型: %c"), Cur_piece);settextcolor(RGB(225, 175, 45));outtextxy(0, 0, str);
}
int main() {initgraph(600, 600);ExMessage msg;BeginBatchDraw();bool running = true;*/while (running) {DWORD start_time = GetTickCount();/*while (peekmessage(&msg)) {if (msg.message == WM_LBUTTONDOWN) {int x = msg.x;int y = msg.y;int index_x = x / 200;int index_y = y / 200;if (Board_data[index_y][index_x] == '-') {Board_data[index_y][index_x] = Cur_piece;if (Cur_piece == 'O') Cur_piece = 'X';else Cur_piece = 'O';}}}cleardevice();DrawBoard();	DrawPiece();DrawTipText();FlushBatchDraw();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;}*/DWORD end_time = GetTickCount();DWORD delta_time = end_time - start_time;/*}EndBatchDraw();return 0;}*/

如果要确保画面以最高60帧的速度刷新,那么每次循环的总时间应该是1000 / 60,如果实际消耗的毫秒数小于1000 / 60,便可以通过运用sleep()延时剩下的时间,如果超过了,就直接进入下一次循环:

可以发现CPU占用率显著下降。

完整代码如下:

#include<graphics.h>char Board_data[3][3] = {{'-', '-', '-'},{'-', '-', '-'},{'-', '-', '-'},
};char Cur_piece = 'O';bool CheckWin(char c) {if (Board_data[0][0] == c && Board_data[0][1] == c && Board_data[0][2] == c) return true;if (Board_data[1][0] == c && Board_data[1][1] == c && Board_data[1][2] == c) return true;if (Board_data[2][0] == c && Board_data[2][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][0] == c && Board_data[2][0] == c) return true;if (Board_data[0][1] == c && Board_data[1][1] == c && Board_data[2][1] == c) return true;if (Board_data[0][2] == c && Board_data[1][2] == c && Board_data[2][2] == c) return true;if (Board_data[0][0] == c && Board_data[1][1] == c && Board_data[2][2] == c) return true;if (Board_data[0][2] == c && Board_data[1][1] == c && Board_data[2][0] == c) return true;return false;
}bool CheckDraw() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {if (Board_data[i][j] == '-') return false;}}return true;
}void DrawBoard() {line(0, 200, 600, 200);line(0, 400, 600, 400);line(200, 0, 200, 600);line(400, 0, 400, 600);
}void DrawPiece() {for (size_t i = 0; i < 3; i++) {for (size_t j = 0; j < 3; j++) {switch (Board_data[i][j]) {case 'O':circle(200 * j + 100, 200 * i + 100, 100);break;case 'X':line(200 * j, 200 * i, 200 * (j + 1), 200 * (i + 1));line(200 * (j + 1), 200 * i, 200 * j, 200 * (i + 1));break;}}}
}void DrawTipText() {static TCHAR str[64];_stprintf_s(str, _T("当前棋子类型: %c"), Cur_piece);settextcolor(RGB(225, 175, 45));outtextxy(0, 0, str);
}
int main(){initgraph(600, 600);ExMessage msg;BeginBatchDraw();bool running = true;while (running) {DWORD start_time = GetTickCount();while (peekmessage(&msg)) {if (msg.message == WM_LBUTTONDOWN) {int x = msg.x;int y = msg.y;int index_x = x / 200;int index_y = y / 200;if (Board_data[index_y][index_x] == '-') {Board_data[index_y][index_x] = Cur_piece;if (Cur_piece == 'O') Cur_piece = 'X';else Cur_piece = 'O';}}}cleardevice();DrawBoard();DrawPiece();DrawTipText();FlushBatchDraw();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;}DWORD end_time = GetTickCount();DWORD delta_time = end_time - start_time;if (delta_time < (1000 / 60)) {Sleep((1000 / 60) - delta_time);}}EndBatchDraw();return 0;}

相关文章:

无引擎游戏开发(3):数据结构设计|功能函数完善

为了简单起见&#xff0c;我们将棋盘的二维数组定义为全局变量。除此之外还要定义一个char类型的全局变量来识别当前的落子类型&#xff0c;我们将其初始化为‘O’。 char Board_data[3][3] {{-, -, -},{-, -, -},{-, -, -}, };char Cur_piece O; 现在回到“读取操作”部分…...

Laravel 高级:了解$loop

Blade 提供 foreach、while、for 和 forelse 等指令来与 PHP 循环配合使用。 您知道吗... 这些指令中有一个方便的 $loop 变量&#xff0c;它指示当前循环迭代&#xff1f;在本文中&#xff0c;我们将探索 $loop 和 loop 指令。&#x1f60e; 使用$loop比foreach更深入 该for…...

深入理解指针(1)

目录&#xff1a; 1. 内存和地址 2. 指针变量和地址 3. 指针变量类型的意义 4. const修饰指针 5. 指针运算 6. 野指针 7. assert断⾔ 8. 指针的使⽤和传址调用 1. 内存和地址 1.1 内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&a…...

在无线网中 2.4G、5G、WiFi6、WiFi7 都是什么意思?

有同学问我在无线网中 2.4G/5G/WiFi6/WiFi7 都是什么意思&#xff1f;其实这是两个概念&#xff0c; 2.4G/5G 是频段&#xff0c;WiFi6/WiFi7 是无线协议的版本&#xff0c;千万别把版本和频段搞混了。 WiFi 协议是一系列基于 IEEE 802.11 标准的无线局域网技术协议&#xff0…...

milvus元数据解析工具milvusmetagui介绍使用

简介 milvusmetagui是一款用来对milvus的元数据进行解析的工具&#xff0c;milvus的元数据存储在etcd上&#xff0c;而且经过了序列化&#xff0c;通过etcd-manager这样的工具来查看是一堆二进制乱码&#xff0c;因此开发了这个工具对value进行反序列化解析。 在这里为了方便交…...

LabVIEW电磁超声热态金属在线缺陷检测系统

LabVIEW软件开发的电磁超声热态金属在线缺陷检测系统针对极端高温环境下的金属材料&#xff0c;进行实时、无损的缺陷检测&#xff0c;具有高精度和高可靠性&#xff0c;能够显著提高材料质量控制的效率和准确性。 项目背景 随着工业技术的发展&#xff0c;高温环境下的金属材…...

leecode代码模板

二分算法&#xff1a; 34. 在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。你必须设计…...

可靠性测试及模型计算

双85高温高湿测试 场景描述&#xff1a; 85℃温度 85%湿度 老化测试 目的&#xff1a; 衡量产品使用寿命 反向推导&#xff1a; 如何根据产品寿命及工况计算双85测试时间 模型介绍 本质是化学反应速率&#xff08;老化的本质是&#xff09;随温度的变化 温湿度循环测…...

【Tools】 深入了解Burp Suite:Web应用抓包利器

唱 情 歌 齐齐来一遍 无时无刻都记住掌声 响遍天 来唱 情 歌 由从头再一遍 如情浓有点泪流难避免 音阶起跌拍子改变 每首歌 是每张脸 喜欢我 别遮脸 任由途人发现 &#x1f3b5; 刘德华《十七岁》 在Web应用和移动应用的开发与测试过程中&#xff0c;抓包…...

技术先进、应用广泛、社区活跃的[项目名称]

项目介绍 ----  [项目介绍内容]&#xff0c;此项目在开源社区中备受欢迎&#xff0c;其创新性技术和广泛应用领域吸引了大量开发者关注。  代码解释 ----  [代码解释内容]&#xff0c;该项目采用[编程语言]&#xff0c;通过[技术栈]实现&#xff0c;具有[功能特点]。  …...

Vue中data的属性可以和methods中方法同名吗,为什么?

在Vue中&#xff0c;data的属性不可以和methods中的方法同名&#xff0c;原因如下&#xff1a; 命名规范&#xff1a;从编程规范的角度来看&#xff0c;同名属性或方法可能会导致混淆和难以维护的代码。data通常用于存储组件的状态或数据&#xff0c;而methods则包含组件的行为…...

Esxi上创建windows 11虚拟机

下载windows 11系统镜像 Download Windows 11 (microsoft.com) 虚拟机配置 正常安装部署&#xff0c;需要注意以下几点&#xff1a; 1.cpu开启虚拟化&#xff0c;启用CPU热添加 2.内存开启热插拔 3.磁盘类型最好选择精简置备&#xff08;磁盘只使用最初所需要的数据存储空间…...

法大大亮相国家级期刊,助力数字政务有实“例”!

近日&#xff0c;在最新发布的国家级学术期刊《市场监督管理》中&#xff0c;法大大作为国内领先的电子签厂商亮相&#xff0c;这也是电子签行业的“第一次”。 截自《市场监督管理》2024年第12期 《市场监督管理》杂志于1953年创刊&#xff0c;是中国工商出版社主办的一本学术…...

【管理咨询宝藏131】麦肯锡波士顿贝恩经典战略咨询报告套装

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏131】麦肯锡波士顿贝恩经典战略咨询报告套装 【格式】PDF版本 【关键词】麦肯锡、波士顿咨询、贝恩咨询、战略咨询、战略落地、战略洞察 【强烈…...

Python | Leetcode Python题解之第160题相交链表

题目&#xff1a; 题解&#xff1a; class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:A, B headA, headBwhile A ! B:A A.next if A else headBB B.next if B else headAreturn A...

SSRF学习,刷题

[HNCTF 2022 WEEK2]ez_ssrf 给了一个Apache2的界面&#xff0c;翻译一下 就是一个默认的界面,目录扫描 可以看到flag.php,肯定是不能直接访问得到的&#xff0c;还有index.php&#xff0c;访问这个 可以看到三个参数data,host,port 还有fsockopen() 函数是 PHP 中用于打开一个…...

K-Means 算法详解

K-Means 是一种常用的无监督学习算法&#xff0c;广泛应用于数据聚类分析。本文将详细讲解 K-Means 算法的原理、步骤、公式以及 Python 实现&#xff0c;帮助你深入理解这一经典算法。 什么是 K-Means 算法&#xff1f; K-Means 算法是一种基于原型的聚类算法&#xff0c;其…...

【DIY飞控板PX4移植】BARO模块BMP388气压计的PCB硬件设计和PX4驱动配置

BARO模块BMP388气压计的PCB硬件设计和PX4驱动配置 BMP388简介硬件设计封装原理图PCB设计引脚选择问题 PX4驱动配置飞控板的配置文件夹结构default.px4board文件nuttx-config/nsh/defconfig文件nuttx-config/include/board.h文件src/board_config.h文件src/i2c.cpp文件init/rc.b…...

Flutter框架高阶——Window应用程序设置窗体窗口背景完全透明

文章目录 1.修改 main.cpp1&#xff09;C 与 Win32 API2&#xff09;EnableTransparency()3&#xff09;中文注释 2.编写 Flutter 代码1&#xff09;bitsdojo_window2&#xff09;window_manager3&#xff09;区别对比4&#xff09;同时使用&#xff08;1&#xff09;设置初始化…...

HJ39判断两个IP是否属于同一子网

提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 HJ39判断两个IP是否属于同一子网 一、 代码&#xff1a; 第一版代码没有对掩码网络号进行处理。一开始对非法字段的理解就是value大于255。然后执行示例&#xff0c; 254.255.0.0 85.122.52.249 10.57.…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...