无引擎游戏开发(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;
	
}

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

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

相关文章

Rancher注册已有k8s集群

Rancher安装后注册K8s集群操作 1.Rancher安装 编辑docker—compose文件 version: 3.8services:rancher:image: registry.cn-hangzhou.aliyuncs.com/rancher-images/rancher:v2.8.5container_name: rancherprivileged: truerestart: unless-stoppedports:- "18080:80&qu…

[创业之路-118] :制造业企业的必备管理神器-ERP-主要功能模块说明与系统架构

目录 一、ERP功能的标准化 二、常见的ERP标准化功能 2.1 基础档案 2.2 供应链 2.3 人力资源管理 2.4 资产管理 2.5 生产制造 2.6 财务会计 2.7 管理会计 2.8 CRM客户管理管理 2.9 商业智能分析 三、常见的ERP软件供应商 国内ERP软件供应商 国外ERP软件供应商 四…

西电研究生录取通知书来啦~ (吸吸欧气)

今日桂枝平折得 几年春色并将来&#xff0c;西安电子科技大学 那些深夜点灯的过往 那种日夜备考的迷惘 那个全力奔赴的梦想 都在这封通知书里得到回响&#xff01; —— Xidian University —— —— Xidian University —— XDU 今年的通知书采用紫色为主色 封面压印年…

阿里云API文档有哪些实用功能?如何使用?

阿里云API安全性如何保障&#xff1f;阿里云API怎么实现自动化&#xff1f; 阿里云作为全球领先的云计算服务提供商&#xff0c;提供了广泛的API接口&#xff0c;以满足各类用户的需求。阿里云API文档不仅详尽&#xff0c;而且易于使用&#xff0c;AokSend将详细介绍阿里云API…

渗透测试基础(四) MS08-067 漏洞攻击

1. 漏洞介绍 漏洞描述 Microsoft Windows Server服务RPC请求缓冲区溢出漏洞Windows的Server服务在处理特质RPC请求时存在缓冲区溢出漏洞&#xff0c;远程攻击者可以通过发送恶意的RPC请求触发这个溢出&#xff0c;导致完全入侵用户系统&#xff0c;以SYSTEM权限执行任意指令。…

iptables(3)规则管理

简介 上一篇文章中,我们已经介绍了怎样使用iptables命令查看规则,那么这篇文章我们就来介绍一下,怎样管理规则,即对iptables进行”增、删、改”操作。 注意:在进行iptables实验时,请务必在个人的测试机上进行,不要再有任何业务的机器上进行测试。 在进行测试前,为保障…

海量数据处理利器 Roaring BitMap 原理介绍

作者&#xff1a;来自 vivo 互联网服务器团队- Zheng Rui 本文结合个人理解梳理了BitMap及Roaring BitMap的原理及使用&#xff0c;分别主要介绍了Roaring BitMap的存储方式及三种container类型及Java中Roaring BitMap相关API使用。 一、引言 在进行大数据开发时&#xff0c;…

Raycaster--当物体放在容器中并做了转换,交点坐标不对的问题。

交点坐标问题 问题解决x关键点 总结 问题 子代放在了一个容器里&#xff0c;容器做了旋转、位移。 递归获得了最近的相交子代获取到的交点坐标并不是想要的交点坐标。 经过可视化观察&#xff0c;很像是没转换之前的坐标点。 解决x 在 Three.js 中&#xff0c;当你使用 Rayc…

详细介绍如何解决vcomp140.dll丢失的步骤,分享几种vcomp140.dll修复方法

当这个vcomp140.dll文件丢失时&#xff0c;可能会导致相关程序运行出错甚至无法运行。很多用户可能会遇到vcomp140.dll丢失的问题&#xff0c;但是这并不是不可解决的困难。接下来就和大家分享几种解决vcomp140.dll丢失的方法&#xff0c;给大家详细的关于如何解决vcomp140.dll…

matplotlib 做饼图

饼图可以很好地帮助用户快速了解整体市场数据的占比分配 import matplotlib.pyplot as pltexplode (0,0.1,0,0) labels Frogs,Hogs,Dogs,Logs sizes [15, 30, 45, 10] fig,ax plt.subplots() # colors 设置图形颜色 ;pctdistance&#xff1a;设置百分比标签与圆心的距离&am…

MacBook Air M3的电脑怎么样 新买MacBook Air提示内存不足 苹果电脑内存不够用怎么办

Apple的MacBook Air系列一直是轻薄便携笔记本电脑的代表&#xff0c;最新推出的MacBook Air M3因其出色的性能和优雅的设计而受到广泛关注。然而&#xff0c;许多用户在购买全新的MacBook Air后反应他们遇到了内存不足的提示。 本文将探讨MacBook Air M3的电脑怎么样&#xff0…

【MySQL】事务二

事务二 1.数据库并发的场景2.读-写2.1 3个记录隐藏字段2.2 undo日志2.3 模拟 MVCC2.4 Read View2.5 RR 与 RC的本质区别 3.读-读4.写-写 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我…

示例:应用DependencyPropertyDescriptor监视依赖属性值的改变

一、目的&#xff1a;开发过程中&#xff0c;经常碰到使用别人的控件时有些属性改变没有对应的事件抛出&#xff0c;从而无法做处理。比如TextBlock当修改了IsEnabled属性我们可以用IsEnabledChanged事件去做对应的逻辑处理&#xff0c;那么如果有类似Background属性改变我想找…

太湖远大毛利率下滑:研发费用率远低同行,募投项目合理性疑点重重

《港湾商业观察》黄懿 6月20日&#xff0c;浙江太湖远大新材料股份有限公司&#xff08;以下简称“太湖远大”&#xff0c;873743.NQ&#xff09;即将迎来过会。 2023年11月30日&#xff0c;太湖远大所提交的上市申请材料正式获北交所受理&#xff0c;保荐机构为招商证券&…

DataWorks Copilot:大模型时代数据开发的新范式

导读 DataWorks 是阿里云一站式智能化数据开发与治理平台&#xff0c;支持搭配MaxCompute/Hologres/AnalyticDB/StarRocks/EMR/CDH 等大数据引擎&#xff0c;为企业构建数据仓库、数据湖以及湖仓一体&#xff08;Lakehouse&#xff09;现代数据架构提供数据平台产品解决方案。…

数据结构_二叉树

目录 一、树型结构 二、二叉树 2.1 概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.5 遍历二叉树 2.6 操作二叉树 总结 一、树型结构 树是一种非线性的数据结构&#xff0c;它是由 n(n>0) 个有限结点组成一个具有层次关系的集合&#xff0c;一棵 n 个…

程控漏电流测试电阻箱的应用

程控漏电流测试电阻箱是用于测量和控制电流的设备&#xff0c;广泛应用于电力系统、电子设备、自动化设备等领域。它的主要功能是通过改变电阻值来控制电流的大小&#xff0c;从而实现对设备的保护和控制。 程控漏电流测试电阻箱在电力系统中有着重要的应用&#xff0c;电力系统…

调教NewspaceGPT之GPT4o实战

NewspaceGPT地址&#xff1a;https://newspace.ai0.cn 需求一&#xff1a;我需要一个创意logo 我的问题 我觉得我的描述对一个设计人员来说时精准的&#xff0c;但是不具体的。 需求描述&#xff1a;我需要一个logo。 表现司法公正和司法数字化&#xff0c;人工智能化 。 Ne…

微信聊天记录导出为电脑文件实操教程(附代码)

写在前面 最近&#xff0c;微信中加的群有点多&#xff0c;信息根本看不过来。如果不看&#xff0c;怕遗漏了有价值的信息&#xff1b;如果一条条向上翻阅&#xff0c;实在是太麻烦。 有没有办法一键导出所有聊天记录&#xff1f; 一来翻阅更方便一点&#xff0c;二来还可以…

乐鑫esp32系列睡眠模式下蓝牙连接功耗测试,新支持ESP-C6,启明云端乐鑫代理商

本教程适用于ESP32-S3、ESP32-C3、ESP32-C6&#xff1b; 睡眠模式介绍 ESP32系列常见的休眠方式有三种&#xff0c;分别为Modem-sleep、Light-sleep 和 Deep-sleep。 Modem-sleep模式&#xff1a;CPU 正常工作&#xff0c;可以对时钟进行配置。 进入 Modem-sleep 模式后&…