广东双非一本的大三小白,计科专业,想在制作毕设前夯实基础,毕设做出一款属于自己的游戏!
简单的一个事件中心系统 EventCenter.cs
using System.Collections.Generic; using UnityEngine.Events; public class EventCenter : BaseSingleton<EventCenter> { // key对应事件的名字,value对应的是监听这个事件对应的委托函数【们】 private Dictionary<string, UnityAction> eventDic = new Dictionary<string, UnityAction>(); // 添加事件监听 public void AddEventListener(string name, UnityAction action) { if (eventDic.ContainsKey(name)) eventDic[name] += action; else eventDic.Add(name, action); } // 事件触发 public void EventTrigger(string name) { if (eventDic.ContainsKey(name)) eventDic[name].Invoke(); } // 一定要移除,不然可能会发生内存泄漏 public void RemoveEventListen(string name, UnityAction action) { if (eventDic.ContainsKey(name)) eventDic[name] -= action; } // 清空事件中心,主要用在场景切换时 public void Clear() { eventDic.Clear(); } }测试,比如怪物死亡,对应的玩家,分数事件之类的发生变化(以下代码均放到游戏物体上才可触发)
public class Monster : MonoBehaviour { public string name = "123"; private void Start() { Dead(); } void Dead() { Debug.Log("怪物死亡"); // 触发事件 EventCenter.GetInstance().EventTrigger("MonsterDead"); } } public class Player : MonoBehaviour { private void Start() { EventCenter.GetInstance().AddEventListener("MonsterDead", MonsterDeadDo); } private void OnDestroy() => EventCenter.GetInstance().RemoveEventListen("MonsterDead", MonsterDeadDo); private void MonsterDeadDo() => Debug.Log("玩家得到奖励"); } public class Task : MonoBehaviour { private void Start() { EventCenter.GetInstance().AddEventListener("MonsterDead", TaskWaitMonsterDeadDo); } private void OnDestroy() => EventCenter.GetInstance().RemoveEventListen("MonsterDead", MonsterDeadDo); public void TaskWaitMonsterDeadDo() => Debug.Log("任务 记录"); }为什么要在Destroy中移除事件呢,比如玩家死亡,怪物在玩家之后死亡,他要执行玩家里的那个方法,所以玩家死亡的时候在内存里不会真正消失,因为怪物跟玩家有所关联,这就可能造成内存泄漏
为了让事件中心系统更加通用(处理多参数方法),我们需要升级我们的 EventCenter.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class EventCenter : BaseManager<EventCenter> { // key对应事件的名字,value对应的是监听这个事件对应的委托函数【们】 private Dictionary<string, UnityAction<object>> eventDic = new Dictionary<string, UnityAction<object>>(); // 添加事件监听 public void AddEventListener(string name, UnityAction<object> action) { if (eventDic.ContainsKey(name)) { eventDic[name] += action; } else { eventDic.Add(name, action); } } // 事件触发 public void EventTrigger(string name, object info) { if (eventDic.ContainsKey(name)) { eventDic[name]?.Invoke(info); } } public void RemoveEventListen(string name, UnityAction<object> action) { if (eventDic.ContainsKey(name)) { eventDic[name] -= action; } } // 清空事件中心,主要用在场景切换时 public void Clear() { eventDic.Clear(); } }因为用到object,所以会有装箱拆箱的问题,不过为了更加通用,只能在使用的时候尽量不传递值类型,就不会发生装箱拆箱问题了
Monster.cs
void Dead() { Debug.Log("怪物死亡"); // 触发事件 EventCenter.GetInstance().EventTrigger("MonsterDead", this); }可以看到这里吧this传进去,那么怎么用呢?
修改一下player的代码,其他同理
public class Player : MonoBehaviour { private void Start() { EventCenter.GetInstance().AddEventListener("MonsterDead", MonsterDeadDo); } private void MonsterDeadDo(object info) { Debug.Log("玩家得到奖励:" + (info as Monster).name); } private void OnDestroy() { EventCenter.GetInstance().RemoveEventListen("MonsterDead", MonsterDeadDo); } }可以把这个object当成Monster然后调用name
现在的事件中心模块因为有object,会涉及到装箱拆箱问题,所以会有一定的性能损耗,所以存在优化的空间
涉及到里氏转换原则:基类装子类
灵活使用里氏转换原则,灵活运用空接口与泛型的配合,为什么要用空接口呢,因为如果要穿个T过去,就必须是 public class EventCenter<T> ,但EventCenter只有一个,比如传入了GameObject作为T就不能再改了
关于实现无参方法,依然要继承空接口,创建同名类即可(最好看下面代码)
先创建接口并对接口进行继承 EventInfo.cs
using UnityEngine.Events; public interface IEventInfo { } // 实现一个参数 public class EventInfo<T> : IEventInfo { public UnityAction<T> actions; public EventInfo(UnityAction<T> action) { actions += action; } } // 实现无参 public class EventInfo : IEventInfo { public UnityAction actions; public EventInfo(UnityAction action) { actions += action; } }升级 EventCenter.cs ,还添加了不需要参数触发的(重载)也行
using System.Collections.Generic; using UnityEngine.Events; public class EventCenter : BaseSingleton<EventCenter> { // key对应事件的名字,value对应的是监听这个事件对应的委托函数【们】 private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>(); // 添加事件监听,一个参数的 public void AddEventListener<T>(string name, UnityAction<T> action) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo<T>).actions += action; else eventDic.Add(name, new EventInfo<T>(action)); } // 添加事件监听,无参数的 public void AddEventListener(string name, UnityAction action) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo).actions += action; else eventDic.Add(name, new EventInfo(action)); } // 事件触发,无参的 public void EventTrigger(string name) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo).actions?.Invoke(); } //事件触发,一个参数的 public void EventTrigger<T>(string name, T info) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo<T>).actions?.Invoke(info); } //移除监听,无参的 public void RemoveEventListener(string name, UnityAction action) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo).actions -= action; } //移除监听,一个参数的 public void RemoveEventListener<T>(string name, UnityAction<T> action) { if (eventDic.ContainsKey(name)) (eventDic[name] as EventInfo<T>).actions -= action; } // 清空事件中心,主要用在场景切换时 public void Clear() { eventDic.Clear(); } }使用,还是拿之前的player举例
public class Player : MonoBehaviour { private void Awake() { EventCenter.GetInstance().AddEventListener<Monster>("MonsterDead", MonsterDeadDo); } private void MonsterDeadDo(Monster info) { Debug.Log("玩家得到奖励:" + info.name); } private void OnDestroy() { EventCenter.GetInstance().RemoveEventListen<Monster>("MonsterDead", MonsterDeadDo); } }