简体   繁体   English

Unity ScriptableObject、UnityEvent 和 GenericObject 的使用

[英]Unity ScriptableObject, UnityEvent & GenericObject usage

I would like to combine ScriptableObject along with UnityEvent and GenericObject usage.我想将ScriptableObjectUnityEvent和 GenericObject 的用法结合起来。 My ultimate goal is to create generic event and listener and then use ScriptableObject to create specific events eg GameObject, int and etc. and handle these with respective listeners.我的最终目标是创建通用事件和侦听器,然后使用 ScriptableObject 创建特定事件,例如 GameObject、int 等,并使用相应的侦听器处理这些事件。 Here is the code I have so far: EventTemplate.cs这是我到目前为止的代码: 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监听模板.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);
    }
}

Now, when I have both generic types, I created one Event and one Listener for int type.现在,当我拥有这两种泛型类型时,我为int类型创建了一个事件和一个侦听器。 These are two files:这是两个文件:

EventInt.cs 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> {

}

and ListenerInt.csListenerInt.cs

using UnityEngine;
using UnityEngine.Events;

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

public class ListenerInt : ListenerTemplate<int> {
}

then my expectation was, once I add ListenerInt.cs to specific game component via Editor, I will able to access gameEvent and response in the same fashion I can access them as if I define UnityEvent for int type.然后我的期望是,一旦我通过编辑器将ListenerInt.cs添加到特定的游戏组件,我将能够以与访问它们相同的方式访问gameEventresponse ,就像我为 int 类型定义 UnityEvent 一样。 However, the reality is that I cannot see / access neither gameEvent nor response via the Editor.然而,现实是我无法通过编辑器查看/访问gameEventresponse

Unity serialization doesn't work on generics T . Unity 序列化不适用于泛型T

you would need to explicitely create an inherited non-generic type for everything you want to serialize in the Inspector.您需要为要在 Inspector 中序列化的所有内容明确创建继承的非泛型类型。 You would need eg a你需要例如一个

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

in order to be able to serialize it.为了能够序列化它。


In order to do what you want (kind of) I would do this:为了做你想做的(有点)我会这样做:

First use an interface like首先使用类似的interface

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

Then make your ListenerTemplate然后让你的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);
    }
}

As you can see any class inheriting from ListenerTemplate<T> will have to somehow provide both the UnityEvent<T> and the EventTemplate<T> .正如您所看到的,任何从ListenerTemplate<T>继承的类都必须以某种方式同时提供UnityEvent<T>EventTemplate<T>

So eg所以例如

// 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>{ }

and

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

This at least reduces the implementation overhead to these two fields for every inheritor and according specific implementations of EventTemplate and UnityEvent .根据EventTemplateUnityEvent具体实现,这至少减少了每个继承者对这两个字段的实现开销。

Finally the EventTemplate<T> just has to use a list of IEventListener instead最后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);
    }
}

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM