Search Unity

Question ScrObj-based event architecture and passing arguments

Discussion in 'Scripting' started by PascalTheDog, Sep 16, 2021.

  1. PascalTheDog

    PascalTheDog

    Joined:
    Mar 16, 2015
    Posts:
    86
    Hey,

    You most likely known about Ryan Hipple's Unity Austin 2017 talk already. The bit about a UnityEvent + Scriptable Object-based event architecture is really good. It's a great way to get rid of bothersome dependencies and to enable non-programmers. Here is the bit in question. It all boils down to this:

    Code (CSharp):
    1. public class GameEvent : ScriptableObject
    2. {
    3.     private List<GameEventListener> listeners = new List<GameEventListener>();
    4.  
    5.     public void RegisterListener(GameEventListener listener) => listeners.Add(listener);
    6.     public void UnregisterListener(GameEventListener listener) => listeners.Remove(listener);
    7.  
    8.     public void Raise()
    9.     {
    10.         for (int i = listeners.Count - 1; i >= 0; --i)
    11.             [B]listeners[i].RaiseEvent();[/B]
    12.     }
    13. }
    Code (CSharp):
    1. public class GameEventListener : MonoBehaviour
    2. {
    3.     public GameEvent gameEvent;
    4.     [B]public UnityEvent response;[/B]
    5.  
    6.     private void OnEnable() => gameEvent.RegisterListener(this);
    7.     private void OnDisable() => gameEvent.UnregisterListener(this);
    8.     public void RaiseEvent() => response.Invoke();
    9. }
    There is a big drawback though: that framework doesn't allow you to pass arguments. You can only raise "plain" events that cannot pass any value or reference — which rapidly proves a frustrating limitation. You would most likely want a Player_OnHealthChange event to pass an int representing the player's current HP so you can update the health bar, or an Inventory_OnItemGet event to pass a string representing the item name so you can display what the player has just found. And as your project becomes more and more sophisticated, you'll eventually want to pass your own structs or classes.

    Of course, you can do that using UnityEvent<int> or UnityEvent<string> or UnityEvent<MyClass> instead of the plain UnityEvent, and having RaiseEvent pass the argument (see the lines in bold). The problem? As far as I know, that would mean creating another set of classes that are almost entirely identical to GameEvent & GameEventListener... for every type of argument you wish to be able to pass! Needless to say, that would get out of hand fast. Is there any way to create a generic GameEvent that could pass anything as an argument?

    Assistance would be greatly appreciated. Cheers!
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    I'm pretty sure it's not possible because you can't create Scriptable Objects with generic parameters as concrete assets in your project. I'm using a similar structure and yes, it is annoying, but you only have to do it once and rarely add new event definitions after the basics. It's even what the Unity devs use: https://github.com/UnityTechnologies/open-project-1