ccruiの博客

ccruiの博客

Unity 事件广播与监听

108
2023-12-08

脚本下载

EventType.cs

EventDispose.cs

广播系统优点

  1. 简化代码结构: 该事件处理系统可以帮助更轻松地管理事件和监听器,从而简化代码结构。可以将不同部分的代码分开,以实现更好的代码组织和可维护性。

  2. 低耦合性: 通过使用事件处理系统,不同的游戏对象或组件可以通过事件通信,而无需直接引用彼此。这降低了代码的耦合性,使得项目更加灵活和可扩展。

  3. 易于扩展: 可以轻松地添加新的事件类型和监听器,以响应不同的游戏事件。这使得项目的功能扩展变得相对容易,无需大规模修改现有代码。

  4. 代码可读性: 使用事件处理系统,可以更清晰地看到哪些部分的代码响应了特定类型的事件,从而提高了代码的可读性和维护性。

调用方法

using UnityEngine;
using Event;

public class TestEventScript : MonoBehaviour
{
    private (EventType, long) listenerIdentifier;

    private void Awake()
    {
        // 在 Awake 方法中添加监听器,并存储返回的唯一标识符
        listenerIdentifier = EventDispose.AddListener<string>(EventType.ShowText, ShowTextMethod);
    }

    private void ShowTextMethod(string text)
    {
        // 当 ShowText 事件被触发时,执行此方法
        Debug.Log($"ShowText Event Triggered: {text}");
    }

    private void OnDestroy()
    {
        // 注意,若使用匿名函数添加监听器,只能使用 方法1 取消监听
    
        // 移除监听器 - 方法1: 使用事件类型和唯一标识符 (建议使用)
        EventDispose.RemoveListener(listenerIdentifier.Item1, listenerIdentifier.Item2);
        
        // 移除监听器 - 方法2: 使用事件类型和监听器操作
        EventDispose.RemoveListener<string>(EventType.ShowText, ShowTextMethod);
    }

    // 调用这个方法来测试广播事件
    public void TestBroadcast()
    {
        // 广播 ShowText 事件
        EventDispose.Broadcast<string>(EventType.ShowText, "Hello, World!");
    }
}

详细代码

EventType 脚本

namespace Event
{
    /// <summary>
    /// 定义可能的事件类型
    /// </summary>
    public enum EventType
    {
        ShowText,
    }
}

EventDispose 脚本

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace Event
{
    /// <summary>
    /// EventDispose类提供了一个用于管理事件监听器的集中式事件处理系统。
    /// </summary>
    public static class EventDispose
    {
        /// <summary>
        /// 存储不同事件类型和ID的事件处理程序的字典。
        /// </summary>
        private static readonly ConcurrentDictionary<EventType, Dictionary<long, Delegate>> EventDic = new();

        /// <summary>
        /// 用于监听器的唯一标识符。
        /// </summary>
        private static long _listenerId = 0;

        /// <summary>
        /// 为指定的事件类型添加新的监听器。监听器与唯一标识符关联。
        /// </summary>
        /// <typeparam name="T">事件负载的类型。</typeparam>
        /// <param name="eventType">要监听的事件类型。</param>
        /// <param name="action">事件触发时要调用的操作。</param>
        /// <returns>包含事件类型和监听器的唯一标识符的元组。</returns>
        public static (EventType, long) AddListener<T>(EventType eventType, Action<T> action)
        {
            long key = Interlocked.Increment(ref _listenerId);
            // 获取或创建事件类型对应的字典
            var value = EventDic.GetOrAdd(eventType, _ => new());

            // 添加到内部字典
            value[key] = action;

            return (eventType, key);
        }

        /// <summary>
        /// 为指定的事件类型添加新的监听器。监听器与唯一标识符关联。
        /// </summary>
        /// <param name="eventType">要监听的事件类型。</param>
        /// <param name="action">事件触发时要调用的操作。</param>
        /// <returns>包含事件类型和监听器的唯一标识符的元组。</returns>
        public static (EventType, long) AddListener(EventType eventType, Action action)
        {
            long key = Interlocked.Increment(ref _listenerId);
            // 获取或创建事件类型对应的字典
            var value = EventDic.GetOrAdd(eventType, _ => new());

            // 添加到内部字典
            value[key] = action;

            return (eventType, key);
        }

        /// <summary>
        /// 将指定类型的事件广播给所有已注册的监听器。
        /// </summary>
        /// <param name="eventType">要广播的事件类型。</param>
        /// <param name="arg">要传递给事件监听器的参数。</param>
        /// <typeparam name="T">参数的类型。</typeparam>
        public static void Broadcast<T>(EventType eventType, T arg)
        {
            if (!EventDic.TryGetValue(eventType, out var value))
            {
                Debug.LogWarning($"不存在事件类型{eventType}的监听,无法广播事件");
                return;
            }

            foreach (var item in value)
            {
                if (item.Value is Action<T> action)
                {
                    action.Invoke(arg);
                }
                else
                {
                    Debug.LogError($"事件类型不匹配,请检查事件类型{eventType}的监听的参数类型是否和广播的参数类型一致");
                }
            }
        }

        /// <summary>
        /// 将指定类型的事件广播给所有已注册的监听器。
        /// </summary>
        /// <param name="eventType">要广播的事件类型。</param>
        public static void Broadcast(EventType eventType)
        {
            if (!EventDic.TryGetValue(eventType, out var value))
            {
                Debug.LogWarning($"不存在事件类型{eventType}的监听,无法广播事件");
                return;
            }

            foreach (var item in value)
            {
                if (item.Value is Action action)
                {
                    action.Invoke();
                }
                else
                {
                    Debug.LogError($"事件类型不匹配,请检查事件类型{eventType}的监听的参数类型是否和广播的参数类型一致");
                }
            }
        }

        /// <summary>
        /// 从指定的事件类型和键中删除监听器。
        /// </summary>
        /// <param name="eventType">要从中删除监听器的事件类型。</param>
        /// <param name="key">与监听器关联的唯一键。</param>
        public static void RemoveListener(EventType eventType, long key)
        {
            if (EventDic.TryGetValue(eventType, out var value) && value.ContainsKey(key))
            {
                value.Remove(key);
            }
        }

        /// <summary>
        /// 从指定的事件类型中删除监听器。
        /// </summary>
        /// <typeparam name="T">事件的类型。</typeparam>
        /// <param name="eventType">事件类型。</param>
        /// <param name="action">监听器操作。</param>
        public static void RemoveListener<T>(EventType eventType, Action<T> action)
        {
            if (EventDic.TryGetValue(eventType, out var value))
            {
                foreach (var item in value)
                {
                    if (item.Value.Equals(action))
                    {
                        value.Remove(item.Key);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 从指定的事件类型中删除监听器。
        /// </summary>
        /// <param name="eventType">事件类型。</param>
        /// <param name="action">监听器操作。</param>
        public static void RemoveListener(EventType eventType, Action action)
        {
            if (EventDic.TryGetValue(eventType, out var value))
            {
                foreach (var item in value)
                {
                    if (item.Value.Equals(action))
                    {
                        value.Remove(item.Key);
                        break;
                    }
                }
            }
        }
    }
}