更新日期:2025年6月17日。
项目源码:后续章节发布

索引

  • 马赛克【Mosaic】
    • 一、游戏最终效果
    • 二、玩法简介
    • 三、正式开始
      • 1.定义游戏窗口类
      • 2.规划游戏窗口、视口区域
      • 3.地图方块阵列
        • ①.定义方块结构体
        • ②.生成方块阵列
        • ③.计算九宫格黑色方块数量
        • ④.排除任意九宫格内不存在任何一个显示文字方块的情况
        • ⑤.设置马赛克题目
      • 4.绘制方块阵列
      • 5.标记方块
      • 6.检测游戏是否通关
      • 7.绘制游戏操作说明
      • 8.游戏技巧
        • ①.数字0所在九宫格全为白色
        • ②.数字9所在九宫格全为黑色
        • ③.靠边6所在九宫格全为黑色
        • ④.精准排除法
        • ⑤.模糊排除法
      • 9.暂停游戏、退出游戏

马赛克【Mosaic】

本篇的目标是开发一个马赛克【Mosaic】小游戏。

一、游戏最终效果

Unity编辑器小游戏:马赛克

二、玩法简介

马赛克是扫雷游戏的变种,是一款推理游戏,其玩法简单却富有挑战性。

游戏界面由方块阵列组成,玩家需要推理每个方块的正确颜色,并标记为该颜色(分为黑色白色),所以游戏通关后的界面看起来像马赛克一样,因而得名。

有些方块上会显示一个数字,代表了该方块所在的9宫格中黑色方块的数量(包含该方块自身),玩家需要通过这些信息来推理逐步找出所有黑色方块。

三、正式开始

1.定义游戏窗口类

首先,定义马赛克的游戏窗口类MiniGame_Mosaic,其继承至MiniGameWindow【小游戏窗口基类】

    /// <summary>/// 马赛克/// </summary>public class MiniGame_Mosaic : MiniGameWindow{}

2.规划游戏窗口、视口区域

通过覆写虚属性实现规划游戏视口区域大小:

        /// <summary>/// 游戏名称/// </summary>public override string Name => "马赛克 [Mosaic]";/// <summary>/// 游戏窗体大小/// </summary>public override Vector2 WindowSize => new Vector2(700, 530);/// <summary>/// 游戏视口区域/// </summary>public override Rect ViewportRect => new Rect(5, 25, 500, 500);

注意:游戏窗体大小必须 > 游戏视口区域。

然后通过代码打开此游戏窗口:

        [MenuItem("MiniGame/马赛克 [Mosaic]", priority = 3)]private static void Open_MiniGame_Mosaic(){MiniGameWindow.OpenWindow<MiniGame_Mosaic>();}

便可以看到游戏的窗口、视口区域如下(左侧深色凹陷区域为视口区域):

在这里插入图片描述

3.地图方块阵列

马赛克游戏的背景也是由一系列方块组成的,所以我们先来绘制如下这样的地图方块阵列:

在这里插入图片描述

①.定义方块结构体

首先,定义方块结构体Block,其代表方块阵列中的一个方块:

        /// <summary>/// 方块/// </summary>public struct Block{/// <summary>/// 方块位置/// </summary>public Rect Position;/// <summary>/// 是否为黑色/// </summary>public bool IsBlack;/// <summary>/// 是否为白色/// </summary>public bool IsWhite;/// <summary>/// 周围九宫格内黑色方块数量/// </summary>public int BlackCount;/// <summary>/// 是否显示数量文字/// </summary>public bool IsShowCount;}
②.生成方块阵列

我们设计如下四种难度等级的关卡:

名称地图大小
初级5*5
中级10*10
高级15*15
大师级20*20
        private readonly string[] LEVELS = new string[] { "初级(5*5)", "中级(10*10)", "高级(15*15)", "大师级(20*20)" };

所以游戏视口的宽度、高度是根据大师级难度(方块尺寸25 * 方块宽高20 = 500)的大小来设置的:

        private const int BLOCKSIZE = 25;

根据选择的不同难度,来生成对应的地图方块阵列:

        private Block[,] _mosaic;/// <summary>/// 开始游戏/// </summary>private void StartGame(){if (_level == 0){WIDTH = 5;HEIGHT = 5;}else if (_level == 1){WIDTH = 10;HEIGHT = 10;}else if (_level == 2){WIDTH = 15;HEIGHT = 15;}else if (_level == 3){WIDTH = 20;HEIGHT = 20;}GenerateMosaic();}/// <summary>/// 生成马赛克矩阵/// </summary>private void GenerateMosaic(){//生成马赛克矩阵_mosaic = new Block[WIDTH, HEIGHT];for (int row = 0; row < WIDTH; row++){for (int col = 0; col < HEIGHT; col++){_mosaic[row, col].Position = new Rect(row * BLOCKSIZE, col * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE);_mosaic[row, col].IsBlack = Utility.IsTriggerProbability(60);_mosaic[row, col].IsWhite = false;_mosaic[row, col].IsShowCount = Utility.IsTriggerProbability((_level == 0 || _level == 1) ? 70 : 60);}}}

在生成方块阵列的方法中,每一个方块有60%概率为黑色:

_mosaic[row, col].IsBlack = Utility.IsTriggerProbability(60);

初级中级时,每一个方块有70%概率显示九宫格内黑色方块数量,高级大师级60%,相应提升了难度:

_mosaic[row, col].IsShowCount = Utility.IsTriggerProbability((_level == 0 || _level == 1) ? 70 : 60);
③.计算九宫格黑色方块数量

在生成方块阵列完成后,下一步就需要计算每一个方块所属九宫格中的黑色方块数量:

        /// <summary>/// 生成马赛克矩阵/// </summary>private void GenerateMosaic(){//......//计算所有方块所在九宫格的黑色方块数量for (int row = 0; row < WIDTH; row++){for (int col = 0; col < HEIGHT; col++){_mosaic[row, col].BlackCount = CalculateBlackCount(row, col);}}}/// <summary>/// 计算方块所在九宫格的黑色方块数量/// </summary>private int CalculateBlackCount(int x, int y){int count = 0;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int newX = x + i;int newY = y + j;if (newX >= 0 && newX < WIDTH && newY >= 0 && newY < HEIGHT && _mosaic[newX, newY].IsBlack){count++;}}}return count;}
④.排除任意九宫格内不存在任何一个显示文字方块的情况

我们必须确保,任意九宫格中,至少有一个方块会显示黑色方块数量,否则会显著提升解题难度,甚至不可解:

        /// <summary>/// 生成马赛克矩阵/// </summary>private void GenerateMosaic(){//......//排除单一九宫格内不存在任何一个显示文字方块的情况for (int row = 0; row < WIDTH; row++){for (int col = 0; col < HEIGHT; col++){int count = CalculateShowCount(row, col);if (count <= 0){_mosaic[row, col].IsShowCount = true;}}}}/// <summary>/// 计算方块所在九宫格的显示文字的数量/// </summary>private int CalculateShowCount(int x, int y){int count = 0;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int newX = x + i;int newY = y + j;if (newX >= 0 && newX < WIDTH && newY >= 0 && newY < HEIGHT && _mosaic[newX, newY].IsShowCount){count++;}}}return count;}
⑤.设置马赛克题目

马赛克游戏也可以看作是一道逻辑解密题,由于之前我们随机生成了一些黑色方块,现在部分方块上已经标注了其所在九宫格中黑色方块的数量,现在只需要将所有黑色方块去掉,使玩家通过逻辑推理来寻找黑色方块即可:

        /// <summary>/// 生成马赛克矩阵/// </summary>private void GenerateMosaic(){//......//设置马赛克题目for (int row = 0; row < WIDTH; row++){for (int col = 0; col < HEIGHT; col++){_mosaic[row, col].IsBlack = false;}}}

4.绘制方块阵列

然后在OnGameViewportGUI方法中绘制方块阵列:

		//未标记的方块风格private GUIStyle _noBlockGS;//已标记的方块风格private GUIStyle _blockGS;protected override void OnGameViewportGUI(){base.OnGameViewportGUI();DrawPanel();}/// <summary>/// 绘制画布/// </summary>private void DrawPanel(){for (int h = 0; h < HEIGHT; h++){for (int w = 0; w < WIDTH; w++){DrawBlock(w, h);}}}/// <summary>/// 绘制方块/// </summary>private void DrawBlock(int x, int y){string count = _mosaic[x, y].IsShowCount ? _mosaic[x, y].BlackCount.ToString() : "";if (_mosaic[x, y].IsBlack){GUI.backgroundColor = Color.black;GUI.Box(_mosaic[x, y].Position, count, _blockGS);GUI.backgroundColor = Color.white;}else if (_mosaic[x, y].IsWhite){GUI.Box(_mosaic[x, y].Position, count, _blockGS);}else{GUI.Box(_mosaic[x, y].Position, count, _noBlockGS);}}

此时就能绘制出游戏的地图方块阵列了,比如初级(5*5)的:

在这里插入图片描述

注意:这里有一个选择关卡难度的过程省略了,该过程很简单便不浪费篇幅赘述了,后续在源码中即可一目了然。

两种状态的方块绘制出来大致是这样的:

在这里插入图片描述

5.标记方块

OnGamePlayingEvent方法中完成标记方块的逻辑:

        protected override void OnGamePlayingEvent(Event e, Vector2 mousePosition){base.OnGamePlayingEvent(e, mousePosition);if (e.type == EventType.MouseDown){if (e.button == 0){for (int h = 0; h < HEIGHT; h++){for (int w = 0; w < WIDTH; w++){if (_mosaic[w, h].Position.Contains(mousePosition)){//鼠标左键标记为黑色(再次点击则取消标记黑色)_mosaic[w, h].IsWhite = false;_mosaic[w, h].IsBlack = !_mosaic[w, h].IsBlack;Repaint();return;}}}}else if (e.button == 1){for (int h = 0; h < HEIGHT; h++){for (int w = 0; w < WIDTH; w++){if (_mosaic[w, h].Position.Contains(mousePosition)){//鼠标右键标记为白色(再次点击则取消标记白色)_mosaic[w, h].IsBlack = false;_mosaic[w, h].IsWhite = !_mosaic[w, h].IsWhite;Repaint();return;}}}}}}

6.检测游戏是否通关

检测游戏是否通关的逻辑为:每一个显示了黑色方块数量的方块,其所在九宫格内必须真实标记相应数量的黑色方块,其余标记为白色。

跟扫雷不同的是:每个方块的黑、白属性并不固定,只要最终满足每个九宫格的黑色方块数量即可。

        /// <summary>/// 检测马赛克题目是否完成(游戏是否通关)/// </summary>private bool CheckMosaicQuestion(){for (int row = 0; row < WIDTH; row++){for (int col = 0; col < HEIGHT; col++){//如果此方块显示了黑色方块数量,则检测其所在九宫格中是否存在相应数量的方块if (_mosaic[row, col].IsShowCount){int count = CalculateBlackCount(row, col);//任意九宫格中黑色方块数量不对,则游戏未通关if (count != _mosaic[row, col].BlackCount){return false;}}}}return true;}

7.绘制游戏操作说明

最后,操作说明等其他UI统一绘制在OnOtherGUI方法中:

        protected override void OnOtherGUI(){base.OnOtherGUI();Rect rect = new Rect(ViewportRect.x + ViewportRect.width + 5, ViewportRect.y + ViewportRect.height - 25, 80, 20);GUI.backgroundColor = Color.green;//玩家可主动点击Done按钮,检测游戏是否通关if (GUI.Button(rect, "Done")){if (CheckMosaicQuestion()){IsGameSuccessed = true;}else{ShowNotification(new GUIContent("You are failed, please try again."));}}rect.x += 85;GUI.backgroundColor = Color.yellow;//也可重新开始(如果当前题目无解,随机生成会有无解的情况)if (GUI.Button(rect, "Restart")){OnRestart();}GUI.backgroundColor = Color.white;rect.Set(ViewportRect.x + ViewportRect.width + 5, ViewportRect.y + ViewportRect.height - 75, 80, 20);GUI.Button(rect, "Mouse Left");rect.x += 85;rect.width = 100;GUI.Label(rect, "Marked as black");rect.Set(ViewportRect.x + ViewportRect.width + 5, ViewportRect.y + ViewportRect.height - 50, 80, 20);GUI.Button(rect, "Mouse Right");rect.x += 85;rect.width = 100;GUI.Label(rect, "Marked as white");}

这里绘制出来的效果如下:

在这里插入图片描述

8.游戏技巧

介绍一些游戏技巧(并不是全部)。

①.数字0所在九宫格全为白色

数字0代表所在九宫格中一个黑色方块也没有:

在这里插入图片描述

②.数字9所在九宫格全为黑色

同理,数字9代表所在九宫格中全为黑色方块:

在这里插入图片描述

③.靠边6所在九宫格全为黑色

靠边数字6代表所在九宫格中有6个黑色方块,但其九宫格只有6个方块,所以全为黑色:

在这里插入图片描述

④.精准排除法

如下图方块数字4,已知其下方2个方块为白色,排除后其九宫格只剩4个方块,所以4个全为黑色:

在这里插入图片描述

⑤.模糊排除法

如下图方块数字5,已知其下方2个方块为黑色,则上方4个方块中只能有3个黑色方块。

转向数字8,表明其所在九宫格中只有1个白色方块,则其上方左侧方块均为黑色(那1个白色方块在与数字5的交界区域中)。

在这里插入图片描述

至此,一个简单的马赛克小游戏就完成了,简单但却耐玩,试玩效果如下:马赛克【Mosaic】。

9.暂停游戏、退出游戏

同俄罗斯方块。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于深度学习的智能图像质量评估系统:技术与实践

前言 在数字图像处理和计算机视觉领域&#xff0c;图像质量评估&#xff08;Image Quality Assessment, IQA&#xff09;是一个重要的研究方向。图像质量评估的目标是通过算法自动评估图像的质量&#xff0c;包括清晰度、对比度、噪声水平等。传统的图像质量评估方法主要依赖于…

【Golang面试题】Go语言实现请求频率限制

Go语言实现请求频率限制&#xff1a;从计数器到令牌桶的完整指南 在实际开发中&#xff0c;接口被恶意刷请求是常见问题。本文将深入探讨Go语言中四种主流的请求限流方案&#xff0c;从简单到复杂逐步深入&#xff0c;助你构建高可用服务。 一、基础方案&#xff1a;计数器法…

11Labs 增长负责人分享:企业级市场将从消费级或开发者切入丨Voice Agent 学习笔记

本文摘自 Founder Park AI 产品如何做增长&#xff0c;ElevenLabs的案例很值得学习。 专注于 AI 语音生成的独角兽企业 ElevenLabs 可以说一直在高速增长。在今年 1 月完成 1.8 亿美元 C 轮融资后&#xff0c;ElevenLabs 的估值突破 30 亿&#xff0c;直指 33 亿美元。2024 年…

Linux 命令:grep

概述 在Linux系统里&#xff0c;grep是一款十分实用的命令行工具&#xff0c;它主要用于在文件或者输入流中搜索符合特定模式的文本。下面为你详细介绍它的用法。资料已经分类整理好&#xff1a;https://pan.quark.cn/s/26d73f7dd8a7 基本语法 grep [选项] 搜索模式 [文件..…

Java八股文——MySQL「架构篇」

MySQL主从复制了解吗 面试官您好&#xff0c;我了解MySQL的主从复制。它是构建高可用、高可扩展数据库架构的核心基石。 1. 主从复制的核心原理与流程 整个主从复制的过程&#xff0c;就是一场围绕 binlog&#xff08;二进制日志&#xff09; 的“接力赛”。这个过程主要可以…

ubuntu下python版本升级导致pyqt不能正常运行解决

最终解决方案 ubuntu下多python版本pyqt兼容性问题解决 python3.9 -m pip install --upgrade --force-reinstall --prefer-binary pyqt5)尝试解决方案一(失败) 系统默认python版本可以&#xff0c;其他版本不行 sudo apt install pyqt5-dev-tools尝试解决方案二(失败) 一直…

AIGC工具平台-VideoRetalking音频对口型数字人

唇形合成技术正逐渐成为AIGC内容生产领域的重要工具&#xff0c;能够实现音视频数据的高度融合。基于VideoRetalking模块的可视化界面降低了技术门槛&#xff0c;使非技术背景的用户也能便捷体验唇形驱动数字人合成的流程。 本文重点解析该模块的使用方式及开发流程&#xff0…

前端项目如何部署为https

如何为项目部署设置HTTPS 设置HTTPS是保护网站数据传输安全的重要步骤。以下是设置HTTPS的主要方法&#xff1a; 1. 获取SSL/TLS证书 免费证书选项 Let’s Encrypt&#xff1a;最流行的免费证书颁发机构Cloudflare&#xff1a;提供免费SSL和CDN服务ZeroSSL&#xff1a;另一…

nginx 配置 系统升级页面

默认80端口配置如下&#xff1a; server {listen 80; # 指定端口号server_name 192.168.2.96; # 替换为实际域名或IP# 全局重定向到升级页面&#xff08;排除自身防循环&#xff09;if ($request_uri !~* "/upgrade.html") {return 307 /upgrade.html; # 临时重定…

计算机基础(一)——设计模式

一、设计模式 设计模式&#xff08;Design Patterns&#xff09;是软件开发中反复出现问题的解决方案的通用描述。 它是经过总结、提炼的高效代码结构和设计方案&#xff0c;帮助开发者写出更灵活、可维护和可扩展的代码。 优点注意点规范代码结构&#xff0c;提高开发效率设…

Mac电脑 磁盘检测和监控工具 DriveDx

DriveDx Mac 一款不监视驱动器的内置S.M.A.R.T.状态的先进驱动器运行状况诊断和监测工具。 还分析了所有驱动器健康密切相关的指标&#xff0c; SSD或硬盘驱动器故障&#xff08;像SSD磨损 /耐久性&#xff0c;坏扇区重新分配&#xff0c;离线坏道&#xff0c;未定扇形区&…

频繁操作Json嵌套数据PostgreSQL配合JSON操作工具类+sql

文章目录 1.工具类2.依赖3.sql 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 背景&#xff1a;因为频繁操作json嵌套数据 PostgreSQL得…

京东云 centos vim有操作混乱的问题

centos云服务器 安装micro编辑器可以解决 yum install micro

限流系列之二:TDMQ CKafka 版限流方案详解及最佳实践

导语 在当今大数据和实时通信的时代&#xff0c;消息队列在分布式系统中扮演着至关重要的角色。CKafka 作为一种高性能、高可靠的消息中间件&#xff0c;被广泛应用于各种业务场景中。然而&#xff0c;随着业务的增长和数据流量的增加&#xff0c;CKafka 在生产者和消费者以极…

消息队列的基本概念

文章目录 为什么需要消息队列&#xff1f;&#x1f914;&#x1f3af; 核心价值&#x1f4cb; 使用场景 &#x1f3d7;️ 架构层面的基本概念整体架构图&#x1f4e6; 核心组件详解1. Broker&#xff08;消息代理&#xff09;2. Topic&#xff08;主题&#xff09;3. Partition…

Shell脚本中和||语法解析

https://www.cnblogs.com/liuyuelinfighting/p/16377705.html 在 Shell 脚本中&#xff0c;&& 和 || 是逻辑操作符&#xff0c;用于根据前一个命令的退出状态&#xff08;成功或失败&#xff09;决定是否执行后续命令。这种语法称为 命令链&#xff08;Command Chainin…

MySQL中的常见运算符

精选专栏链接 &#x1f517; MySQL技术笔记专栏Redis技术笔记专栏大模型搭建专栏Python学习笔记专栏深度学习算法专栏 欢迎订阅&#xff0c;点赞&#xff0b;关注&#xff0c;每日精进1%&#xff0c;共攀技术高峰 更多内容持续更新中&#xff01;希望能给大家带来帮助~ &…

高级IO技术详解:阻塞/非阻塞IO、多路复用与内存映射

高级IO技术详解&#xff1a;阻塞/非阻塞IO、多路复用与内存映射 关键词&#xff1a;阻塞IO 非阻塞IO select/poll/epoll mmap 一、阻塞IO vs 非阻塞IO 类型行为特点设置方式阻塞IO- 读空管道阻塞- 写满管道阻塞默认模式非阻塞IO- 读空文件返回 -1&#xff0c;errnoEAGAIN- 写满…

【无标题】拓扑对偶框架的严格性补完与哲学突破

拓扑对偶框架的严格性补完与哲学突破&#xff1a; 一、数学严格性补完&#xff1a;同调类守恒的解决方案 1.1 负系数问题的几何化修正 **问题本质**&#xff1a;当 $a_i$ 含负数时&#xff0c;曲率分配 $\kappa\frac{2\pi a_i}{A_{\text{max}}}$ 导致伪黎曼流形 **解决方案…

从0开始学习R语言--Day25--A/B测试 vs 多臂老虎机

通常在比较两个不同的方案对数据的影响时&#xff0c;我们会各拿50%的数据去进行对照试验&#xff0c;这样观测到的结果会最大程度地保留统计学上的特点。但实际上&#xff0c;并不是所有对比不同方案都要这样做&#xff0c;一来&#xff0c;我们需要等到两组实验都完全结束后&…