一、前言
嗨,大家好,我是新发。下班坐地铁的时候,好几次看到其他人在玩消消乐,既然大家都这么喜欢玩,那我就写个Unity
制作水果消消乐的教程吧。
我会根据内容点分成好几篇文章来讲,希望对想学Unity
的同学有所帮助,创作不易,喜欢的同学欢迎关注、点赞、收藏,文章目录如下:
第一篇:生成冰块阵列
第二篇:随机生成水果
第三篇:水果拖动与交换逻辑
第四篇:使用DOTween插件实现水果的滑动效果
第五篇:水果的消除检测,实现消除效果
第六篇:水果下落与新水果生成
第七篇:水果消除特效
第八篇:游戏得分加分效果
第九篇:使用UGUI显示游戏UI
游戏运行效果如下:
最终的Demo
工程已上传到GitHub
,感兴趣的同学可以自行下载下来学习。
GitHub
地址:https://github.com/linxinfa/UnityXiaoXiaoLeDemo
注:我使用的Unity
版本为2020.1.14f1c1
。
本篇讲水果拖动与交换的实现,本篇的效果:
二、事件管理器
我们想要监听水果被点击的事件,那么我们先封装一个事件管理器,可以用来订阅事件和抛出事件。
创建一个EventDispatcher.cs
脚本。
代码如下:
// EventDispatcher.cs
using 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.cs
public 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_STANDALONE
if (Input.GetMouseButton(0))
#else
if(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
插件来实现水果的滑动效果。
[点击进入下一篇]