【用unity实现100个游戏之11】复刻经典消消乐游戏
文章目录
- 前言
- 开始项目
- 开始
- 一、方块网格生成
- 二、方块交换
- 三、添加交换的动画效果
- 四、水平消除检测
- 五、垂直消除检测
- 六、完善删除功能
- 七、效果优化(移动方块后再进行消除检测)
- 八、方块下落
- 十、方块填充
- 十一、后续
- 源码
- 参考
- 完结
前言
欢迎来到经典消消乐游戏的复刻版!在这个令人上瘾的游戏中,您将体验到无穷的挑战和欢乐。
消消乐是一款经典的益智游戏,旨在通过消除相同的方块来获得高分。您需要在棋盘上寻找相同颜色或形状的方块,并将它们消除以获得积分。随着游戏的进行,难度也会逐渐增加,需要您的观察力、反应能力和策略思维。
我们的团队致力于为您呈现一款精心制作的复刻版本,在保留经典玩法的同时,还增加了一些全新的特色玩法和关卡设计。您将享受到华丽的图形效果、各种有趣的道具和奖励,以及丰富多样的关卡挑战。
挑战自我,突破极限,挖掘隐藏在每个关卡背后的秘密。消除方块,释放您内心的压力,让愉悦的感觉充满您的每一天!
感谢您选择我们的游戏,并希望您在这个充满乐趣和回忆的消消乐世界中度过美好的时光。开始您的消除之旅吧!
祝您玩得愉快!
注意:此版本的消消乐游戏仅供娱乐和休闲目的,请勿用于商业用途。
先来看看实现的最终效果

开始项目
百度盘链接:https://pan.baidu.com/s/12QX7_Jwmc1AITpHYrgfwFQ
提取码:5uvj
开始
一、方块网格生成
新增Block方块脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//方块脚本
public class Block : MonoBehaviour
{public int RowIndex;//行位置public int ColIndex;//列位置public void Init(int row, int col){this.RowIndex = row;this.ColIndex = col;}void Start(){}//根据行列下标设置方块的相对位置public void ResetPosition(){transform.localPosition = new Vector3(ColIndex * 88, -RowIndex * 88, 0);}
}
新增GameUI 脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GameUI : MonoBehaviour
{public Transform nodeTf;public int RowCount = 7;//行数public int ColCount = 8;//列数public Block[,] BlockArr;//存储消除方块的二维数组void Start(){InitGrid();}// 初始化网格public void InitGrid(){BlockArr = new Block[RowCount, ColCount];//构建二维数组for (int row = 0; row < RowCount; row++)for (int col = 0; col < ColCount; col++){Block b = CreateRandomBlock();b.Init(row, col);b.ResetPosition();BlockArr[row, col] = b;}}public Block CreateRandomBlock(){int ran = Random.Range(1, 5);GameObject obj = Instantiate(Resources.Load("Block/block" + ran.ToString()), nodeTf) as GameObject;return obj.AddComponent<Block>();}
}
挂载脚本

效果

二、方块交换
修改GameUI,新增交换方法
public Block[,] BlockArr;//存储消除方块的二维数组
public List<Block> SelectBlockArr;//存储选中方块的集合
public static GameUI Instance;
void Awake()
{Instance = this;SelectBlockArr = new List<Block>();
}public void SelectBlock(Block b)
{//已经选中移除if (SelectBlockArr.Contains(b)){SelectBlockArr.Remove(b);return;}//添加到选中集合SelectBlockArr.Add(b);if (SelectBlockArr.Count == 2){//选中两个 方块 交换//相邻的才能交换if ((Mathf.Abs(SelectBlockArr[0].RowIndex - SelectBlockArr[1].RowIndex) + Mathf.Abs(SelectBlockArr[0].ColIndex - SelectBlockArr[1].ColIndex)) == 1){SwapBlock(SelectBlockArr[0], SelectBlockArr[1]);}SelectBlockArr.Clear();}}public void SwapBlock(Block b1, Block b2)
{//交换数组位置BlockArr[b1.RowIndex, b1.ColIndex] = b2;BlockArr[b2.RowIndex, b2.ColIndex] = b1;//交换下标int tempRowIndex = b1.RowIndex;int tempColIndex = b1.ColIndex;b1.RowIndex = b2.RowIndex;b1.ColIndex = b2.ColIndex;b2.RowIndex = tempRowIndex;b2.ColIndex = tempColIndex;//交换位置b1.ResetPosition();b2.ResetPosition();
}
Block脚本添加点击事件,调用
using UnityEngine.EventSystems;
public class Block : MonoBehaviour,IPointerClickHandler{//点击public void OnPointerClick(PointerEventData eventData){GameUI.Instance.SelectBlock(this);}
}
效果

三、添加交换的动画效果
引入DOTween,修改Block脚本
//根据行列下标设置方块的相对位置
public void ResetPosition()
{// transform.localPosition = new Vector3(ColIndex * 88, -RowIndex * 88, 0);transform.DOLocalMove(new Vector3(ColIndex * 88,-RowIndex * 88,0),0.25f);
}
修改GameUI脚本
public void SelectBlock(Block b)
{//已经选中移除if (SelectBlockArr.Contains(b)){SelectBlockArr.Remove(b);b.transform.DOScale(1f, 0.25f);return;}//添加到选中集合SelectBlockArr.Add(b);if (SelectBlockArr.Count == 2){//选中两个 方块 交换//相邻的才能交换if ((Mathf.Abs(SelectBlockArr[0].RowIndex - SelectBlockArr[1].RowIndex) + Mathf.Abs(SelectBlockArr[0].ColIndex - SelectBlockArr[1].ColIndex)) == 1){SwapBlock(SelectBlockArr[0], SelectBlockArr[1]);}SelectBlockArr[0].transform.DOScale(1, 0.25f);SelectBlockArr[1].transform.DOScale(1, 0.25f);SelectBlockArr.Clear();}else{b.transform.DOScale(1.2f, 0.25f);}
}
效果

四、水平消除检测
修改GameUI代码
public List<Block> DelBlockArr;//存储将要消除的方块
void Awake()
{DelBlockArr = new List<Block>();
}public void SelectBlock(Block b)
{//。。。//交换SwapBlock(SelectBlockArr[0], SelectBlockArr[1]);//检测水平方块CheckDelHorizontal();//有删除的方块if (HasDelBlock()){//删除DelBlockByArr();}//。。。}//检查水平的方块是否满足消除消除条件
public void CheckDelHorizontal()
{//遍历每一行for (int row = 0; row < RowCount; row++){//存储每一行第一列方块的名称string preName = BlockArr[row, 0].gameObject.name;int count = 0;//方块名称相同个数的计数值//从第二列开始遍历for (int col = 1; col < ColCount; col++){Block b = BlockArr[row, col];if (b.gameObject.name == preName){//计数加一count++;}else{// 判断计数是否大于等于2if (count >= 2){// 将名称相同的方块添加到要消除的集合中for (int j = 0; j <= count; j++){// 判断是否已经存在消除队列,不存在再添加到集合Block delBlock = BlockArr[row, col - 1 - j];if (DelBlockArr.IndexOf(delBlock) == -1){DelBlockArr.Add(delBlock);}}}// 记录当前方块名称preName = b.gameObject.name;// 计数归0count = 0;}}//判断一行遍历完后个数是否满足消除情况if (count >= 2){for(int j = 0; j <= count; j++){//判断是否已经存在消除队列,不存在再添到集合Block delBlock = BlockArr[row, ColCount - 1 - j];if (DelBlockArr.IndexOf(delBlock) == -1){DelBlockArr.Add(delBlock);}}}}
}//是否有消除的方块
public bool HasDelBlock()
{return DelBlockArr.Count > 0;
}//消除Del集合中的方块
public void DelBlockByArr()
{for (int i = DelBlockArr.Count - 1; i >= 0; i--){Block b = DelBlockArr[i];//置空BlockArr[b.RowIndex, b.ColIndex] = null;//删除b.Delete();}//清空DelBlockArr.Clear();
}
Block新增删除方法
//删除
public void Delete()
{gameObject.GetComponent<Image>().color = Color.red;
}
效果

五、垂直消除检测
//垂直检测
public void CheckDelVertical()
{//遍历每一行for (int col = 0; col < ColCount; col++){string preName = BlockArr[0, col].gameObject.name;int count = 0;//方块名称相同个数的计数值//从第二列开始遍历for (int row = 1; row < RowCount; row++){Block b = BlockArr[row, col];if (b.gameObject.name == preName){//计数加一count++;}else{// 判断计数是否大于等于2if (count >= 2){// 将名称相同的方块添加到要消除的集合中for (int j = 0; j <= count; j++){// 判断是否已经存在消除队列,不存在再添加到集合Block delBlock = BlockArr[row - 1 - j, col];if (DelBlockArr.IndexOf(delBlock) == -1){DelBlockArr.Add(delBlock);}}}// 记录当前方块名称preName = b.gameObject.name;// 计数归0count = 0;}}//判断一行遍历完后个数是否满足消除情况if (count >= 2){for(int j = 0; j <= count; j++){//判断是否已经存在消除队列,不存在再添到集合Block delBlock = BlockArr[RowCount- 1 - j, col];if (DelBlockArr.IndexOf(delBlock) == -1){DelBlockArr.Add(delBlock);}}}}
}
效果

六、完善删除功能
修改Block代码
//删除
public void Delete()
{// gameObject.GetComponent<Image>().color = Color.red;Destroy(gameObject);//生成消除特效GameObject effect = Instantiate(Resources.Load("del")) as GameObject;effect.transform.position = transform.position;Destroy(effect, 1);
}
效果

七、效果优化(移动方块后再进行消除检测)
修改Block
//根据行列下标设置方块的相对位置
public Tween ResetPosition()
{// transform.localPosition = new Vector3(ColIndex * 88, -RowIndex * 88, 0);return transform.DOLocalMove(new Vector3(ColIndex * 88, -RowIndex * 88, 0), 0.25f);
}
修改GameUI
public void SelectBlock(Block b)
{//。。。//交换SwapBlock(SelectBlockArr[0], SelectBlockArr[1]);//动画序列Sequence seq = DOTween.Sequence();seq.Insert(0, SelectBlockArr[0].ResetPosition());seq.Insert(0, SelectBlockArr[1].ResetPosition());seq.AppendCallback(delegate (){//检测水平方块CheckDelHorizontal();CheckDelVertical();//有删除的方块if (HasDelBlock()){//删除DelBlockByArr();}});//。。。
}public void SwapBlock(Block b1, Block b2)
{//。。。//交换位置// b1.ResetPosition();// b2.ResetPosition();
}
效果

八、方块下落
修改GameUI
//方块下落
public void DropBlock()
{//从倒数第二行开始遍历for (int row = RowCount - 2; row >= 0; row--){for (int col = 0; col < ColCount; col++){//不为null的方块才能下落Block b = BlockArr[row, col];if (b != null) DropBlock(b);}}
}public void DropBlock(Block b)
{//获得当前方块下方的下标int rowIndex = b.RowIndex + 1;int colIndex = b.ColIndex;if (rowIndex > RowCount - 1){//超出下标b.ResetPosition();//移动return;}Block next_block = BlockArr[rowIndex, colIndex];if (next_block != null){//下方有方块不能再下落b.ResetPosition();return;}else{//下方没有方块与null方块进行交换BlockArr[b.RowIndex, b.ColIndex] = null;b.RowIndex = rowIndex;BlockArr[b.RowIndex, b.ColIndex] = b;//检测是否继续下落DropBlock(b);}
}
在删除方块后调用,运行效果

十、方块填充
GameUI新增填充方块方法
//填充方块
public void FillEmptyBlock()
{Sequence seq = DOTween.Sequence();for (int row = 0; row < RowCount; row++){for (int col = 0; col < ColCount; col++){Block b = BlockArr[row, col];if (b == null){b = CreateRandomBlock();b.Init(row, col);BlockArr[row, col] = b;b.transform.localPosition = new Vector3(col * 88, 88, 0);seq.Insert(0.25f, b.ResetPosition());}}}seq.AppendCallback(delegate (){//继续检测是否满足消除条件//检测水平方块CheckDelHorizontal();//检测垂直方块CheckDelVertical();//有删除的方块if (HasDelBlock()){//删除DelBlockByArr();//检测下落DropBlock();//填充FillEmptyBlock();}});
}
在检测下落后调用该方法,运行效果

十一、后续
- 后续可以加入计分规则,关卡选择功能
- 攻击图标合成对右边的美少女造成伤害,五角星给我们自己加盾,药品给我们回血,能量球合成积攒攻击力(增加后续攻击、盾或药品的数值,每次使用能量清零)
- 每过几个回合美少女会对我们发起攻击
- 通关会解锁该美少女,或者获取图集,满足爱的收集玩家,有玩下去的欲望
- 加入金币功能,每次通关可以获取,勾起玩家重复游玩关卡的欲望,金币可以用于升级主角,比如提升基础攻击力、血量等
- 制作排行版本功能,可以统计游玩时间,分数,关卡数,解锁美少女图集数,金币数进行排行
后续大家可以自行扩展,这里就不过多赘述了。至于后续是否继续完善开发,就看大家想不想看了。
源码
要啥源码,好好看,好好学!
参考
【视频】https://www.bilibili.com/video/BV1ST4y1y7Jc
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。点赞越多,更新越快哦!当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
好了,我是向宇,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关文章:
【用unity实现100个游戏之11】复刻经典消消乐游戏
文章目录 前言开始项目开始一、方块网格生成二、方块交换三、添加交换的动画效果四、水平消除检测五、垂直消除检测六、完善删除功能七、效果优化(移动方块后再进行消除检测)八、方块下落十、方块填充十一、后续 源码参考完结 前言 欢迎来到经典消消乐游…...
若依cloud 修改包名等
一、项目的项目名。 先改pom 然后在重命名文件 1、 修改主pom.xml <artifactId>ruoyi-api</artifactId> 缓存 <artifactId>zxf-api</artifactId> <groupId>com.ruoyi</groupId> <groupId>com.zhixiaofeng</groupId> 2、…...
健康系统练习
健康系统 项目建构: 前后端分离,前端vue3,后端Java,springboot做跨域处理,前端将在vscode中 的tomcat下部署,后端将在ideal中集成的tomcat中部署 创建项目工程在ideal中直接选用springi…创建,…...
网络协议从入门到底层原理学习(一)—— 简介及基本概念
文章目录 网络协议从入门到底层原理学习(一)—— 简介及基本概念一、简介1、网络协议的定义2、网络协议组成要素3、广泛的网络协议类型网络通信协议网络安全协议网络管理协议 4、网络协议模型对比图 二、基本概念1、网络互连模型2、计算机之间的通信基础…...
centos密码过期导致navicat无法通过SSH登录阿里云RDS问题
具体错误提示:2013 - Lost connection to server at "hand hake: reading initial communication packet, system error: 0 解决办法:更新SSH服务器密码...
对于pytorch和对应pytorch网站的探索
一、关于网站上面的那个教程: 适合PyTorch小白的官网教程:Learning PyTorch With Examples - 知乎 (zhihu.com) 这个链接也是一样的, 总的来说,里面讲了这么一件事: 如果没有pytorch的分装好的nn.module用来继承的话,需要设计…...
和AI聊天:动态规划
动态规划 动态规划(Dynamic Programming,简称 DP)是一种常用于优化问题的算法。它解决的问题通常具有重叠子问题和最优子结构性质,可以通过将问题分解成相互依赖的子问题来求解整个问题的最优解。 动态规划算法主要分为以下几个步…...
微信小程序——使用插槽slot快捷开发
微信小程序的插槽(slot)是一种组件化的技术,用于在父组件中插入子组件的内容。通过插槽,可以将父组件中的一部分内容替换为子组件的内容,实现更灵活的组件复用和定制。 插槽的使用步骤如下: 在父组件的wx…...
大数据技术之Hadoop:使用命令操作HDFS(四)
目录 一、创建文件夹 二、查看指定目录下的内容 三、上传文件到HDFS指定目录下 四、查看HDFS文件内容 五、下载HDFS文件 六、拷贝HDFS文件 七、HDFS数据移动操作 八、HDFS数据删除操作 九、HDFS的其他命令 十、hdfs web查看目录 十一、HDFS客户端工具 11.1 下载插件…...
静态路由配置实验:构建多路由器网络拓扑实现不同业务网段互通
文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. 配置 IP 地址2. 按照需求配置静态路由,实现连接 PC 的业务网段互通 摘要: 本实验旨在通过配置网络设备的IP地址和静态路由,实现不同业务网段之间的互通。通过构建一组具有…...
Python函数的概念以及定义方式
一. 前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 二. 什么是函数? 假设你现在是一个工人,如果你实现就准备好了工具,等你接收到任务的时候, 直接带上工…...
【数学建模竞赛】超详细Matlab二维三维图形绘制
二维图像绘制 绘制曲线图 g 是表示绿色 b--o是表示蓝色/虚线/o标记 c*是表示蓝绿色(cyan)/*标记 ‘MakerIndices,1:5:length(y) 每五个点取点(设置标记密度) 特殊符号的输入 序号 需求 函数字符结构 示例 1 上角标 ^{ } title( $ a…...
2023国赛数学建模E题思路代码 黄河水沙监测数据分析
E题最大的难度是数据处理,可以做一个假设,假设一定时间内流量跟含沙量不变,那么我们可以对数据进行向下填充,把所有的数据进行合并之后可以对其进行展开特性分析,在研究调水调沙的实际效果时,可以先通过分析…...
窗口延时、侧输出流数据处理
一 、 AllowedLateness API 延时关闭窗口 AllowedLateness 方法需要基于 WindowedStream 调用。AllowedLateness 需要设置一个延时时间,注意这个时间决定了窗口真正关闭的时间,而且是加上WaterMark的时间,例如 WaterMark的延时时间为2s&…...
发送HTTP请求
HTTP请求是一种客户端向服务器发送请求的协议。它是基于TCP/IP协议的应用层协议,用于在Web浏览器和Web服务器之间传输数据。 HTTP请求由以下几个部分组成: 请求行:包含请求方法、请求的URL和HTTP协议的版本。常见的请求方法有GET、POST、PUT、…...
高等工程数学张韵华版第四章课后题答案
下面答案仅供参考! 章节目录 第4章 欧氏空间和二次型 4.1内积和欧氏空间 4.1.1内积的定义 4.1.2欧氏空间的性质 4.1.3 正交投影 4.1.4 施密特正交化 4.2 正交变换和对称变换 4.2.1 正交变换 4.2.2 正交矩阵 4.2.3 对称变换 4.2.4 对称矩阵 4.3 二…...
wpf C# 用USB虚拟串口最高速下载大文件 每包400万字节 平均0.7s/M,支持批量多设备同时下载。自动识别串口。源码示例可自由定制。
C# 用USB虚拟串口下载大文件 每包400万字节 平均0.7s/M。支持批量多设备同时下载。自动识别串口。可自由定制。 int 32位有符号整数 -2147483648~2147483647 但500万字节时 write时报端口IO异常。可能是驱动限制的。 之前用这个助手发文件,连续发送࿰…...
代码随想录二刷day20
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣654. 最大二叉树二、力扣617. 合并二叉树三、力扣700. 二叉搜索树中的搜索四、力扣98. 验证二叉搜索树 前言 一、力扣654. 最大二叉树 /*** Definitio…...
Yolov5如何训练自定义的数据集,以及使用GPU训练,涵盖报错解决
本文主要讲述了Yolov5如何训练自定义的数据集,以及使用GPU训练,涵盖报错解决,案例是检测图片中是否有救生圈。 最后的效果图大致如下: 效果图1效果图2 前言 系列文章 1、详细讲述Yolov5从下载、配置及如何使用GPU运行 2、…...
设计模式之单列模式
单列模式是一种经典的设计模式,在校招中最乐意考的设计模式之一~ 设计模式就是软件开发中的棋谱,大佬们针对一些常见的场景,总结出来的代码的编写套路,按照套路来写,不说你写的多好,至少不会太差~ 在校招中…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
