Home > Software design >  C# class inheritance with ScriptableObject UnityEvents and differently-typed UnityActions
C# class inheritance with ScriptableObject UnityEvents and differently-typed UnityActions

Time:02-19

I'm creating custom UnityEvents for a game. The base class looks like this:

public class EventSO : ScriptableObject
{
    List<UnityAction> listeners = new List<UnityAction>();

    public virtual void Raise() { ... }
    public virtual void AddListener(UnityAction listener) { ... }
    public virtual void RemoveListener(UnityAction listener) { ... }
}

Sometimes I want to pass variables to an event. So I inherit:

public class FloatEventSO : EventSO
{
    List<UnityAction<float>> listeners = new List<UnityAction<float>>();

    public override void Raise(float f) { ... }
    public override void AddListener(UnityAction<float> listener) { ... }
    public override void RemoveListener(UnityAction<float> listener) { ... }
}

My goal is to be able to create ScriptableObjects for individual events I can then assign and invoke: a good example for a Float event might be to communicate game times. So, a ScriptableObject named "OnEveryHalfSec", for example.

The first problem with this approach is that I'll need to make a new class for each combination of variables passed to an event: UnityAction<float, int>, UnityAction<float, string>... and so on.

The second problem is that I can't override the base class like I've shown above; Raise(float) can't override Raise(). So I have to re-define on every class.

Is there a better way to approach this problem?

CodePudding user response:

It looks like you should define

public override void Raise(float f) { ... }

as

 public virtual void Raise(float f) { ... } in the base class

You didn't put "(float f)" in the base class

CodePudding user response:

You want to use generics!

Have a base class like e.g.

public abstract ParameterEventSO<T> : ScriptableObject
{
    List<UnityAction<T>> listeners = new List<UnityAction<T>>();

    public virtual void Raise(T value) 
    {
        foreach(var listener in listeners)
        {
            listener?.Invoke (value);
        }
    }
    public virtual void AddListener(UnityAction<T> listener)
    {
        listeners.Add(listener);
    }
    public virtual void RemoveListener(UnityAction<T> listener)
    {
        listeners.Remove(listener);
    }
}

Now you can have as many derived classes as you need with specific types like e.g.

[CreateAssetMenu]
public class FloatEvent : ParameterEventSO<float> { }

or

[CreateAssetMenu]
public class IntListEvent : ParameterEventSO<List<int>> { }

or also

public class CustomArgs
{
    public Vector3 Position;
    public string Name;
    public DateTime TimeStamp;
}

[CreateAssetMenu]
public class CustomArgsEvent : ParameterEventSO<CustomArgs> { }
  • Related