Unity C# Event 自定义事件系统

冰封百度

注:转载请标明原文地址:https://segmentfault.com/a/11...

在Unity中 用C#实现自定义的事件系统
参考自《Advanced C# Messenger》 实现一个高效的,独立的,可复用的事件系统。这种方式实现的事件,GC会很低,值类型0GC。

注:《Advanced C# Messenger》中使用泛型Delegate实现的事件系统,发送事件参数没有类型转换,实现0GC的效果,但是IL2CPP后,所有泛型都生成了对应的静态类型代码,用的越多生成的代码体积越庞大,运行时占用的内存也越多,相当于用空间换了时间。但当类型过于丰富时,展开的代码太过庞大,性价比急剧降低。所以个人弃用这种实现方式,转为传统用法,Advanced只做参考学习用,下面为传统实现。

用法:

// 定义事件类型
// 用枚举类型定义, 强制约定,避免出现因为string或int值打错产生的错误
public enum EventType {
    ShowMainCamera,
    HideMainCamera
}

// 添加事件监听器
EventUtil.AddListener(EventType, OnCallback)

// 移除事件监听器
EventUtil.RemoveListener(EventType, OnCallback)

// 发送事件(无参数)
EventUtil.SendMessage(EventType)

// 发送事件(1个参数)
EventUtil.SendMessage(EventType, "arg1")

// 发送事件(多个参数)
EventUtil.SendMessage(EventType, new string[]{"arg1", "arg2", "arg3"})

// 事件回调(无参数)
private void OnCallback(object arg) {
    print(arg); // null
}

// 事件回调(1个参数)
private void OnCallback1(object arg) {
    print(arg); // "arg1"
}

// 事件回调(多个参数)
private void OnCallbackN(object arg) {
    string[] args = (string[])arg;
    print(args); // System.String[]
}

测试代码

EventDemo.cs

using UnityEngine;

/// <summary>
/// 事件测试Demo
/// ZhangYu 2019-12-25
/// </summary>
public class EventDemo : MonoBehaviour {

    // 定义事件类型
    public enum EventType {
        Arg0,
        Arg1,
        ArgN
    }

    private void Start() {
        AddListeners();
        SendMessages();
    }

    private void OnDestroy() {
        RemoveListeners();
        EventUtil.Clear();
    }

    // 添加事件监听器
    private void AddListeners() {
        EventUtil.AddListener(EventType.Arg0, OnCall0);
        EventUtil.AddListener(EventType.Arg1, OnCall1);
        EventUtil.AddListener(EventType.ArgN, OnCallN);
    }

    // 移除事件监听器
    private void RemoveListeners() {
        EventUtil.RemoveListener(EventType.Arg0, OnCall0);
        EventUtil.RemoveListener(EventType.Arg1, OnCall1);
        EventUtil.RemoveListener(EventType.ArgN, OnCallN);
    }

    // 发送事件
    public void SendMessages() {
        EventUtil.SendMessage(EventType.Arg0);
        EventUtil.SendMessage(EventType.Arg1, "arg1");
        EventUtil.SendMessage(EventType.ArgN, new string[] { "arg1", "arg2", "arg3" });
    }

    private void OnCall0(object arg) {
        print("无参数:" + arg);
    }

    private void OnCall1(object arg) {
        print("1个参数:" + arg);
    }

    private void OnCallN(object arg) {
        string[] args = (string[])arg;
        print("多个参数:" + args);
    }

}

实现效果:
实现效果.png

事件工具

事件工具
EventUtil.cs
对EventSender做了一个简单封装,静态工具类,方便使用。

using System;

/// <summary>
/// 事件工具
/// <para>ZhangYu 2019-12-25</para>
/// </summary>
public static class EventUtil {

    /// <summary> 事件发送器 </summary>
    private static EventSender<Enum, object> sender = new EventSender<Enum, object>();

    /// <summary> 添加事件监听器 </summary>
    /// <param name="eventType">事件类型</param>
    /// <param name="eventHandler">事件处理器</param>
    public static void AddListener(Enum eventType, Action<object> eventHandler) {
        sender.AddListener(eventType, eventHandler);
    }

    /// <summary> 移除事件监听器 </summary>
    /// <param name="eventType">事件类型</param>
    /// <param name="eventHandler">事件处理器</param>
    public static void RemoveListener(Enum eventType, Action<object> eventHandler) {
        sender.RemoveListener(eventType, eventHandler);
    }

    /// <summary> 是否已经拥有该类型的事件监听器 </summary>
    /// <param name="eventType">事件类型</param>
    public static bool HasListener(Enum eventType) {
        return sender.HasListener(eventType);
    }

    /// <summary> 发送事件 </summary>
    /// <param name="eventType">事件类型</param>
    public static void SendMessage(Enum eventType) {
        sender.SendMessage(eventType, null);
    }

    /// <summary> 发送事件 </summary>
    /// <param name="eventType">事件类型</param>
    public static void SendMessage(Enum eventType, object eventArg) {
        sender.SendMessage(eventType, eventArg);
    }

    /// <summary> 清理所有事件监听器 </summary>
    public static void Clear() {
        sender.Clear();
    }

}

事件发送器
EventSender.cs
核心类,功能都在这里实现的

using System;
using System.Collections.Generic;

/// <summary>
/// 事件发送器
/// <para>ZhangYu 2019-12-25</para>
/// <para>Blog:https://segmentfault.com/a/1190000018391937</para>
/// </summary>
public class EventSender<TKey, TValue> {

    /// <summary> 事件表 </summary>
        private Dictionary<TKey, Action<TValue>> dict = new Dictionary<TKey, Action<TValue>>();

        /// <summary> 添加事件监听器 </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventHandler">事件处理器</param>
        public void AddListener(TKey eventType, Action<TValue> eventHandler) {
            Action<TValue> callbacks;
            if (dict.TryGetValue(eventType, out callbacks)) {
                dict[eventType] = callbacks + eventHandler;
            } else {
                dict.Add(eventType, eventHandler);
            }
        }

        /// <summary> 移除事件监听器 </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventHandler">事件处理器</param>
        public void RemoveListener(TKey eventType, Action<TValue> eventHandler) {
            Action<TValue> callbacks;
            if (dict.TryGetValue(eventType, out callbacks)) {
                callbacks = (Action<TValue>)Delegate.RemoveAll(callbacks, eventHandler);
                if (callbacks == null) {
                    dict.Remove(eventType);
                } else {
                    dict[eventType] = callbacks;
                }
            }
        }

        /// <summary> 是否已经拥有该类型的事件监听器 </summary>
        /// <param name="eventType">事件名称</param>
        public bool HasListener(TKey eventType) {
            return dict.ContainsKey(eventType);
        }

        /// <summary> 发送事件 </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventArg">事件参数</param>
        public void SendMessage(TKey eventType, TValue eventArg) {
            Action<TValue> callbacks;
            if (dict.TryGetValue(eventType, out callbacks)) {
                callbacks.Invoke(eventArg);
            }
        }

        /// <summary> 清理所有事件监听器 </summary>
        public void Clear() {
            dict.Clear();
        }

}

核心类是EventSender,用来管理事件,可以复用,这样可以做事件隔离,各系统用不同的EventSender,互不影响。EventUtil是对EventSender的封装,封装成静态工具类,方便使用。相同的思路,可以封装成UIEventUtil、InputEventUtil、SocketEventUtil等等...

阅读 8.6k

冰封百度的学习笔记
程序生涯中的技术整理

Unity游戏程序员一枚。生命不息,学习不止。

193 声望
36 粉丝
0 条评论

Unity游戏程序员一枚。生命不息,学习不止。

193 声望
36 粉丝
文章目录
宣传栏