c++编写消消乐游戏

news2024/11/26 0:41:57
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <time.h>
using namespace sf;

#define GAME_ROWS_COUNT  8
#define GAME_COLS_COUNT  8

int ts = 57;  // 每一个游戏小方块区域的大小

bool isMoving = false;
bool isSwap = false;

// 相邻位置的第几次单击,第2次单击才交换方块
int click = 0;

Vector2i pos; //鼠标单击时的位置
Vector2i offset(15, 273);

int posX1, posY1; //第一次单击的位置(记录行和列的序号)
int posX2, posY2; //第二次单击的位置(记录行和列的序号)

struct Block {
	int x, y; //坐标值     x ==  col * ts   y == row * ts;
	int row, col;  //第几行,第几列
	int kind; //表示第几种小方块
	bool match; //表示是否成三
	int alpha; //透明度

	Block() {
		match = false;
		alpha = 255;
		kind = -1;
	}
} grid[GAME_ROWS_COUNT + 2][GAME_ROWS_COUNT + 2];

void swap(Block p1, Block p2) {
	std::swap(p1.col, p2.col);
	std::swap(p1.row, p2.row);

	grid[p1.row][p1.col] = p1;
	grid[p2.row][p2.col] = p2;
}

void doEvent(RenderWindow *window) {
	Event e;
	while (window->pollEvent(e)) {
		if (e.type == Event::Closed) {
			window->close();
		}

		if (e.type == Event::MouseButtonPressed) {
			if (e.key.code == Mouse::Left) {
				if (!isSwap && !isMoving) click++;
				pos = Mouse::getPosition(*window)- offset;
			}
		}
	}

	if (click == 1) {
		posX1 = pos.x / ts + 1;
		posY1 = pos.y / ts + 1;
	}
	else if (click == 2) {
		posX2 = pos.x / ts + 1;
		posY2 = pos.y / ts + 1;

		// 是相邻方块就交换位置
		if (abs(posX2 - posX1) + abs(posY2 - posY1) == 1) {
			// 交换相邻的两个小方块
			// 消消乐的方块,怎么表示?
			swap(grid[posY1][posX1], grid[posY2][posX2]);
			isSwap = 1;
			click = 0;
		}
		else {
			click = 1;
		}
	}
}

void check() {
	for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			if (grid[i][j].kind == grid[i + 1][j].kind &&
				grid[i][j].kind == grid[i - 1][j].kind) {
				//grid[i - 1][j].match++;
				//grid[i][j].match++;
				//grid[i + 1][j].match++;
				for (int k = -1; k <= 1; k++) grid[i+k][j].match++;
			}

			if (grid[i][j].kind == grid[i][j - 1].kind &&
				grid[i][j].kind == grid[i][j + 1].kind) {
				//grid[i][j - 1].match++;
				//grid[i][j + 1].match++;
				//grid[i][j].match++;
				for (int k = -1; k <= 1; k++) grid[i][j + k].match++;
			}
		}
	}
}

void doMoving() {
	isMoving = false;

	for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			Block& p = grid[i][j]; // 引用p, 就是grid[i][j]的别名
			int dx, dy;

			for (int k = 0; k < 4; k++) {
				dx = p.x - p.col * ts;
				dy = p.y - p.row * ts;

				if (dx) p.x -= dx / abs(dx);
				if (dy) p.y -= dy / abs(dy);
			}

			if (dx || dy) isMoving = true;
		}
	}
}

void xiaochu() {
	for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			if (grid[i][j].match && grid[i][j].alpha > 10) {
				grid[i][j].alpha -= 10;
				isMoving = true;
			}
		}
	}
}

void huanYuan() {
	if (isSwap && !isMoving) {
		// 如果此时没有产生匹配效果,就要还原
		int score = 0;
		for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
			for (int j = 1; j <= GAME_COLS_COUNT; j++) {
				score += grid[i][j].match;
			}
		}

		if (score == 0) {
			swap(grid[posY1][posX1], grid[posY2][posX2]);
		}

		isSwap = false;
	}
}

void updateGrid() {
	for (int i = GAME_ROWS_COUNT; i > 0; i--) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			if (grid[i][j].match) {
				for (int k = i - 1; k > 0; k--) {
					if (grid[k][j].match == 0) {
						swap(grid[k][j], grid[i][j]);
						break;
					}
				}

			}
		}
	}

	for (int j = 1; j <= GAME_COLS_COUNT; j++) {
		int n = 0;
		for (int i = GAME_ROWS_COUNT; i > 0; i--) {
			if (grid[i][j].match) {
				grid[i][j].kind = rand() % 7;
				grid[i][j].y = -ts * n;
				n++;
				grid[i][j].match = false;
				grid[i][j].alpha = 255;
			}
		}
	}
}

void drawBlocks(Sprite * sprite, RenderWindow *window) {
	for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			Block p = grid[i][j];
			sprite->setTextureRect(
				IntRect(p.kind * 52, 0, 52, 52));
			// 设置透明度
			sprite->setColor(Color(255, 255, 255, p.alpha));
			sprite->setPosition(p.x, p.y);
			// 因为数组gird中的Block, 每个Block的行标,列标是从1计算的,
			// 并根据行标和列表来计算的x,y坐标
			// 所以坐标的偏移,需要少便宜一些,也就是相当于在正方形区域的左上角的左上角方向偏移一个单位
			// 在这个位置开发存放第0行第0列(实际不绘制第0行第0列)
			sprite->move(offset.x-ts, offset.y-ts);  // to do
			window->draw(*sprite);
		}
	}
}

void initGrid() {
	for (int i = 1; i <= GAME_ROWS_COUNT; i++) {
		for (int j = 1; j <= GAME_COLS_COUNT; j++) {
			grid[i][j].kind = rand() % 3; 
			grid[i][j].col = j;
			grid[i][j].row = i;
			grid[i][j].x = j * ts;
			grid[i][j].y = i * ts;
		}
	}
}

int main(void) {
	srand(time(0));

	RenderWindow window(VideoMode(485, 917), "Rock-xiaoxiaole");
	// 设置刷新的最大帧率
	window.setFramerateLimit(60);

	Texture t1, t2;
	t1.loadFromFile("images/bg2.png");
	if (! t2.loadFromFile("images/t4.png")) {
		return -1;
	}
	Sprite spriteBg(t1);
	Sprite spriteBlock(t2);

	initGrid();

	while (window.isOpen()) {
		// 处理用户的点击事件
		doEvent(&window);

		// 检查匹配情况
		check();

		// 移动处理
		doMoving();

		// 消除
		if (!isMoving) {
			xiaochu();
		}

		// 还原处理
		huanYuan();

		if (!isMoving) {
			updateGrid();
		}

		// 渲染游戏画面
		window.draw(spriteBg);
		// 渲染所有的小方块
		drawBlocks(&spriteBlock, &window);
		// 显示
		window.display();
	}

	return 0;
}

  • <SFML/Graphics.hpp>:SFML 图形模块的头文件,用于创建窗口、渲染精灵等图形相关操作。
  • <SFML/Audio.hpp>:SFML 音频模块的头文件,用于音频播放和处理。
  • <time.h>:C 语言标准库中的时间头文件,在此代码中用于生成随机数种子

这段代码需要依赖其他 SFML 库文件和资源文件才能正常编译和运行。在编译和执行之前,请确保已正确配置 SFML 开发环境并添加了必要的依赖项。

  1. swap(Block p1, Block p2): This function swaps the position of two blocks (p1 and p2) by swapping their row and column values.

  2. doEvent(RenderWindow *window): This function handles user events, such as mouse clicks and window closures. It checks for mouse button presses and updates the positions of the clicked blocks accordingly.

  3. check(): This function checks for matches in the game grid. It iterates through each block and checks if there are three identical blocks in a row or column. If a match is found, it increments the match counter for those blocks.

  4. doMoving(): This function moves the blocks to their appropriate positions after a swap or match has occurred. It checks each block's position and adjusts it if it is not aligned with its row or column. It sets the isMoving flag to true if any blocks are still moving.

  5. xiaochu(): This function handles the removal of matched blocks by decreasing their alpha value (transparency). It sets the isMoving flag to true if any blocks are still being removed.

  6. huanYuan(): This function reverts the last swap if no match occurs as a result of the swap. It checks if a swap has occurred (isSwap flag) and if all blocks have finished moving (isMoving flag).

  7. updateGrid(): This function updates the game grid by moving blocks down if there are empty spaces below them and generating new random blocks at the top. It iterates through each column from bottom to top and replaces matched blocks with new random blocks.

  8. drawBlocks(Sprite *sprite, RenderWindow *window): This function draws the blocks on the game window using the provided sprite. It iterates through each block in the game grid, sets the sprite's texture rectangle and color based on the block's properties, and then draws the sprite on the window.

  9. initGrid(): This function initializes the game grid by assigning random block types to each block and setting their initial positions.

  10. main(): The main function of the program. It initializes the window, loads textures for the background and block sprites, calls initGrid() to initialize the game grid, and enters the main game loop. Inside the game loop, it calls the various functions in the correct order to handle events, update the game state, and render the game screen.

These functions together implement the logic and rendering of a basic match-three puzzle game using C++ and SFML.

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

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

相关文章

springboot基于协同过滤算法商品推荐系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器&…

MybatisPlus中的save方法和insert方法区别

insert方法时直接插入一条数据&#xff0c;需要手动设置每个字段的值&#xff0c;例如&#xff1a; save方法首先会先判断该数据是否已经存在于数据库中&#xff0c;如果存在则更新数据&#xff0c;如果不存在则插入数据。使用save方法时&#xff0c;需要先设置主键值&#xff…

41.RocketMQ之高频面试题大全

消息重复消费 影响消息正常发送和消费的重要原因是网络的不确定性。 引起重复消费的原因 ACK 正常情况下在consumer真正消费完消息后应该发送ack&#xff0c;通知broker该消息已正常消费&#xff0c;从queue中剔除。当ack因为网络原因无法发送到broker&#xff0c;broker会认为…

【单片机】msp430f5529 万年历,数字时钟,矩阵键盘修改时间,7针OLED显示,内部温度读取

文章目录 功能实物图原理图一些程序片段矩阵键盘内部温度读取将年月日转为星期 功能 1 显示万年历&#xff0c;利用内部RTC模块 2 按键修改时间 3 显示芯片内部的温度数值 实物图 原理图 一些程序片段 矩阵键盘 https://qq742971636.blog.csdn.net/article/details/1316505…

Vue3 + TS + Element-Plus 封装Tree组件 《亲测可用》

前期回顾 Vite Vue3 Ts 《企业级项目》二次封装 el-table、el-pagination、el-tooltip、el-dialog_vue后台管理系统需要二次封装的组件有哪些_彩色之外的博客-CSDN博客封装的功能有哪些&#xff1f;分页、表格排序、文字居中、溢出隐藏、操作列、开关、宽、最小宽、type类型…

【Vue H5项目实战】从0到1的自助点餐系统—— 搭建脚手架(Vue3.2 + Vite + TS + Vant + Pinia + Node.js)

前言 H5 项目基于 Web 技术&#xff0c;可以在智能手机、平板电脑等移动设备上的浏览器中运行&#xff0c;无需下载和安装任何应用程序&#xff0c;且H5 项目的代码和资源可以集中在服务器端进行管理&#xff0c;只需更新服务器上的代码&#xff0c;即可让所有顾客访问到最新的…

6.2Java EE——Spring的入门程序

下面通过一个简单的入门程序演示Spring框架的使用&#xff0c;要求在控制台打印“张三&#xff0c;欢迎来到Spring”&#xff0c;实现步骤具体如下。 1、在IDEA中创建名称为chapter06的Maven项目&#xff0c;然后在pom.xml文件中加载需使用到的Spring四个基础包以及Spring依赖…

【域渗透篇】渗透域环境下的windows7与2008-r2

目录 前言 域环境搭建测试 主机发现&&端口服务扫描&&漏洞脚本扫描 主机发现 全端口扫描 漏洞脚本扫描 永恒之蓝获取shell 主机信息收集 查看当前所在域及当前域用户 找到域控制器 收集域成员的IP msf后渗透阶段 Hashdump获取本地密码信息 破解ha…

cesium学习文档

文章目录 1. 简易的cesium安装依赖修改 vite.config.js申请token创建简单的cesium 2. 修改查看器3. 修改摄像头4. 设置纽约城市模型5. 划分城市区域并且着色6. 地图标记显示7. 实现无人机巡城 1. 简易的cesium 安装依赖 yarn add -D cesium vite vite-plugin-cesium修改 vite…

Openlayers实战:小物块运动轨迹动画

Openlayers地图在做轨迹的时候,除了标注各个位置点,连线,还经常会用到轨迹动画。 本实战就是这样示例,一个物块在轨迹上移动。其实质是用setInterval,每个一小段时间,重新计算定位一下小物块位置,整体串起来就是在移动。 效果图 源代码 /* * @Author: 大剑师兰特(xia…

TextMining Day3 基于信息抽取的文本挖掘

TextMining Day3 基于信息抽取的文本挖掘 1. 简介2. 背景:文本挖掘与信息提取3. 数据挖掘与信息提取相结合3.1 DISCOTEX系统3.2 评价 4. 使用挖掘规则改进IE4.1 算法4.2 评价 7. 结论 1. 简介 图1:基于IE&#xff08;信息抽取&#xff09;的文本挖掘框架概述 本文报告了计算机…

stm32 使用CubeIDE 移植RTX5

STM32 使用st的官方开发环境 cubeide &#xff08;eclipse gcc&#xff09;移植 cmsis rtos2 RTX5 实时操作系统 这套环境的主要优势是免费。cubeide免费使用。RTX5 免商业版税&#xff08;已从原keil中剥离出来&#xff0c;现在完全开源免费&#xff09;。 一&#xff0c;环…

微软开源社区上线,能够给微软Win95等“上古系统”打补丁

日前一个基于社区的项目“Windows Update Restored”上线&#xff0c;据了解该项目的目的是为老系统重新提供对Windows Update的支持&#xff0c;可为 Windows 95 / NT 4.0/98(包括 SE)/ME/ 2000 SP2 等“上古时期”的微软操作系统提供升级补丁、修复 bug 或安全漏洞。 据悉&a…

Python+Requests+Excel接口测试实战

1、EXCEL文件接口保存方式&#xff0c;如图。 2、然后就是读取EXCEL文件中的数据方法&#xff0c;如下&#xff1a; 1 import xlrd2 3 4 class readExcel(object):5 def __init__(self, path):6 self.path path7 8 property9 def getSheet(self): 10 …

设计模式【创建型】-- 原型模式

原型模式&#xff08;Prototype&#xff09; 原型模式是指原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节&#xff0c;不调用构造函数 主要应用&#xff1a; 浅拷贝深拷贝 原型模式&#xff1a; 抽象原型类&#xf…

【Redis】—— Redis的AOF持久化机制

&#x1f4a7; 【 R e d i s 】—— R e d i s 的 A O F 持久化机制 \color{#FF1493}{【Redis】 —— Redis的AOF持久化机制} 【Redis】——Redis的AOF持久化机制&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞…

如何搭建一个优秀的移动商城?

移动商城是一种新兴的购物方式。随着移动设备的普及和人们对线上购物的需求不断增加&#xff0c;移动商城已经成为了现代电子商务的重要组成部分。 对于想要在这个领域中获得成功的人来说&#xff0c;建立一个优秀的移动商城非常重要。但在搭建移动商城的过程中&#xff0c;有很…

【监控系统】Prometheus架构相关概念及源码部署实战

上篇我们介绍了业界主流监控框架的对比&#xff0c;我们监控系统这块主要是采用Prometheus。 那么&#xff0c;什么是Prometheus 官网&#xff1a;https://prometheus.io/docs/introduction/overview/ Prometheus是一个开源的系统监控和报警系统&#xff0c;现在已经加入到CN…

Android Java代码与JNI交互 JNI方法Java类字段 (六)

🔥 Android Studio 版本 🔥 🔥 Java 基础类型数据对应 jni 中的字母 🔥 通过 jni 查找java某个类中相应字段对应的数据类型 , 需要使用到 jni 中的 GetFieldID() 函数 jfieldID GetFieldID(jclass clazz, const char* name, const char* sig){ return functions-&g…

MySQL 如何处理 慢查询

如何定位慢查询? 方式一&#xff1a; 可以使用开源工具&#xff0c;比如&#xff1a; 调试工具&#xff1a;Arthas运维工具&#xff1a;Prometheus 、Skywalking 方式二&#xff1a; 使用MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;long_qu…