在游戏开发中,为了降低各个模块之间的耦合度,常常会采用属性改变时候触发某个事件来通知其他的物体,而其他物体响应到这个事件,进而改变自身的状态。而为了方便管理,会写一个事件中心来集中管理事件的注册和触发。
先看一下事件中心机制的大体设计思路:

类设计图.png
- EventCenter:事件中心,提供了事件的注册、取消注册、分发事件等功能
- Events:事件的基类,标记事件类型
下面是一个简单EventCenter的写法
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EventSystem
{
public static class EventCenter
{
// 事件字典
private static Dictionary<int, LinkedList<Action<Events>>> es = new Dictionary<int, LinkedList<Action<Events>>>();
public static void Register<T>(Action<Events> events) where T : Events<T>,new()
{
Register(Events<T>.Id, events);
}
private static void Register(int id, Action<Events> events)
{
if (events == null)
{
return;
}
if (!es.ContainsKey(id))
{
es.Add(id, new LinkedList<Action<Events>>());
}
if (!es[id].Contains(events))
{
es[id].AddLast(events);
}
}
public static void UnRegister<T>(Action<Events> events)where T : Events<T>, new()
{
UnRegister(Events<T>.Id, events);
}
private static void UnRegister(int id, Action<Events> events)
{
if (!es.ContainsKey(id))
{
return;
}
es[id].Remove(events);
}
public static void UnRegister(Action<Events> events)
{
if (events == null)
{
return;
}
foreach (LinkedList<Action<Events>> item in es.Values)
{
item.Remove(events);
}
}
public static void Trigger<T>(T t) where T : Events<T>, new()
{
int id = Events<T>.Id;
if (es.ContainsKey(id))
{
foreach (var item in es[id])
{
item?.Invoke(t);
}
}
}
/// <summary>
/// 清理字典中无人监听的事件id
/// </summary>
public static void Clean()
{
foreach (var item in es)
{
if (item.Value.Count == 0)
{
es.Remove(item.Key);
}
}
}
/// <summary>
/// 清理某个事件的所有监听者
/// </summary>
/// <param name="id">事件Id</param>
public static void Clean(int Id)
{
es.Remove(Id);
}
/// <summary>
/// 清除所有id和监听者
/// </summary>
public static void Clear()
{
es.Clear();
}
}
}
再写一个事件基类及其派生类:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EventSystem
{
public abstract class Events
{
public object sender;
//别名
public string Alias { get; set; } = "default";
}
public abstract class Events<T> : Events where T : Events<T>, new()
{
private static int _id = 0;
public static int Id
{
get
{
if (_id == 0)
{
_id = typeof(T).GetHashCode();
}
return _id;
}
}
}
}
之后是优化方面,为了减少频繁的new Events()导致的GC问题,构建了一个EventHelper类来缓存事件(应该有坑,为了尽可能的减少坑,我又在事件Events上添加一个Alias属性,用来为Events实例来指定一个别名,事件发送者在获取事件的时候根据别名+类型来获取,以减少出现意想不到的坑)。为了不会出现坑,还是使用new Events()更保险一点
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EventSystem
{
/// <summary>
/// 用来缓存具体事件 -主要是为了少new事件,0GC或者减少GC
/// </summary>
public static class EventsHelper
{
private static List<Events> pools = new List<Events>();
public static int Count { get { return pools.Count; } }
/// <summary>
/// 获取事件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="alias">事件标记</param>
/// <returns></returns>
public static T GetEvents<T>(string alias, object sender = null) where T : Events<T>, new()
{
T t = null;
for (int i = 0, len = pools.Count; i < len; i++)
{
if (pools[i] is T && pools[i].Alias == alias)
{
t = pools[i] as T;
break;
}
}
if (t == null)
{
t = new T { Alias = alias };
pools.Add(t);
}
t.sender = sender;
return t;
}
}
}
之后是一个测试的脚本,随便挂到一个GameObject上,就可以看到效果
using System;
using System.Collections;
using System.Collections.Generic;
using EventSystem;
using UnityEngine;
// demo
public class AEvents : Events<AEvents>
{
// 事件参数
public string EventName = "A";
}
public class BEvents : Events<BEvents>
{
// 事件参数
public string EventName = "B";
}
public class EventSystemDemo : MonoBehaviour
{
private void Awake()
{
EventCenter.Register<AEvents>(OnAEvents);
EventCenter.Register<BEvents>(OnBEvents);
//EventCenter.UnRegister<AEvents>(OnAEvents);
}
private void Start()
{
EventCenter.Trigger(EventsHelper.GetEvents<AEvents>("A", this));
// 或者
// EventCenter.Trigger(new AEvents() { sender = this, EventName = "A" });
EventCenter.Trigger(EventsHelper.GetEvents<BEvents>("B", this));
Debug.Log("EventHelper Count: " + EventsHelper.Count);
}
private void OnBEvents(Events obj)
{
BEvents ae = obj as BEvents;
Debug.LogError("EventB {sender:" + ae.sender.ToString() + " args:" + ae.EventName);
}
private void OnAEvents(Events obj)
{
AEvents ae = obj as AEvents;
Debug.LogError("EventA {sender:" + ae.sender.ToString() + " args:" + ae.EventName);
}
}
.
ok,第一次写简书,不知道写点啥,自己也是个大坑,就这样吧
等等,按照惯例,最后还要留种。。。
链接:https://pan.baidu.com/s/1da5TNmk0HjDVnaFnfhjZuQ
提取码:lee2