C++入门——实现十字消除游戏_c++十字消除-程序员宅基地

技术标签: C++  c++  easyx  

参考

  1. 《C和C++游戏趣味编程》 童晶

十字消除游戏

用户点击空白方块,沿其上、下、左、右方向寻找一个彩色方块,如果有两个或两个以上颜色一致,就将其消除。在进度条时间结束前消除足够的方块,可以进入下一关

红色方块的表示与绘制

定义Block结构体,利用Block类型的二维数组存储画面中所有小方块的信息。在startup()中将所有方块设置为红色填充、白色线条,在show()中绘制出所有方块:

在这里插入图片描述

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define BlockSize 40           // 小方块的边长
#define RowNum 13              // 画面一共RowNum行
#define ColNum 21              // 画面一共ColNum列

struct Block
{
    
	int x, y;                  // 在画面中的x, y坐标
	int i, j;                  // 在二维数组中的i, j下标
};

// 全局变量
Block blocks[RowNum][ColNum];

void startup()
{
    
	int i, j;

	int width = BlockSize * ColNum;
	int height = BlockSize * RowNum;
	initgraph(width, height);
	setbkcolor(RGB(220, 220, 220));
	setfillcolor(RGB(255, 0, 0));
	setlinestyle(PS_SOLID, 2);  // 设置线型、线宽
	cleardevice();              // 以背景颜色清屏
	BeginBatchDraw();

	for (i = 0; i < RowNum; i++)
	{
    
		for (j = 0; j < ColNum; j++)
		{
    
			blocks[i][j].x = j * BlockSize;
			blocks[i][j].y = i * BlockSize;
			blocks[i][j].i = i;
			blocks[i][j].j = j;
		}
	}
}

void show()
{
    
	cleardevice();
	setlinecolor(RGB(255, 255, 255));
	int i, j;
	for (i = 0; i < RowNum; i++)
	{
    
		for (j = 0; j < ColNum; j++)
		{
    
			fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
		}
	}
	FlushBatchDraw();
}

int main()
{
    
	startup();
	while (1)
	{
    
		show();
	}
	return 0;
}

随机颜色方块的实现

定义colors数组记录所有可能的颜色,其中第0种为灰白色,其他为彩色:

COLORREF colors[ColorTypeNum + 1];

在startup()中对colors数组进行初始化:

colors[0] = RGB(220, 220, 220);
for (i = 1; i < ColorTypeNum + 1; i++)
{
    
	colors[i] = HSVtoRGB((i - 1) * 40, 0.6, 0.8);
}

在结构体Block中添加成员变量colorId:

struct Block
{
    
	int x, y;                  // 在画面中的x, y坐标
	int i, j;                  // 在二维数组中的i, j下标
	int colorId;               // 对应颜色下标
};

在startup()中对blocks初始化,设置其颜色序号为[0, ColorTypeNum]的随机数:

int t = rand() % (ColorTypeNum + 1);
blocks[i][j].colorId = t;

为了让空白比例更高,可以调整一下:

int t = rand() % (int(ColorTypeNum * 1.5));
if (t < ColorTypeNum + 1)
{
    
	blocks[i][j].colorId = t;
}
else
{
    
	blocks[i][j].colorId = 0;
}

在show()中绘制对应颜色的小方块:

setfillcolor(colors[blocks[i][j].colorId);
fillrectangle(blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);

在这里插入图片描述

鼠标点击与十字消除

添加updateWithInput()函数,根据鼠标点击位置(e.x, e.y)计算点中的小方块在二维数组中的行列号(clicked_i, clicked_j)

void updateWithInput()
{
    
	ExMessage e;
	if (peekmessage(&e))
	{
    
		if (e.message == WM_LBUTTONDOWN)
		{
    
			int clicked_i = int(e.y) / BlockSize;
			int clicked_j = int(e.x) / BlockSize;
		}
	}
}

先判断被鼠标点击的方块是否为空白方块,如果不是则直接返回:

if (blocks[clicked_i][clicked_j].colorId != 0)
{
    
	return;
}

定义数组fourBlocks[4]存储上下左右4个方向找到的第一个不是空白的方块:

Block fourBlocks[4] = {
     blocks[clicked_i][clicked_j] };  // 初始化为点击的方块

首先向上寻找,找到第一个不是空白的方块,存储到fourBlocks[4]中:

int search;
for (search = 0; clicked_i - search >= 0; search++)      // 向上搜索
{
    
	if (blocks[clicked_i - search][clicked_j].colorId != 0)
	{
    
		fourBlocks[0] = blocks[clicked_i - search][clicked_j];
		break;
	}
}

同理,向下、左、右分别找到第一个不是空白的方块:

for (search = 0; clicked_i + search < RowNum; search++)  // 向下搜索
{
    
	if (blocks[clicked_i + search][clicked_j].colorId != 0)
	{
    
		fourBlocks[1] = blocks[clicked_i + search][clicked_j];
		break;
	}
}

for (search = 0; clicked_j - search >= 0; search++)      // 向左搜索 
{
    
	if (blocks[clicked_i][clicked_j - search].colorId != 0)
	{
    
		fourBlocks[2] = blocks[clicked_i][clicked_j - search];
		break;
	}
}

for (search = 0; clicked_j + search < ColNum; search++)
{
    
	if (blocks[clicked_i][clicked_j + search].colorId != 0)
	{
    
		fourBlocks[3] = blocks[clicked_i][clicked_j + search];
		break;
	}
}

进一步,遍历fourBlocks,统计对应颜色方块的个数。如果某种颜色的方块个数colorStatistics[i] >= 2,则将对应方块的颜色序号设为0:

int colorStatistics[ColorTypeNum + 1] = {
     0 };
for (i = 1; i <= ColorTypeNum; i++)
{
    
	for (j = 0; j < 4; j++)
	{
    
		if (fourBlocks[j].colorId == i)
		{
    
			colorStatistics[i]++;
		}
	}
	if (colorStatistics[i] >= 2)
	{
    
		for (j = 0; j < 4; j++)
		{
    
			if (fourBlocks[j].colorId == i)
			{
    
				blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;
			}
		}
	}
}

方块提示框的绘制

首先定义绘制提示框的函数:

void drawBlockHint(int i, int j, COLORREF color, int isfill)
{
    
	setlinecolor(color);
	setfillcolor(color);
	if (isfill == 1)           // 画填充方块
	{
    
		fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
	}
	if (isfill == 0)           // 画非填充的方块线框
	{
    
		rectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
	}
}

在updateWithInput()中,如果点击的是空白方块,则执行:

show();                                                  // 先绘制其他方块
drawBlockHint(clicked_i, clicked_j, RGB(100, 100, 100), 1); // 对被点击的空白方块,绘制填充灰色的空白方块

如果十字区域有要消除的彩色方块,则执行:

if (fourBlocks[j].colorId == i)
{
    
	drawBlockHint(fourBlocks[j].i, fourBlocks[j].j, RGB(0, 0, 0), 0);  // 要消除的方块绘制提示框
	blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;
}

倒计时与进度条

添加全局变量:

float maxTime;                 // 游戏允许的总时长
float remainTime;              // 游戏剩余时长startup()中,加大窗口高度用于显示倒计时进度条:

int height = BlockSize * (RowNum + 2);

在updateWithoutInput()中,定义静态变量start,每次运行时获得当前时刻now,计算程序已运行的时间duration,求出游戏剩余时间:

void updateWithoutInput()
{
    
	static clock_t start = clock();                               // 记录第一次运行时刻
	clock_t now = clock();                                        // 获取当前时刻
	double duration = double(now - start) / CLOCKS_PER_SEC;       // 程序运行时间
	remainTime = maxTime - duration;
}

在show()中绘制倒计时进度条:

setlinecolor(RGB(255, 0, 0));                                 // 设置进度条颜色
setfillcolor(RGB(255, 0, 0));
fillrectangle(0, BlockSize * (RowNum + 0.2), remainTime * BlockSize * ColNum / maxTime, BlockSize * (RowNum + 0.8)); // 绘制进度条

得分计算与胜负判断

定义score记录玩家消去的方块个数,noZeroBlockNum记录游戏开始时彩色砖块的总数,设定当score >= 0.9 * noZeroBlockNum时游戏胜利:

int score;
int noZeroBlockNum;			   // 彩色方块个数

在startup()中统计彩色砖块的总数:

if (blocks[i][j].colorId != 0)
{
    
	noZeroBlockNum++;
}

在updateWithInput()中,更新十字区域消除的方块个数:

score += colorStatistics[i];

在show()中,显示当前得分score:

TCHAR s[80];                                                  // 定义字符数组
setbkmode(TRANSPARENT);
settextcolor(RGB(0, 0, 0));
settextstyle(25, 0, _T("宋体"));
swprintf_s(s, _T("当前%d分,达到%d分游戏胜利"), level, score, int(0.9 * noZeroBlockNum));
outtextxy(BlockSize * (ColNum / 4.5), BlockSize * (RowNum + 1.1), s);

多关卡与增加游戏难度

在startup()中,随着level的增加,当前关的游戏总时长越来越短:

maxTime = 200 - level * 10;

在show()中,显示当前为第几关、已得分数、得到多少分可以进入下一关:

swprintf_s(s, _T("当前第%d关,已得%d分,达到%d分进入下一关"), level, score, int(0.9 * noZeroBlockNum));

在updateWithoutInput()中,如果得分达到要求,则将level加1,重新计时;否则重新开始:

if (score >= int(0.9 * noZeroBlockNum))
{
    
	level++;                                                  // 进入下一关
	start = clock();                                          // 重新开始计时
	startup();
}
else if (remainTime <= 0)
{
    
	start = clock();
	startup();
}

完整代码

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <time.h>
#define BlockSize 40           // 小方块的边长
#define RowNum 13              // 画面一共RowNum行
#define ColNum 21              // 画面一共ColNum列
#define ColorTypeNum 9         // 方块彩色颜色的个数

struct Block
{
    
	int x, y;                  // 在画面中的x, y坐标
	int i, j;                  // 在二维数组中的i, j下标
	int colorId;               // 对应颜色下标
};

// 全局变量
Block blocks[RowNum][ColNum];
COLORREF colors[ColorTypeNum + 1];
float maxTime;                 // 游戏允许的总时长
float remainTime;              // 游戏剩余时长
float punishTime;              // 点错扣除的时间
int score;
int noZeroBlockNum;			   // 彩色方块个数
int level = 1;                 // 当前关卡序号


void drawBlockHint(int i, int j, COLORREF color, int isfill)
{
    
	setlinecolor(color);
	setfillcolor(color);
	if (isfill == 1)           // 画填充方块
	{
    
		fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
	}
	if (isfill == 0)           // 画非填充的方块线框
	{
    
		rectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
	}
}

void startup()
{
    
	int i, j;

	int width = BlockSize * ColNum;
	int height = BlockSize * (RowNum + 2);
	initgraph(width, height);
	setbkcolor(RGB(220, 220, 220));
	setfillcolor(RGB(255, 0, 0));
	setlinestyle(PS_SOLID, 2);  // 设置线型、线宽
	cleardevice();              // 以背景颜色清屏
	BeginBatchDraw();
	srand(time(0));
	maxTime = 300 - level * 10;
	remainTime = maxTime;
	punishTime = 0;

	colors[0] = RGB(220, 220, 220);
	for (i = 1; i < ColorTypeNum + 1; i++)
	{
    
		colors[i] = HSVtoRGB((i - 1) * 40, 0.6, 0.8);
	}

	noZeroBlockNum = 0;

	for (i = 0; i < RowNum; i++)
	{
    
		for (j = 0; j < ColNum; j++)
		{
    
			int t = rand() % (int(ColorTypeNum * 1.5));
			if (t < ColorTypeNum + 1)
			{
    
				blocks[i][j].colorId = t;
			}
			else
			{
    
				blocks[i][j].colorId = 0;
			}
			blocks[i][j].x = j * BlockSize;
			blocks[i][j].y = i * BlockSize;
			blocks[i][j].i = i;
			blocks[i][j].j = j;
			if (blocks[i][j].colorId != 0)
			{
    
				noZeroBlockNum++;
			}
		}
	}
	score = 0;
}

void show()
{
    
	cleardevice();
	setlinecolor(RGB(255, 255, 255));

	int i, j;
	for (i = 0; i < RowNum; i++)
	{
    
		for (j = 0; j < ColNum; j++)
		{
    
			setfillcolor(colors[blocks[i][j].colorId]);
			fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);
		}
	}
	setlinecolor(RGB(255, 0, 0));                                 // 设置进度条颜色
	setfillcolor(RGB(255, 0, 0));
	fillrectangle(0, BlockSize * (RowNum + 0.2), remainTime * BlockSize * ColNum / maxTime, BlockSize * (RowNum + 0.8)); // 绘制进度条

	TCHAR s[80];                                                  // 定义字符数组
	setbkmode(TRANSPARENT);
	settextcolor(RGB(0, 0, 0));
	settextstyle(25, 0, _T("宋体"));
	swprintf_s(s, _T("当前第%d关,已得%d分,达到%d分进入下一关"), level, score, int(0.9 * noZeroBlockNum));
	outtextxy(BlockSize * (ColNum / 4.5), BlockSize * (RowNum + 1.1), s);
	FlushBatchDraw();
}

void updateWithoutInput()
{
    
	static clock_t start = clock();                               // 记录第一次运行时刻
	clock_t now = clock();                                        // 获取当前时刻
	double duration = double(now - start) / CLOCKS_PER_SEC;       // 程序运行时间
	remainTime = maxTime - duration - punishTime;

	if (score >= int(0.9 * noZeroBlockNum))
	{
    
		level++;                                                  // 进入下一关
		start = clock();                                          // 重新开始计时
		startup();
	}
	else if (remainTime <= 0)
	{
    
		start = clock();
		startup();
	}
}

void updateWithInput()
{
    
	if (remainTime <= 0)
	{
    
		return;
	}
	int i, j;
	ExMessage e;
	if (peekmessage(&e))
	{
    
		if (e.message == WM_LBUTTONDOWN)
		{
    
			int clicked_i = int(e.y) / BlockSize;
			int clicked_j = int(e.x) / BlockSize;

			if (blocks[clicked_i][clicked_j].colorId != 0)
			{
    
				return;
			}

			show();                                                  // 先绘制其他方块
			drawBlockHint(clicked_i, clicked_j, RGB(100, 100, 100), 1); // 对被点击的空白方块,绘制填充灰色的空白方块

			Block fourBlocks[4] = {
     blocks[clicked_i][clicked_j] };  // 初始化为点击的方块

			int search;
			for (search = 0; clicked_i - search >= 0; search++)      // 向上搜索
			{
    
				if (blocks[clicked_i - search][clicked_j].colorId != 0)
				{
    
					fourBlocks[0] = blocks[clicked_i - search][clicked_j];
					break;
				}
			}

			for (search = 0; clicked_i + search < RowNum; search++)  // 向下搜索
			{
    
				if (blocks[clicked_i + search][clicked_j].colorId != 0)
				{
    
					fourBlocks[1] = blocks[clicked_i + search][clicked_j];
					break;
				}
			}

			for (search = 0; clicked_j - search >= 0; search++)      // 向左搜索 
			{
    
				if (blocks[clicked_i][clicked_j - search].colorId != 0)
				{
    
					fourBlocks[2] = blocks[clicked_i][clicked_j - search];
					break;
				}
			}

			for (search = 0; clicked_j + search < ColNum; search++)  // 向右搜索
			{
    
				if (blocks[clicked_i][clicked_j + search].colorId != 0)
				{
    
					fourBlocks[3] = blocks[clicked_i][clicked_j + search];
					break;
				}
			}

			int colorStatistics[ColorTypeNum + 1] = {
     0 };
			int isBadClick = 1;
			for (i = 1; i <= ColorTypeNum; i++)
			{
    
				for (j = 0; j < 4; j++)
				{
    
					if (fourBlocks[j].colorId == i)
					{
    
						colorStatistics[i]++;
					}
				}
				if (colorStatistics[i] >= 2)
				{
    
					isBadClick = 0;
					for (j = 0; j < 4; j++)
					{
    
						if (fourBlocks[j].colorId == i)
						{
    
							drawBlockHint(fourBlocks[j].i, fourBlocks[j].j, RGB(0, 0, 0), 0);
							blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;
						}
					}
					score += colorStatistics[i];
				}
			}

			if (isBadClick == 1)     // 如果错误点击
			{
    
				punishTime += 10;
			}

			FlushBatchDraw();
			Sleep(300);
		}
	}
}

int main()
{
    
	startup();
	while (1)
	{
    
		show();
		updateWithoutInput();
		updateWithInput();
	}
	return 0;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/beilizhang/article/details/120729085

智能推荐

中小企业业财融合策略(2):财务赋能,如何支持业务经营?_业务经营支持与赋能-程序员宅基地

文章浏览阅读231次。前文我们谈到了,企业业务流程的规范性,决定了企业经营相关信息、数据获取的准确性和及时性,很多时候,财务人员对流程的规范性、完整性感知更深刻,因此企业的业务流程建设,应该要让财务参与进来,一方面:财务可以从信息流、资金流、物流协同,及企业成本和收入的角度对流程进行审视。财务由被动转为主动,并不能仅仅是财务的主观愿望,而应该是公司管理的主动安排,例如:规定每个期间财务部要对公司的运营部门做《评价报告》,该报告直接交给总经理,报告内容,侧重在过程指标的展示说明,这也是体现财务分析能力的关键!_业务经营支持与赋能

安装Ubuntu 出现ubi partman crashed,ubi-partman failed with exit code 10-程序员宅基地

文章浏览阅读6.5k次。出现这个问题好像是因为硬盘中有遗留的raid信息导致。在安装选项中添加 nodmraid参数,再启动进行安装就好了如果不会添加参数可以参考这篇文章:安装ubuntu时黑屏三种解决办法就和添加 nomodeset参数一样转载于:https://www.cnblogs.com/xiyu714/p/10125984.html..._ubi-partman crashed

Linux下找不到IFCONFIG命令_linux系统ifconfig找不到?-程序员宅基地

文章浏览阅读2.3w次。# ifconfig  提示命令不存在  使用 # /sbin/ifconfig 即可  原因: 系统默认的环境变量设置不对  在 普通用户 和root用户下分别执行echo $PATH,PATH里少了四个地址:/sbin:/usr/sbin:/usr/local/sbin:/usr/kerberos/sbin  而,ifconfig恰恰就在/sbin里面。_linux系统ifconfig找不到?

在APK中获取鸿蒙应用Ability信息_getbundleinfo-程序员宅基地

文章浏览阅读4.2k次。Android开发工具箱大概在版本2.2.0(2021-06-10)就已经支持查看鸿蒙系统信息以及鸿蒙应用信息了。这里我讲一下Android开发工具箱是如何在Android应用中(APK)获取鸿蒙应用Ability信息(类似于Android应用的四大组件信息)Android开发工具箱可在应用宝、酷安下载,微信公众号:Android开发工具箱https://www.coolapk.com/apk/com.su.assistant.proAndroid中,我们获取应用列表之后,再获取应用的P_getbundleinfo

QT QModbus Rtu Serial Master 分包_qt头文件没有qmodbus-程序员宅基地

文章浏览阅读225次。使用单独线程控制modbus收发数据,信号槽交互UI和线程间的数据,信号槽自定义结构体发送,废话不多说,上代码。//如下自定义结构体,方便信号槽发送。欢迎加入QQ群538536725灌水。//最后是qt pro配置文件。//如下主UI头文件。_qt头文件没有qmodbus

el-button 去除边框,hover保留_el-button 无边框-程序员宅基地

文章浏览阅读7.3k次。el-button要想按钮普通情况没有边框 , hover及其它效果有边框 , 可以加type=“text” 属性_el-button 无边框

随便推点

Lombok简介、使用、工作原理、优缺点_lombok的优缺点-程序员宅基地

文章浏览阅读1.4k次。一、Lombok简介官方介绍Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your_lombok的优缺点

前端进阶之路-程序员宅基地

文章浏览阅读204次。前端进阶之路思维导图地址https://zh.javascript.info/红宝书:JavaScript高级程序设计绿宝书:JavaScript语言精髓与编程实践黄宝书:你不知道的JavaScript(上/中)蓝宝书:JavaScript设计模式与开发实践react工程师修炼指南JS DOM编程艺术(第2版):看红宝书的前置书,因为比较简单min-vue源码didact源码算法第四版http权威指南迷你书籍: DOM启蒙Ja

Java中解决乐观锁中ABA经典问题_java 锁 aba-程序员宅基地

文章浏览阅读397次。packagecom.yellow.cas;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;importjava.util.concurrent.atomic.AtomicStampedReference;public classABATest3 { publi..._java 锁 aba

Leetcode300题总结-程序员宅基地

文章浏览阅读520次。leetcode300题总结一下

php使用smtp封装类发送邮件_php mail port-程序员宅基地

文章浏览阅读6.5k次。<?phpdate_default_timezone_set('Asia/Shanghai'); // 'Asia/Chongqing' or 'PRC'require_once ('mail.class.php'); $smtpserver = "smtp.163.com";//SMTP服务器 $smtpserverport =25;//SMTP服务器端口 $smtpusermai_php mail port</div>

软件设计师 第一章 计算机基本工作原理_软件设计师第四版知识摘编:第1章-程序员宅基地

文章浏览阅读211次。1.海明码海明码是一种多重(复式)奇偶检错编码。它将信息用逻辑形式编码,以便能够检和纠错。用在海明码中的全部传输码字是由原来的信息和附加的奇偶校验位组成的。每一个这种奇偶位被编在传输码字的特定位置上。推导并使用长度为n位的码字的海明码,所需步骤如下:(1)确定最小的校验位数k,将它们记成D1、D2、…、Dk,每个校验位符合不同的奇偶测试规定。(2)原有信息和k个校验位一起编成长为n+k位的新码字。选择k校验位(0或1)需满足必要的奇偶条件。(3)对所接收的信息作所需的k个奇偶检查。(4)如果所有的_软件设计师第四版知识摘编:第1章