Unity事件中心机制

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

先看一下事件中心机制的大体设计思路:

类设计图.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

(茕茕无依撸代码,无可奈何负韶华啊)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容