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

news2024/10/6 6:48:17

为了简单起见,我们将棋盘的二维数组定义为全局变量。除此之外还要定义一个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;
	
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

保护模式下的内存访问(笔记)

;代码清单12-1;文件名&#xff1a;c12_mbr.asm;文件说明&#xff1a;硬盘主引导扇区代码;创建日期&#xff1a;2011-5-16 19:54&#xff1b;修改于2022-02-16 11:15;设置堆栈段和栈指针mov ax, csmov ss, axmov sp, 0x7c00;计算GDT所在的逻辑段地址mov ax, [cs: gdt_base 0x7c…

树(数据结构篇)

数据结构之树 树 本篇讲的树也就是多叉树 普通树(多叉树) 概念&#xff1a; 树就是由根节点(父亲)分出多个分支节点(儿子)&#xff0c;然后分支又分出多个分支&#xff0c;我们将这种结构称为树,树也可以这么定义&#xff1a;一棵树由称作根的节点r以及0个或多个非空的(子…

滴漏智能监测报警摄像机

智能监测摄像机在现代城市安全管理中扮演着关键角色。这些先进设备利用高度精确的图像识别技术&#xff0c;实时监测各类安全隐患&#xff0c;特别是在防疫措施中的应用日益显著。滴漏智能监测报警摄像机作为其中的一种&#xff0c;具备了多重功能&#xff0c;极大地增强了公共…

【Golang - 90天从新手到大师】Day06 - 数组

系列文章合集 Golang - 90天从新手到大师 数组是golang中最常用的一种数据结构,数组就是同一类型数据的有序集合 定义一个数组 格式: var name [n]type n为数组长度,n>0 且无法修改,type为数组的元素类型如: var a [2]int上面的例子定义了一个长度为2,元素类型为int的数组…

SpringBoot的配置文件和YAML文件的语法

1.SpringBoot的有两种格式的全局配置文件&#xff0c;使用任何一个功能都是一样的 注意&#xff1a;SpringBoot的全局配置文件名都是固定的application.xxx ① application.properties&#xff0c; 这个是默认Spring initializr默认自动生成的配置文件&#xff0c;也是我们属…

机器学习参数寻优:方法、实例与分析

机器学习参数寻优:方法、实例与分析 机器学习模型的性能很大程度上依赖于其参数的选择。参数寻优(Hyperparameter Tuning)是提升模型表现的关键步骤之一。本文将详细介绍主流的参数寻优方法,包括网格搜索(Grid Search)、随机搜索(Random Search)、贝叶斯优化(Bayesia…

Java中的do-while循环及其示例

Java中的do-while循环及其示例 在上一个教程中&#xff0c;我们讨论了while循环。在本教程中&#xff0c;我们将讨论java中的do-while循环。do-while循环类似于while循环&#xff0c;但它们之间有区别&#xff1a;在while循环中&#xff0c;条件是在执行循环体之前求值的&am…

1分钟带你部署本地Llama3大模型

介绍 LLaMa 3由Meta于2024年4月18日正式发布&#xff0c;这一版本是对先前LLaMa系列的重大升级。新发布的模型包括8B&#xff08;80亿参数&#xff09;和70B&#xff08;700亿参数&#xff09;两个版本&#xff0c;这两个版本在一系列行业标准基准测试中展示了最先进的性能。 从…

Jenkins 发测试邮件报错 553 Mail from must equal authorized user

Jenkins 发测试邮件报错 553 Mail from must equal authorized user 报错信息报错原因解决办法 报错信息 org.eclipse.angus.mail.smtp.SMTPSenderFailedException: 553 Mail from must equal authorized user at org.eclipse.angus.mail.smtp.SMTPTransport.mailFrom(SMTPTra…

EasyRecovery软件帮你轻松找回丢失的信息!即使是电脑小白也能轻松上手。

&#x1f389;【数据恢复&#xff0c;EasyRecovery帮你轻松找回丢失的信息&#xff01;】&#x1f389; 大家好&#xff5e;&#x1f44b;今天我要来安利一款超级实用的软件——EasyRecovery数据恢复工具&#xff01;作为经常跟各种数据打交道的你们&#xff0c;是不是也有过误…

【Windows Server】设置远程连接数量

打开运行&#xff08;快捷键WinR&#xff09;输入gpedit.msc打开“本地组策略编辑器” 依次点选【计算机配置】——【管理模板】——【Windows组件】——【远程桌面服务】——【远程桌面会话主机】——【连接】 双击“限制连接的数量”选择“已启用”&#xff0c;允许的RD最…

0.4 隔行扫描(Interlaced Scan)简介

0.4 隔行扫描简介 隔行扫描&#xff08;Interlaced Scan&#xff09;是一种将图像显示在扫描式的显示设备上的方法&#xff0c;例如阴极射线管&#xff08;CRT&#xff09;。 隔行扫描设备交替扫描图像的奇场&#xff08;图像的所有奇数行&#xff0c;1、3、5&#xff09;和偶…

SpringMVC框架学习笔记(八):自定义拦截器和异常处理

1 自定义拦截器 1.1 什么是拦截器 1.1.1 说明 &#xff08;1&#xff09;Spring MVC 也可以使用拦截器对请求进行拦截处理&#xff0c;用户可以自定义拦截器来实现特定 的功能. &#xff08;2&#xff09;自定义的拦截器必须实现 HandlerInterceptor 接口 1.1.2 自定义拦截…

[创业之路-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软件供应商 四…

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;设置初始化…

做金蝶BI数据分析?后悔没早遇到这套方案

金蝶ERP是企业信息化管理的核心平台&#xff0c;在企业管理上有着十分重要的地位&#xff1b;而BI数据可视化工具同样在企业管理中扮演着至关重要的角色&#xff0c;通过将复杂数据转化为直观、易懂的图形和报表&#xff0c;推动企业高效、精准科学化决策。金蝶、BI两者结合&am…

使用二进制安装安装docker

在一些情况下无法使用yum安装docker下面写了一个使用二进制安装docker的文档 官网下载地址https://download.docker.com/linux/static/stable/x86_64/ 可以按需求下载 wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.10.tgz 下载包 tar xf dcker…

JAVA小知识27:异常与异常处理全解

一、异常 异常就是代表程序出现了问题&#xff0c;像我们之前学数组的时候碰到的数组越界异常&#xff0c;以及空指针异常等等都属于开发中经常能碰到的异常。 我们学习异常不是为了在以后中避免异常&#xff0c;而是学习碰到了异常以后该如何的处理。 1.1、异常的分类 Java…

InPixio Photo Cutter v10 解锁版安装教程 (懒人抠图工具)

前言 InPixio Photo Cutter是一款懒人抠图工具&#xff0c;采用了增强的算法切割技术&#xff0c;可以在不影响图像质量的情况下&#xff0c;允许用户从照片中删除任何物体或人物&#xff0c;并且保持其完整的质量。你只需点击几下鼠标&#xff0c;便可从照片中剪下任何细节、…

Linux驱动开发(二)--字符设备驱动开发提升 LED驱动开发实验

1、地址映射 在编写驱动之前&#xff0c;需要知道MMU&#xff0c;也就是内存管理单元&#xff0c;在老版本的 Linux 中要求处理器必须有 MMU&#xff0c;但是现在Linux 内核已经支持无 MMU 的处理器了。 MMU的功能如下&#xff1a; 完成虚拟空间到物理空间的映射 内存保护&…