Unity ScriptableObject、UnityEvent 和 GenericObject 的使用

Ang*_*rov 1 c# scripting unity-game-engine

我想结合ScriptableObject使用UnityEventGenericObject 。我的最终目标是创建通用事件和侦听器,然后使用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,当我拥有两种泛型类型时,我为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> {

}
Run Code Online (Sandbox Code Playgroud)

ListenerInt.cs

using UnityEngine;
using UnityEngine.Events;

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

public class ListenerInt : ListenerTemplate<int> {
}
Run Code Online (Sandbox Code Playgroud)

那么我的期望是,一旦我ListenerInt.cs通过编辑器添加到特定的游戏组件,我将能够访问它们gameEvent,并且response以相同的方式访问它们,就像我为 int 类型定义 UnityEvent 一样。然而,现实是我既无法看到/访问也gameEvent无法response通过编辑器看到/访问。

der*_*ugo 5

Unity 序列化不适用于泛型T

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

[Serializable] public class IntEvent : UnityEvent<T> { }
Run Code Online (Sandbox Code Playgroud)

为了能够将其序列化。


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

首先使用interface喜欢

public interface IEventListener<in T>
{
    void OnEventRaised(T value);
}
Run Code Online (Sandbox Code Playgroud)

然后让你的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,任何继承自的类都ListenerTemplate<T>必须以某种方式提供UnityEvent<T>EventTemplate<T>

所以例如

// 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>{ }
Run Code Online (Sandbox Code Playgroud)

// 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;
}
Run Code Online (Sandbox Code Playgroud)

这至少减少了每个继承者以及根据EventTemplate和的具体实现对这两个字段的实现开销UnityEvent

最后EventTemplate<T>只需要使用一个列表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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述