Unity ScriptableObject,UnityEvent和GenericObject的用法

时间:2019-12-10 05:19:19

标签: c# unity3d scripting

我想结合使用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

1 个答案:

答案 0 :(得分:1)

统一序列化不适用于泛型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<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>{ }

// 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<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);
    }
}

enter image description here