完整项目我已经放到GitHub啦~
GitHub: https://github.com/lucaschen1993/Lukastar

前言

前两篇把消消乐的设计以及基本操作的方法都讲了,这章开始讲消消乐最核心的部分。

生成

首先,先在Hierarchy中制作好600600像素的游戏面板作为容器存放Jewel(每个Jewel大小为100100像素),这样就的到得到一个6*6可以存放36个Jewel的面板
接着是游戏生成的制作方法。
在GameManager中有一个Sprite数组用于保存Jewel的Sprite(通过获取Jewel的形状通过Sprite改变)。

private Sprite[] jewelSprites;

然后在Start中通过动态加载来获取所有的JewelSprite,以及找到在Hierarchy中的游戏面板gamePlayPanel(因为Jewel要放在里边)

jewelSprites = Resources.LoadAll<Sprite>("Graphics/Jewels");
gamePlayPanel = GameObject.Find("/UICanvas/GamePlay/GamePlayPanel");

得到了JewelSprites之后,可以开始写生成的方法InitializedGame()
其实很容易就能想到根据游戏面板的rowSizecolSize可以直接生成36个Jewel,但是要有一个二维数组来保存这些Jewel,这样就容易得到每个Jewel在游戏面板的位置(可以用**GetJewelPosition()**得到)。
那么具体怎么生成的呢?
首先,每一个Jewel的Sprite都要是随机的,可以通过JewelSprites[Random.Range()]来得到随机的Sprite并赋值给Jewel中的JewelPicture。游戏本身的设计是开局生成一个没有三连以及三连以上的Jewel,所以在生成里需要做一些操作来保证生成的是一个没有以上这种情况的开局。这样的话每生成一个Jewel就需要向四周围延申去寻找是否有相同的Jewel了。

以下是具体的方法

                for (int k = 0; k < jewelSprites.Length; k++){remainIndex.Add(int.Parse(jewelSprites[k].name));}//把相同的放进数组equalList中if (j >= 2 && string.Equals(jewelObjects[i, j - 2].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name, jewelObjects[i, j - 1].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name)){equalLists.Add(int.Parse(jewelObjects[i, j - 2].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name));}if (i >=2 && string.Equals(jewelObjects[i - 2, j].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name, jewelObjects[i - 1, j].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name)){equalLists.Add(int.Parse(jewelObjects[i - 2, j].gameObject.transform.Find("JewelPicture").GetComponent<Image>().sprite.name));}//判断equasList中的元素是否相同,若相同则删除一个if (equalLists != null && equalLists.Count == 2 && equalLists[0] == equalLists[1]){equalLists.Remove(equalLists[1]);}//移除remainIndex里面与equalLists相同的元素for (int o = 0; o < equalLists.Count; o++){for (int p = 0; p < remainIndex.Count; p++){if (equalLists[o] == remainIndex[p]){remainIndex.Remove(remainIndex[p]);break;}}}

具体的思路就是找以前生成过Jewel进行匹配,看看该位置的四周是否有相同的Jewel,如果有相同的情况,就把该Sprite从数组中删除(这里声明了一个局部变量remainIndex的数组来保存JewelSprites的内容)。这样实例化Jewel的时候就不会遇到三连重复的Jewel了。

如上图所示,在游戏面板GamePlayPanel外生成Jewel,由于有mask组件的遮罩,所以生成的时候是看不见的,再把生成的Jewel通过DOTweenDOLocalMove移动到合适的位置,填充整个GamePlayPanel

jewelPrefab.transform.Find("JewelPicture").GetComponent<Image>().sprite = jewelSprites[remainIndex[UnityEngine.Random.Range(0, remainIndex.Count)] - 1];
GameObject itemObject = Instantiate<GameObject>(jewelPrefab, gamePlayPanel.transform, false);
itemObject.GetComponent<RectTransform>().localPosition = new Vector2(-250f + j * xOffset, 250f + (i-6) * yOffset);
itemObject.GetComponent<RectTransform>().DOLocalMove(new Vector2(-250f + j * xOffset, 250f + i * yOffset),1.0f);
jewelObjects[i, j] = itemObject;

在实例化的同时也要记着把每一个Jewel都保存到JewelObjects中。

交换

交换的方法其实在上一篇讲JewelManager的时候已经讲过了,就是两个Jewel,调用Move()的方法,互相移动到对方的位置。

匹配

现在介绍匹配的方法MatchJewel(),具体思路是先得到当前Jewel的位置,接着通过上、下、左、右四个方向搜索JewelObjects[,](上面提到过的方便用于保存Jewel的位置),查看是否有与当前位置的Jewel匹配的Jewel。如果有则横向的保存到matchColList,纵向的则保存到matchRowList中。如果有消除则该方法返回true

private bool MatchJewel(GameObject obj){Vector3 jewelPosition = GetJewelPosition(obj);int x = (int)jewelPosition.x;int y = (int)jewelPosition.y;List<GameObject> matchRowList = new List<GameObject>();List<GameObject> matchColList = new List<GameObject>();matchRowList.Add(obj.transform.Find("JewelPicture").gameObject);#region 纵向匹配for (int i = x + 1; i <rowSize; i++){if (i < rowSize && Equals(obj.transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name, jewelObjects[i, y].transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name)){matchRowList.Add(jewelObjects[i, y].transform.Find("JewelPicture").gameObject);}elsebreak;}for (int i = x - 1; i >= 0; i--){if (i >= 0 && Equals(obj.transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name, jewelObjects[i, y].transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name)){matchRowList.Add(jewelObjects[i, y].transform.Find("JewelPicture").gameObject);}elsebreak;}#endregionmatchColList.Add(obj.transform.Find("JewelPicture").gameObject);#region 横向匹配for (int i = y - 1; i >=0; i--){if (i >= 0 && Equals(obj.transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name, jewelObjects[x, i].transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name)){matchColList.Add(jewelObjects[x, i].transform.Find("JewelPicture").gameObject);}elsebreak;}for (int i = y + 1; i <colSize; i++){if (i < colSize && Equals(obj.transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name, jewelObjects[x, i].transform.Find("JewelPicture").gameObject.GetComponent<Image>().sprite.name)){matchColList.Add(jewelObjects[x, i].transform.Find("JewelPicture").gameObject);}elsebreak;}#endregion

接着判断这两个数组是否大于等于3,如果符合条件则把这些数组中的对象添加到needRemoveJewels中,等待下一步消除的操作。判断这里有三种情况,一种是横向连消,一种是纵向连消,还有一种是特殊情况,就是横向纵向都有消除的情况。

       //1.横向连消if (matchColList.Count >= 3 && matchRowList.Count < 3){for (int i = 0; i < matchColList.Count; i++){needRemoveJewels.Add(matchColList[i]);}//播放爆炸的声音AudioManager.Instance.PlayAudio("Audio/boom");return true;}//2.纵向连消else if (matchRowList.Count >= 3 && matchColList.Count < 3){for (int i = 0; i < matchRowList.Count; i++){needRemoveJewels.Add(matchRowList[i]);}//播放爆炸的声音AudioManager.Instance.PlayAudio("Audio/boom");return true;}//3.特殊情况else if (matchColList.Count >= 3 && matchRowList.Count >= 3){matchRowList.RemoveAt(0);for (int i = 0; i < matchRowList.Count; i++){needRemoveJewels.Add(matchRowList[i]);}for (int i = 0; i < matchColList.Count; i++){needRemoveJewels.Add(matchColList[i]);}//播放爆炸的声音AudioManager.Instance.PlayAudio("Audio/boom");return true;}

消除

消除方法RemoveJewel() 是消除从匹配方法得到得needRemoveJewel的数组。

    private bool RemoveJewel(){bool needRefill = false;for (int i = 0; i < needRemoveJewels.Count; i++){if (needRemoveJewels[i]!= null){needRemoveJewels[i].GetComponent<JewelPicture>().Fade();needRefill = true;}}        JewelsManager.Instance.ResetAllJewelSelected();needRemoveJewels.Clear();isRemove = true;return needRefill;}

这里的Fade()方法是在JewelPicture组件中,JewelPicture挂载在Jewel的子对象JewelPicture,因为如果通过Jewel来制作消除动画,会导致动画混乱,因为在两者交换的同时播放了动画,会出现消除动画播放错误的bug,因此才把消除动画的方法放在JewelPicture中。这里也使用了协程的方法,因为要控制动画播放的时间制作出一个间隔。
JewelPciture.cs

    public void Fade(){anim.SetTrigger("JewelCrash");StartCoroutine(FadeJewel());}IEnumerator FadeJewel(){yield return new WaitForSeconds(0.7f);transform.SetParent(null);Destroy(gameObject);}

再填充

Jewel消除了以后,游戏面板就会出现空缺(其实就是Destroy了Jewel的子对象JewelPicture),这个时候就需要把当前格子上面的Jewel的子对象JewelPicture来填充到当前的Jewel中。具体的做法是该Jewel空格子往上拿Jewel的JewelPicture,就是把别人的变成自己的。简单的说就是我拿你的JewelPicture,你拿上面的JewelPicture,他拿更上面的JewelPicture变为自己的JewelPicture。

如上图所示,这样操作就相当于上面的Jewel往下掉落下来,填充到空的格子。那么把消除后的空格子填充了以后,被拿走JewelPicture的空格子怎么办呢?这个时候就需要在空格子的最上面,GamePlayPanel外的位置重新生成JewelPicture并且填充到空的格子,就像InitializedGame()一样,但是此时不用考虑是否重复的问题,尽管随机生成就好了。

    private IEnumerator FillJewels(){yield return new WaitForSeconds(animationPlayTime);//自下而上遍历整个游戏棋盘,遇到空的向上遍历把上面的sprite拿给自己for (int i = rowSize - 1; i > 0; i--){for (int j = 0; j < colSize; j++){//Debug.Log(GetJewelPosition(jewelObjects[i, j])+" "+jewelObjects[i, j].transform.Find("JewelPicture").GetComponent<Image>().sprite);//当前格子为空,则不断往上找不为空的格子并把它的sprite赋值给当前格子,则找到的格子赋值为空if (jewelObjects[i, j].transform.Find("JewelPicture")== null){for (int k =i; k >= 0; k--){if (jewelObjects[k, j].transform.Find("JewelPicture") != null){//当前对象GameObject thisObj = jewelObjects[i, j];//当前对象上面其中一个对象GameObject topObj = jewelObjects[k, j];GameObject topObjChild = topObj.transform.Find("JewelPicture").gameObject;topObjChild.transform.SetParent(thisObj.transform);topObjChild.transform.DOLocalMove(Vector3.zero, moveDownTime);break;}}}}}

这里又!又!又!!!用到协程了!!!再解释一下,这个协程是为了使游戏更流畅,所以waitforsecond是等待了一个动画时间,其实就是消除动画播放完以后再执行填充的操作嘛。

匹配规则

接下来讲匹配规则MatchRule(),这里是判断交换的两个Jewel是否有消除的情况,如果两个Jewel都没有消除的情况则调用Reset的方法,恢复两个Jewel的位置,如果有消除的情况再调用Remove的方法进行消除的操作,消除完后再启用协程填充GamePlayPanel

    //判断匹配的规则public void MatchRule(){GameObject lastSelected = JewelsManager.Instance.lastSelected;GameObject currentSeleted = JewelsManager.Instance.currentSeleted;bool lastMatch = MatchJewel(lastSelected);bool currentMatch = MatchJewel(currentSeleted);if (!lastMatch&&!currentMatch){JewelsManager.Instance.ResetOriginJewel();}else if( lastMatch || currentMatch){RemoveJewel();StartCoroutine(FillJewels());}}

全盘匹配

最后再来讲全盘匹配的问题,因为当再次填充以后,有可能会出现三连以及三连以上的情况,所以这个时候需要遍历JewelObjects[,]来查看是否有匹配的,是否需要消除。如果没有,不需要的话,那么消消乐的一次移动流程就已经完成啦。如果有匹配的,需要消除的,那就把该对象扔到needRemoveJewels的数组,再调用RemoveJewel把它删掉,接着再填充,再全盘匹配,再消除,再填充,再全盘匹配…直到没有匹配的情况出现为止。

    private void ReMatchGameBorad(){for (int i = 0; i < rowSize; i++){for (int j = 0; j < colSize; j++){if (jewelObjects[i, j].transform.Find("JewelPicture").gameObject){MatchJewel(jewelObjects[i, j]);}}}//当needRemoveJewels里面有元素才调用RemoveJewel的方法if(needRemoveJewels.Count>0){RemoveJewel();StartCoroutine(FillJewels());}}

这里有个另外一个思路,因为在再填充的时候我们是需要移动Jewel的中的JewelPicture,这样我们可以得到操作过的Jewel,把它们保存到另外一个数组再进行全盘匹配,会比这个遍历整个JewelObjects省时省力很多。

总结

现在把消消乐的基础内容都讲完啦,做消消乐真的很有乐趣的,特别是每一个模块做出来就会很有成就感。其实做休闲游戏会比做rpg复杂一些,因为很多逻辑上的问题要处理。往后我会在消消乐中添加一些rpg的元素,制作成一个休闲益智的角色扮演闯关游戏。整个完整项目我已经放到github,有需要的朋友可以down下来,或者有什么建议,游戏设计上的改进,麻烦提出来,因为我太想进步了。

从零开始制作基于Unity引擎的宝石消消乐(三)相关推荐

  1. 从零开始制作基于Unity引擎的宝石消消乐(一)

    完整项目我已经放到GitHub啦~ GitHub: https://github.com/lucaschen1993/Lukastar 市场上有些消消乐真好玩,比如hxxxxxpop,pxxxxsag ...

  2. 从零开始制作基于Unity引擎的宝石消消乐(二)

    完整项目我已经放到GitHub啦~ GitHub: https://github.com/lucaschen1993/Lukastar 前言 上一篇文章说到游戏的设计,现在就来讲一讲游戏内操作方法的实 ...

  3. 基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统

    基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统 前言 项目概述 项目实现效果 2D面部表情实时捕捉 3D人体动作实时捕捉 补充 引用 前言 之前做的一个项目--使用 ...

  4. 基于Unity引擎的2D像素风Roguelike地下城游戏Demo

    文章目录 前言 一.场景搭建 1.基础房间 2.随机房间的生成 3.门与墙的生成 4.小地图 5.摄像机在房间之间的转移 二.人物制作 1.基础设定 2.人物动画 3.基础移动 4.攻击动作 三.战斗 ...

  5. 基于Unity引擎的2D像素风Roguelike地下城游戏模块之————背包系统

    文章目录 前言 一.基本界面与逻辑 二.数据管理 三.捡拾物品 四.拖拽功能 1.一些准备与修改 2.拖拽的实现(DragHandler接口) 3.背包界面的拖拽 4.数据的完善 五.一些优化 总结 ...

  6. 纪念碑谷复刻教程 - 基于Unity引擎

    目录 前言 视觉错位效果 寻路系统 PathNode Agent PathManager ClickEffect 旋转机关 Rotator RotatorActivator 工程链接 前言 之前一直很 ...

  7. 基于Unity的弹幕游戏多人联机尝试

    给一个已经定型的游戏添加哪怕是一个小特性,都是棘手的事情,很容易引入新的bug,或者破坏已有的功能.复杂的联网更是如此,它涉及到的改动几乎遍及系统的方方面面.玩家的一举一动都需要在其他人的屏幕上展现出 ...

  8. 原来这些热门游戏都基于Unity开发,为何Unity集训营越来越受欢迎?

    5月,几乎是国内游戏圈最热闹的时刻之一.过去30天内,行业两大巨头--腾讯和网易先后在线上举行年度游戏发布会,带来了近百款游戏的最新消息,也引发了从业人员对Unity引擎的再次热议. 腾讯游戏,网易游 ...

  9. 奇虎360支持Unity引擎 将推3D页游无插件安装

    导读:奇虎360与Unity引擎合作,欲在今年9月推出3D画面网页游戏.游戏将实现"即点即用"的无插件体验. 新浪游戏讯 5月30日消息,奇虎360今日召开媒体发布会,宣布与Uni ...

最新文章

  1. CentOS7.4-btrfs管理及使用
  2. 对操作系统的五点感受--接口/进程/内存/磁盘管理/系统架构
  3. oracle存储过程多分支怎样写,如何写一个分段提交的存储过程啊
  4. java特殊类型_java中如何查找某个特定类型作为形参的方法
  5. vc编译 matlab,精通VC与MATLAB联合编程——编译器的使用
  6. 苹果 Mac 上不显示外置硬盘?9 个必须尝试的修复方法
  7. 关于城市照明的大局观
  8. FreeSWITCH之日志级别
  9. java excel 水印_Java 添加Excel水印
  10. 3D建模电脑配置推荐(收藏)
  11. SSM+MySQL实现软件管理系统(含源码、MySQL、文档,包运行)
  12. ssm毕设项目基于远程协作的汽车故障诊断系统t6ipg(java+VUE+Mybatis+Maven+Mysql+sprnig)
  13. 个体工商户营业执照在网上如何年检?
  14. wp文件转shp_完美实现从mapGIS文件转换成ArcGIS中shp格式文件
  15. Apache Dubbo详解
  16. 三星苹果还能笑多久?联想挑战智能手机全球霸主
  17. Tomcat环境搭建与常见问题详解
  18. python raise和except区别_python raise和assert的区别
  19. VC++开发垃圾文件清理软件之三:程序的界面设计与实现----对话框界面
  20. Warning: Class ‘com.xxx.xxx‘ not found in module ‘xxxx‘

热门文章

  1. 杭电ACM-2036 改革春风吹满地
  2. Android自定义Button样式(自定义样式不生效怎么办)
  3. Java字节输入流的读方法(两种read方法)
  4. 奋斗吧,程序员——第二十八章 叹年来踪迹,何事苦淹留
  5. bindgetuserinfo=onGotUserInfo and @getuserinfo=onGotUserInfo
  6. 生产签名进行 Base64 编码
  7. java实现excel表格导入数据库表
  8. tp5结合es6.x的基本用法
  9. Word:Windows找不到文件‘C:\Program Files(x86)\Adobe\Acrobat DC\Acrobat\AcroTray.exe’。请确定文件名是否正确后,再试一次(已解决)
  10. doa估计算法 matlab,一种未知信源数的DOA估计-music和capon算法仿真实验!