0

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?

2 Answers 2

1

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> { }
Sign up to request clarification or add additional context in comments.

Comments

1

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

4 Comments

That would work if I only ever intended to extend to a float-carrying class, but what if I want a UnityEvent<int>? Then I need a Raise(float) and a Raise(int) in the base class. And so on for every permutation.
Maybe use generics, public virtual void Raise<T>(T value), and when you override, public override void Raise<float>(float f) or something similar to replace the genric value of T
And if I wanted a UnityEvent<int, float>, create a new Raise<T1, T2>(T1 val1, T2 val2) method in the base, adding a new method each time I increase the number of arguments?
Yes you would have to create a new Raise<T> each time with however many arguments you want, like you said Raise<T1, T2>, or Raise<T1, T2, T3>, etc...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.