文章目录

  • 一、前言
  • 二、事件管理器
  • 三、水果点击事件
  • 四、水果拖动交换
  • 五、运行测试

一、前言

嗨,大家好,我是新发。下班坐地铁的时候,好几次看到其他人在玩消消乐,既然大家都这么喜欢玩,那我就写个Unity制作水果消消乐的教程吧。

我会根据内容点分成好几篇文章来讲,希望对想学Unity的同学有所帮助,创作不易,喜欢的同学欢迎关注、点赞、收藏,文章目录如下:
第一篇:生成冰块阵列
第二篇:随机生成水果
第三篇:水果拖动与交换逻辑
第四篇:使用DOTween插件实现水果的滑动效果
第五篇:水果的消除检测,实现消除效果
第六篇:水果下落与新水果生成
第七篇:水果消除特效
第八篇:游戏得分加分效果
第九篇:使用UGUI显示游戏UI

游戏运行效果如下:

最终的Demo工程已上传到GitHub,感兴趣的同学可以自行下载下来学习。
GitHub地址:https://github.com/linxinfa/UnityXiaoXiaoLeDemo
注:我使用的Unity版本为2020.1.14f1c1


本篇讲水果拖动与交换的实现,本篇的效果:

二、事件管理器

我们想要监听水果被点击的事件,那么我们先封装一个事件管理器,可以用来订阅事件和抛出事件。
创建一个EventDispatcher.cs脚本。

代码如下:

// EventDispatcher.csusing UnityEngine;
using System.Collections.Generic;public delegate void MyEventHandler(params object[] objs);/// <summary>
/// 事件管理器,订阅事件与事件触发
/// </summary>
public class EventDispatcher
{/// <summary>/// 订阅事件/// </summary>public void Regist(string eventName, MyEventHandler handler){if (handler == null)return;if (!listeners.ContainsKey(eventName)){listeners.Add(eventName, new Dictionary<int, MyEventHandler>());}var handlerDic = listeners[eventName];var handlerHash = handler.GetHashCode();if (handlerDic.ContainsKey(handlerHash)){handlerDic.Remove(handlerHash);}listeners[eventName].Add(handler.GetHashCode(), handler);}/// <summary>/// 注销事件/// </summary>public void UnRegist(string eventName, MyEventHandler handler){if (handler == null)return;if (listeners.ContainsKey(eventName)){listeners[eventName].Remove(handler.GetHashCode());if (null == listeners[eventName] || 0 == listeners[eventName].Count){listeners.Remove(eventName);}}}/// <summary>/// 触发事件/// </summary>public void DispatchEvent(string eventName, params object[] objs){if (listeners.ContainsKey(eventName)){var handlerDic = listeners[eventName];if (handlerDic != null && 0 < handlerDic.Count){var dic = new Dictionary<int, MyEventHandler>(handlerDic);foreach (var f in dic.Values){try{f(objs);}catch (System.Exception ex){Debug.LogErrorFormat(szErrorMessage, eventName, ex.Message, ex.StackTrace);}}}}}/// <summary>/// 清空事件/// </summary>/// <param name="key"></param>public void ClearEvents(string eventName){if (listeners.ContainsKey(eventName)){listeners.Remove(eventName);}}private Dictionary<string, Dictionary<int, MyEventHandler>> listeners = new Dictionary<string, Dictionary<int, MyEventHandler>>();private readonly string szErrorMessage = "DispatchEvent Error, Event:{0}, Error:{1}, {2}";private static EventDispatcher s_instance;public static EventDispatcher instance{get{if (null == s_instance)s_instance = new EventDispatcher();return s_instance;}}
}

接着,我们创建一个EventDef.cs用来定义事件。

三、水果点击事件

我们定义一个EVENT_FRUIT_SELECTED事件。

// EventDef.cspublic class EventDef
{/// <summary>/// 水果被点击/// </summary>public const string EVENT_FRUIT_SELECTED = "EVENT_FRUIT_SELECTED";
}

接着,我们在FruitItem.cs中添加OnMouseDown函数,抛出EVENT_FRUIT_SELECTED事件。

// FruitItem.cs/// <summary>
/// 水果被点击
/// </summary>
private void OnMouseDown()
{// 抛出事件EventDispatcher.instance.DispatchEvent(EventDef.EVENT_FRUIT_SELECTED, this);
}

接着,我们在FruitSpawner.cs中添加事件的订阅。

// FruitSpawner.cs/// <summary>
/// 被选中的水果
/// </summary>
private FruitItem m_curSelectFruit;private void Awake()
{// 注册事件EventDispatcher.instance.Regist(EventDef.EVENT_FRUIT_SELECTED, OnFruitSelected);
}private void OnDestroy()
{// 注销事件EventDispatcher.instance.UnRegist(EventDef.EVENT_FRUIT_SELECTED, OnFruitSelected);
}/// <summary>
/// 水果被点击
/// </summary>
private void OnFruitSelected(params object[] args)
{// 把被点击的水果对象缓存起来,下面交换水果的逻辑需要用到m_curSelectFruit = args[0] as FruitItem;
}

四、水果拖动交换

我们分析一下操作过程,我们点击到水果后,就是滑动,水平滑动或者竖直滑动,如果往左滑则与左边的水果交换。
我们可以在Update函数中判断滑动。

// FruitSpawner.cs/// <summary>
/// 手指水平滑动量
/// </summary>
private float m_fingerMoveX;
/// <summary>
/// 手指竖直滑动量
/// </summary>
private float m_fingerMoveY;private void Update()
{if (null == m_curSelectFruit) return;if (Input.GetMouseButtonUp(0)){// 手指抬起,释放当前选中的水果对象m_curSelectFruit = null;return;}#if UNITY_EDITOR || UNITY_STANDALONEif (Input.GetMouseButton(0))
#elseif(1 == Input.touchCount && Input.touches[0].phase == TouchPhase.Moved)
#endif{m_fingerMoveX = Input.GetAxis("Mouse X");m_fingerMoveY = Input.GetAxis("Mouse Y");}// 滑动量太小,不处理if (Mathf.Abs(m_fingerMoveX) < 0.3f && Mathf.Abs(m_fingerMoveY) < 0.3f)return;OnFruitMove();m_fingerMoveX = 0;m_fingerMoveY = 0;}/// <summary>/// 水果滑动响应/// </summary>private void OnFruitMove(){// TODO}

OnFruitMove中处理水果的交换,交换之前,我们得先封装两个接口。

// FruitSpawner.cs/// <summary>
/// 根据行号列号获取水果对象
/// </summary>
private FruitItem GetFruitItem(int rowIndex, int columIndex)
{if (rowIndex < 0 || rowIndex >= fruitList.Count) return null;var temp = fruitList[rowIndex] as ArrayList;if (columIndex < 0 || columIndex >= temp.Count) return null;return temp[columIndex] as FruitItem;
}/// <summary>
/// 根据行号列号设置水果对象
/// </summary>
private void SetFruitItem(int rowIndex, int columIndex, FruitItem item)
{var temp = fruitList[rowIndex] as ArrayList;temp[columIndex] = item;
}

现在,我们可以实现OnFruitMove函数体了。

// FruitSpawner.cs/// <summary>
/// 水果滑动响应
/// </summary>
private void OnFruitMove()
{if (Mathf.Abs(m_fingerMoveX) > Mathf.Abs(m_fingerMoveY)){//横向滑动var targetItem = GetFruitItem(m_curSelectFruit.rowIndex, m_curSelectFruit.columIndex + (m_fingerMoveX > 0 ? 1 : -1));if (null != targetItem){StartCoroutine(ExchangeAndMatch(m_curSelectFruit, targetItem));}else{m_curSelectFruit = null;}}else if (Mathf.Abs(m_fingerMoveX) < Mathf.Abs(m_fingerMoveY)){//纵向滑动var targetItem = GetFruitItem(m_curSelectFruit.rowIndex + (m_fingerMoveY > 0 ? 1 : -1), m_curSelectFruit.columIndex);if (null != targetItem){StartCoroutine(ExchangeAndMatch(m_curSelectFruit, targetItem));}else{m_curSelectFruit = null;}}
}/// <summary>
/// 交换水果并检测是否可以消除
/// </summary>
IEnumerator ExchangeAndMatch(FruitItem item1, FruitItem item2)
{// 交换水果Exchange(item1, item2);// TODO 检测是否可消除yield return null;
}

要交换水果,我们封装一个交换水果的方法。

// FruitSpawner.cs/// <summary>
/// 交换水果
/// </summary>
private void Exchange(FruitItem item1, FruitItem item2)
{SetFruitItem(item1.rowIndex, item1.columIndex, item2);SetFruitItem(item2.rowIndex, item2.columIndex, item1);int tmp = 0;tmp = item1.rowIndex;item1.rowIndex = item2.rowIndex;item2.rowIndex = tmp;tmp = item1.columIndex;item1.columIndex = item2.columIndex;item2.columIndex = tmp;item1.UpdatePosition(item1.rowIndex, item1.columIndex);item2.UpdatePosition(item2.rowIndex, item2.columIndex);m_curSelectFruit = null;
}

五、运行测试

运行Unity,测试效果如下:

我们可以看到,水果交换的时候,是瞬间换过去的,没有一个滑动的过程。
下一篇讲使用DOTween插件来实现水果的滑动效果。
[点击进入下一篇]

【游戏开发实战】使用Unity制作水果消消乐游戏教程(三):水果拖动与交换逻辑相关推荐

  1. 【游戏开发实战】Unity从零开发多人视频聊天功能,无聊了就和自己视频聊天(附源码 | Mirror | 多人视频 | 详细教程)

    文章目录 一.前言 二.思考问题与解决方案 1.思考问题 2.解决方案 2.1.Unity中如何开启摄像头并对图像进行采样 2.2.图像如何中转给其他客户端 2.3.如何实现清晰度切换 2.4.客户端 ...

  2. 【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

    文章目录 一.前言 二.实现方案 1.无主之地,第一人称视角 2.我之前做的摇杆控制 3.第一人称视角 + 摇杆控制 三.开始实战 1.资源获取:Unity AssetStore 2.Low Poly ...

  3. iOS cocos2d 2游戏开发实战(第3版)---你的第一个游戏!

    2019独角兽企业重金招聘Python工程师标准>>> 随着苹果公司不断地创新与发展,新的iPhone 5.iPad 4以及iPad mini产品相继问世,包括iOS与Xcode在内 ...

  4. 【游戏开发实战】Unity UGUI制作雷达图/天赋图/属性图/能力图,因为太怕痛就全点了防御力

    文章目录 一.前言 二.最终效果 三.原理 四.具体实现 1.雷达图背景图 2.封装UIPolygon.cs 3.制作预设 4.测试脚本RadarTest.cs 五.运行测试 六.结束语 七.附录:U ...

  5. 【游戏开发实战】Unity快速搭建体素风格关卡地图(Tile3D | 我的世界 | Voxel | 场景 | 编辑器)

    文章目录 一.前言 二.本文最终效果 三.MAST工具基本操作 1.工具下载:Modular Asset Staging Tool(MAST) 2.导入工具:Import 3.创建场景:Scene 4 ...

  6. 【游戏开发实战】Unity粒子在UI界面中的裁切、蒙版、层级问题的解决(ScrollView裁切、Mask蒙版、UI粒子层级关系)

    文章目录 一.前言 二.最终效果 三.使用方法 1.引入插件dll 2.制作粒子特效 3.粒子材质球使用UIAdditive.shader 4.将粒子摆放在UI节点下 5.挂UIParticle组件 ...

  7. 【游戏开发实战】Unity使用ShaderGraph配合粒子系统,制作子弹拖尾特效(Fate/stay night金闪闪的大招效果)

    文章目录 一.前言 二.实现原理 三.ShaderGraph 四.图片素材 五.结束 一.前言 点关注不迷路,持续输出Unity干货文章. 嗨,大家好,我是新发,之前写了一篇文章:<Shader ...

  8. 【游戏开发实战】Unity使用ShaderGraph制作一个Loading水球(能量球 | UI | 2D | 特效 | URP)

    文章目录 一.前言 二.ShaderGraph入门 三.ShaderGraph制作2D Loading水球 1.创建Unlit Shader Graph 2.创建一个圆 3.从下到上填充的效果 4.填 ...

  9. 【游戏开发实战】Unity老工程师开始搞Laya了,不想看官方文档,那就看我的教程吧(Laya2.12.0版本 | 案例 | 入门 | 教程)

    文章目录 一.前言 二.我做的Laya案例集合工程 三.LayaAir IDE下载 四.Hello World工程 1.新建空项目 2.新建场景:HelloWorld.scene 3.创建一个Labe ...

  10. 【游戏开发实战】Unity使用ParticleSystem粒子系统模拟药水在血管中流动(粒子碰撞)

    文章目录 一.前言 二.最终效果 三.制作过程 1.导入血管模型 2.血管Shader 3.血管材质球 4.添加碰撞体 5.药水粒子 四.测试效果 五.结束语 一.前言 点关注不迷路,持续输出Unit ...

最新文章

  1. 学会python编程容易吗-Python编程入门难不难
  2. Windows内核HAL相关学习
  3. oracle创建表需要注意什么,Oracle创建表和插入的相关注意事项
  4. elasticsearch 根据条件去除重复值_Excel工作表中的条件格式,不只是查找重复值,还有7种典型用法...
  5. 在ArcGIS调坐标系引发的一系列问题
  6. 想成功创业:要掌握“三笔钱”和“六三一”原则
  7. 多媒体计算机技术19秋作业1,东师多媒体计算机技术19春在线作业1【标准答案】.doc...
  8. 对称加密和非对称加密转载
  9. js-鼠标经过显示下拉菜单
  10. html重置css样式,css样式重置 移动端适配
  11. 解决linux kernel 提交gerrit时,运行checkpatch.pl产生的xxxx64_defconfig not generated by savedefconfig问题
  12. 【自学Python:Day3】放假的心该怎么冷静下来学……
  13. 团购网站大缩水:5058家团购网站 只剩176家
  14. SwiftUI macOS 轻松搭建音乐Radio类App界面(教程含源码)
  15. 同时设置min-height和max-height后el-scrollbar滚动监听失效?
  16. React写响应式官方网站-媒体查询
  17. [转]多模匹配算法与dictmatch实现
  18. 具见--逍遥游 庄子南华(南怀瑾老师)
  19. Linux如何学(新手入门必看)
  20. 计算机辅助设计和辅助教学,计算机辅助设计教学计划

热门文章

  1. Android存储空间讲解
  2. JS获取fileupload文件路径
  3. AI为超级马里奥兄弟创造了游戏新级别——GECCO 2018最佳论文提名
  4. 《2020年IT行业项目管理调查报告》重磅发布!程序员的薪资永远是个谜……
  5. ca证书demo与相关问题
  6. K8S其实很简单-CKA/CKAD报名和考试技巧(转)
  7. 怎样用计算机xp命令修复软件,windowsxp系统怎么修复 windowsxp系统修复方法详解【图文】...
  8. 《从青铜学到王者》Python工程师之Python基础函数
  9. Real-Time Rendering——Chapter 9 Global Illumination
  10. 什么,你被女朋友拉黑了?那还不来试试这款“拉黑复活检查器”?