简体   繁体   中英

Collection with generic and non-generic UnityEvents

I am working on some project in Unity. I have:

[Serializable]
public class ItemAction
{
    [SerializeField]
    private UnityEvent unityEvent;

    public void Perform()
    {
        unityEvent.Invoke();
    }
}

[Serializable]
public class ItemAction<T>
{
    [SerializeField]
    private UnityEvent<T> unityEvent;

    public void Perform(T parameter)
    {
        unityEvent.Invoke(parameter);
    }
}

Also I have this class:

public abstract class Item : MonoBehaviour
{
    [SerializeField]
    private float weight;
    [SerializeField]
    private [collection of item actions] actions;

    public abstract void Use();
    public abstract void Grab(Transform transform);
    public abstract void Drop();
}

How to create collection with mixed both generic and non-generic ItemAction instances (so some actions may require some parameters)? For example:

For unequipped weapons, I can only grab them. For unequipped medkits, I can grab them or use them immediately. For triggers/switchers, I can only use them.

I could probably use an empty interface, but I don't think it's good solution...

Like you said you can create a empty interface or just use a collection of objects, because you will have to cast anyways or you skip the ItemAction class and only use the version which has a parameter and pass null for actions which don't require a parameter (not a "nice" solution either, but this is how the WPF framework does it with the ICommand interface, for example)

But there is an other problem, no matter if you use a interface or object list, you will not be able to show them in the editor (unless you create a custom editor), because the editor doesn't show generic classes.

Since you want to implement the methods in every specific item class inheriting from Item (at least, I guess so since you declared those methods abstract ), I'd go with another route, and implement interfaces for every specific action.

For example:

public interface IItem {
    float Weight { get; set; }
}

public interface IGrabItem : IItem {
    void Grab();
}

public interface IDropItem : IItem {
    void Drop();
}

public interface IUseItem : IItem {
    void Use();
}

public class MedKit : MonoBehaviour, IGrabItem, IUseItem {
    [SerializeField]
    float weight;
    public float Weight {
        get { return weight; }
        set { weight = value; }
    }
    public void Grab() {
        //Your code
    }
    public void Use() {
        //Your code
    }
}

and then you can choose to implement the code directly in every specific item class or use some kind of event to raise them.

Oh, and btw, if possible, avoid using UnityEvent and rely on the standard .NET event , it's faster and creates less overhead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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