【问题标题】:pass data through UnityEvent in event based architecture在基于事件的架构中通过 UnityEvent 传递数据
【发布时间】:2019-10-06 06:50:21
【问题描述】:

我来自这篇文章

https://unity3d.com/how-to/architect-with-scriptable-objects

并且想知道如何引发事件并将数据传递给它。因为仅仅听这个事件是不够的。我将尝试解释它以进行复制。

首先我创建了一个新的游戏事件LevelSceneLoadedGameEvent

class LevelSceneLoadedGameEvent : UnityEvent<Scene> { }

当引发此事件时,它应该返回加载的当前场景。接下来我创建了一个静态 EventManager 来管理所有这些事件

public static class EventManager
{
    private static Dictionary<Type, UnityEvent> events = new Dictionary<Type, UnityEvent>()
    {
        {
            typeof(LevelSceneLoadedGameEvent),
            new LevelSceneLoadedGameEvent()
        }
    };

    private static UnityEvent GetEvent<EventType>() where EventType : UnityEvent
    {
        return events[typeof(EventType)];
    }

    public static void SubscribeTo<EventType>(UnityAction listener) where EventType : UnityEvent
    {
        GetEvent<EventType>().AddListener(listener);
    }

    public static void UnsubscribeFrom<EventType>(UnityAction listener) where EventType : UnityEvent
    {
        GetEvent<EventType>().RemoveListener(listener);
    }

    public static void Trigger<EventType>() where EventType : UnityEvent
    {
        GetEvent<EventType>().Invoke();
    }

    public static void Trigger<EventType, FirstParam>(FirstParam firstParam) where EventType : UnityEvent
    {
        GetEvent<EventType>().Invoke(firstParam);
    }
}

其他脚本可以订阅/取消订阅事件并触发这些事件。现在出现了两个属于同一类的问题:

  • 我无法将我的通用 UnityEvents 存储到字典中,因为它只能保存非通用的

最好的重载Add方法'Dictionary.Add(Type, UnityEvent)' 的集合初始值设定项有一些无效参数 (CS1950) [Assembly-CSharp] 参数 2:无法从 'LevelSceneLoadedGameEvent' 到 'UnityEngine.Events.UnityEvent' (CS1503) [程序集-CSharp]

  • 我的带有一个参数的Trigger 方法在Invoke 处失败,因为GetEvent 返回一个非泛型UnityEvent

“Invoke”方法没有重载需要 1 个参数 (CS1501) [汇编-CSharp]

如何修复我的 EventManager 以处理非泛型和泛型 UnityEvents

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    UnityEvent 有几个通用重载,可用于添加参数(例如字符串、整数等)

    基本上,Unity 将序列化的任何数据类型都会显示在检查器中。 因此,您基本上需要使用UnityEvent&lt;string&gt;,例如,如果您希望使用字符串引发事件。

    Here 是使用单参数 UnityEvent 的示例。

    【讨论】:

    • 谢谢。我刚刚发现,但是我不能再使用可编写脚本的对象了,对吧?而且我将不得不创建多个事件类,并且无法再使用检查器..
    • 可以用,没问题。您只需要创建一个继承的实例,即 I.E. SceneLoadedEvent : GameEvent&lt;string&gt;。另外,如果我回答了你的问题,请标记为正确答案,谢谢
    • 我想将其标记为答案,但不幸的是我仍然不清楚。我删除了我的脚本对象并创建了两个抽象类GameEventGameEvent&lt;T&gt;Raise 现在将 T value 作为参数。此外,我添加了 OnEventRaised&lt;T&gt;(T value),但 Unity 的 Invoke 方法不带参数。此外,我不能再通过检查器分配事件,因为它们现在只是类......你介意再解释一下吗?
    • 好的,你需要使用泛型类型来暴露Invoke&lt;T&gt;(见这里:docs.unity3d.com/ScriptReference/Events.UnityEvent_1.html)。关于类,如果您将类标记为 Serializable 并将其私有字段标记为 SerializeField,则可以在探查器上显示任何内容
    • 我更新了我的问题,希望这能澄清一些事情:)
    【解决方案2】:

    我通过将我从字典中获得的 UnityEvent 转换为我想要的泛型类型来修复它,并且在将事件添加到字典时我将它转换为 UnityEvent。此外,我必须使用 UnityEventBase 作为字典,因为它是事件的基类。

    固定代码:

    public static class GameEventManager
    {
        private static Dictionary<Type, UnityEventBase> events = new Dictionary<Type, UnityEventBase>()
        {
            {
                typeof(LevelSceneLoadedGameEvent),
                new LevelSceneLoadedGameEvent()
            }
        };
    
        private static EventType GetEvent<EventType>() where EventType : UnityEventBase
        {
            UnityEventBase unityEvent = events[typeof(EventType)];
            return (EventType)unityEvent;
        }
    
        public static void SubscribeTo<EventType>(UnityAction listener) where EventType : UnityEvent
        {
            GetEvent<EventType>().AddListener(listener);
        }
    
        public static void UnsubscribeFrom<EventType>(UnityAction listener) where EventType : UnityEvent
        {
            GetEvent<EventType>().RemoveListener(listener);
        }
    
        public static void SubscribeTo<EventType, FirstParam>(UnityAction<FirstParam> listener) where EventType : UnityEvent<FirstParam>
        {
            GetEvent<EventType>().AddListener(listener);
        }
    
        public static void UnsubscribeFrom<EventType, FirstParam>(UnityAction<FirstParam> listener) where EventType : UnityEvent<FirstParam>
        {
            GetEvent<EventType>().RemoveListener(listener);
        }
    
        public static void Trigger<EventType>() where EventType : UnityEvent
        {
            GetEvent<EventType>().Invoke();
        }
    
        public static void Trigger<EventType, FirstParam>(FirstParam firstParam) where EventType : UnityEvent<FirstParam>
        {
            GetEvent<EventType>().Invoke(firstParam);
        }
    }
    

    【讨论】:

    • 我不推荐这种模式,因为您将无法创建多个事件 psr 事件类型。例如,如果您希望在多个地方调用不同类型的同一事件,这是不可能的。
    • 嗯,目前我认为这不会发生,但您会建议更好的方法吗? :) 顺便说一句。我会将您的答案标记为正确
    • 有很多方法可以解决这个问题,看来您正在尝试实现一种事件总线(或 pub/sub en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) - 您可以添加某种事件过滤。就我个人而言,我会考虑强制所有事件只有一个参数,该参数继承自 BaseEventMessage(相当于 EventArgs),其中包含可能与每种事件类型相关的附加信息。这意味着您为每种事件类型创建一个唯一的事件数据对象
    猜你喜欢
    • 2020-12-31
    • 2016-09-03
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 2014-05-10
    • 2023-03-09
    • 2021-09-04
    • 1970-01-01
    相关资源
    最近更新 更多