【问题标题】:Unity ScriptableObject, UnityEvent & GenericObject usageUnity ScriptableObject、UnityEvent 和 GenericObject 使用
【发布时间】:2020-04-03 06:05:32
【问题描述】:

我想将 ScriptableObjectUnityEvent 和 GenericObject 用法结合起来。我的最终目标是创建通用事件和侦听器,然后使用 ScriptableObject 创建特定事件,例如GameObject、int 等,并与各自的侦听器一起处理这些。 这是我到目前为止的代码: EventTemplate.cs

using System.Collections.Generic;
using UnityEngine;

public class EventTemplate<T> : ScriptableObject {
    private List<ListenerTemplate<T>> listeners = new List<ListenerTemplate<T>>();

    public void Raise(T go) {
        for (int i = listeners.Count - 1; i >= 0; i--) {
            listeners[i].OnEventRaised(go);
        }
    }

    public void RegisterListener(ListenerTemplate<T> listener) {
        listeners.Add(listener);
    }

    public void UnregisterListener(ListenerTemplate<T> listener) {
        listeners.Remove(listener);
    }
}

ListenerTemplate.cs

using UnityEngine;
using UnityEngine.Events;

[System.Serializable]
public class ResponseEvent<T> : UnityEvent<T> { }

public class ListenerTemplate<T> : MonoBehaviour {
    //[SerializeField]
    public EventTemplate<T> gameEvent;

    //[SerializeField]
    public ResponseEvent<T> response;

    private void OnEnable() {
        gameEvent.RegisterListener(this);
    }

    private void OnDisable() {
        gameEvent.UnregisterListener(this);
    }

    public void OnEventRaised(T go) {
        response.Invoke(go);
    }
}

现在,当我拥有两种泛型类型时,我为int 类型创建了一个事件和一个侦听器。 这是两个文件:

EventInt.cs

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
public class EventInt : EventTemplate<int> {

}

ListenerInt.cs

using UnityEngine;
using UnityEngine.Events;

[System.Serializable]
public class ResponseInt : ResponseEvent<int> { }

public class ListenerInt : ListenerTemplate<int> {
}

然后我的期望是,一旦我通过编辑器将ListenerInt.cs 添加到特定游戏组件,我将能够访问gameEventresponse,就像我为int 类型定义UnityEvent 一样访问它们。 然而,现实情况是我无法通过编辑器看到/访问gameEventresponse

【问题讨论】:

    标签: c# unity3d scripting


    【解决方案1】:

    Unity 序列化不适用于泛型 T

    您需要为要在 Inspector 中序列化的所有内容显式创建一个继承的非泛型类型。你需要例如一个

    [Serializable] public class IntEvent : UnityEvent<T> { }
    

    为了能够序列化它。


    为了做你想做的事(有点),我会这样做:

    先用interface点赞

    public interface IEventListener<in T>
    {
        void OnEventRaised(T value);
    }
    

    然后让你的ListenerTemplate

    public abstract class ListenerTemplate<T> : MonoBehaviour, IEventListener<T>
    {
        // These have to be provided by the inheritor
        public abstract UnityEvent<T> unityEvent { get; }
        public abstract EventTemplate<T> gameEvent { get; }
    
        private void OnEnable()
        {
            gameEvent.RegisterListener(this);
        }
    
        private void OnDisable()
        {
            gameEvent.UnregisterListener(this);
        }
    
        public void OnEventRaised(T value)
        {
            unityEvent.Invoke(value);
        }
    }
    

    正如您所见,任何从ListenerTemplate&lt;T&gt; 继承的类都必须以某种方式同时提供UnityEvent&lt;T&gt;EventTemplate&lt;T&gt;

    例如

    // The specific scriptable object doesn't change it just inherits
    [CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
    public class EventInt : EventTemplate<int>{ }
    

    // Specific override for the UnityEvent
    [Serializable] public class IntUnityEvent : UnityEvent<int> { }
    
    public class ListenerInt : ListenerTemplate<int>
    {
        [SerializeField] private EventInt eventInt;
        [SerializeField] private IntUnityEvent intUnityEvent;
    
        // override and populate the two abstract properties
        // with the references from the serialized fields
        public override UnityEvent<int> unityEvent => intUnityEvent;
        public override EventTemplate<int> gameEvent => eventInt;
    }
    

    这至少减少了每个继承者和EventTemplateUnityEvent的具体实现对这两个字段的实现开销。

    最后EventTemplate&lt;T&gt; 只需要使用IEventListener 的列表来代替

    public abstract class EventTemplate<TValue> : ScriptableObject
    {
        private readonly List<IEventListener<TValue>> listeners = new List<IEventListener<TValue>>();
    
        public void Raise(TValue go)
        {
            // actually why iterate backwards?
            for (int i = listeners.Count - 1; i >= 0; i--)
            {
                listeners[i].OnEventRaised(go);
            }
        }
    
        public void RegisterListener(ListenerTemplate<TValue> listener)
        {
            listeners.Add(listener);
        }
    
        public void UnregisterListener(ListenerTemplate<TValue> listener)
        {
            listeners.Remove(listener);
        }
    }
    

    【讨论】:

    • Unity 序列化不仅不适用于&lt;T&gt;(Unity 不知道反序列化时要创建什么类型),接口也是如此。
    • 经过测试,对我来说完美无缺,非常感谢!
    • 虽然我无法掌握此解决方案中接口的用途。我尝试删除 IEventListener,它仍然对我有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多