Search code examples
c#unity-game-engineeventsscriptable-object

UnityEvent inside Scriptable Objects


I think i run into a pretty basic problem. Basically im trying to create a Scriptable Object class called GameEvent. This SO contains a UnityEvent. The idea is to associate a bunch of methods to it from different scripts in the scene.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

[CreateAssetMenu(fileName = "GameEvent", menuName = "CardEmpires/GameEvent", order = 1)]
public class GameEventSO : ScriptableObject
{

    [SerializeField] UnityEvent unityEvent;

GameOver event (GameEventSO) Now, oviously im missing something about how scriptable objects work becouse i'm not able to drag gameObjects, or scripts to the unity event list. I've tried creating a GameObject list inside a Scriptable Object as well, and i run into the same problem: i cannot drag gameObjects from the scene. Does anyone now a way around this? am i missing something? Are scriptable objects not design to do this? Thank u!


Solution

  • As already mentioned in the comments

    Assets can't refer to scene objects

    The idea with such event SOs is actually the other way round. You subscribe via code but can reference the SO asset in whichever scene objects where you need it.

    You have Subscribers of the event like e.g.

    public class Subscriber : MonoBeuaviour 
    {
        [SerializeField] GameEventSO someEvent;
    
        void Awake()
        {
            someEvent.unityEvent.AddListener(Something);
        }
    
        private void Something()
        {
            Debug.Log("Hello!");
        }
    
        void OnDestroy()
        {
            someEvent.unityEvent.RemoveListener(Something);
        }
    }
    

    And then somewhere you have an invoker

    public class Invoker : MonoBehaviour
    {
        [SerializeField] GameEventSO someEvent;
    
        void Update()
        {
            if(Input.GetKeyDown(KeyCode.Space))
            {
                someEvent.unityEvent.Invoke();
            }
        }
    }
    

    For this to work at all though you do not want/need to expose the event in the Inspector but rather have it

    public UnityEvent unityEvent;
    

    HOWEVER, personally I would not use UnityEvent for this at all. This is really only when you want to expose it in the Inspector.

    Rather use a proper

    public event Action Event;
    
    public void Invoke()
    {
        Event?.Invoke();
    }
    

    and un/subscribe via

    someEvent.Event -= Something;
    someEvent.Event += Something;
    

    which is more the c# way without Unity's special wrapper