我想结合使用ScriptableObject
和UnityEvent
和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
添加到特定的游戏组件中,我将能够以与访问gameEvent
和response
相同的方式访问它们为int类型定义UnityEvent。
但是,现实是我无法通过编辑器查看/访问gameEvent
和response
。
答案 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;
}
这至少将每个继承者以及根据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);
}
}