Hello everyone
I am creating my first game in Unity and I am trying to create an event system that I can rely on, to avoid having direct references between the different parts of my scene.
I use a ScriptableObject
approach for my event system, similar to this what was presented at Unite Austin 2017.
However I am having Serialization issues within the Unity Inspector on my EventListeners
I created these 2 classes:
GameEventListener<T> : MonoBehaviour
using UnityEngine.Events;
public abstract class GameEventListener<T> : GameEventListenerBase where T : struct
{
public GameEvent<T> gameEvent;
public UnityEvent<T> unityEvent;
public void SetGameEvent(GameEvent<T> gameEvent)
{
this.gameEvent = gameEvent;
gameEvent.AddListener(this);
}
void OnEnable()
{
gameEvent?.AddListener(this);
}
void OnDisable()
{
gameEvent?.RemoveListener(this);
}
public void OnEventTriggered(T data)
{
unityEvent.Invoke(data);
}
}
GameEvent<T> : ScriptableObject
using System.Collections.Generic;
using UnityEngine;
public abstract class GameEvent<T> : ScriptableObject where T : struct
{
List<GameEventListener<T>> listeners = new List<GameEventListener<T>>();
virtual public void TriggerEvent(T data)
{
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventTriggered(data);
}
}
public void AddListener(GameEventListener<T> listener)
{
listeners.Add(listener);
}
public void RemoveListener(GameEventListener<T> listener)
{
listeners.Remove(listener);
}
void OnDisable()
{
listeners.Clear();
}
}
I then create my custom derived classes for specific events to be able to pass specific data as structs, e.g.:
OnLivesChanged derived classes example
using UnityEngine;
public class OnLivesChangedListener : GameEventListener<OnLivesChangedData>
{
// This is required; because it is not possible to instantiate a GameEventListener<int> in the inspector (Generics are not supported for ScriptableObjects and MonoBehaviours)
// Empty
}
public struct OnLivesChangedData
{
public int currentLives;
public int maxLives;
}
[CreateAssetMenu(menuName = "ScriptableObjects/Game Events/OnLivesChanged")]
public class OnLivesChangedGameEvent : GameEvent<OnLivesChangedData>
{
// Empty
}
Then I create my custom OnLivesChanged
SO, and add a OnLivesChanged
Listener to the GameObject I want:
I then trigger the method from the component I want and it works perfectly fine.
BUT
When I restart Unity, all the references to the GameEvent ScriptableObjects I added to my listeners are gone, see:
When I change something in the scene and check the scene’s file in git, all the ìd
references of the gameEvents are not set correctly:
I am pretty sure this has something to do with Unity’s serialization of ScriptableObjects with Generic types, but I am really struggling to find a solution that works when I restart Unity
And what annoys me, is that the serialization itself works pretty well at first, until I reopen the project…
I tried to mark the my gameEvent attribute in GameEventListener as “SerializeReference” like this:
[SerializeReference] public GameEvent<T> gameEvent;
But it did not seem to do the trick.
I know that Serialization of Generic fields is a tricky thing in Unity, and I a m a bit overwhelmed with all the infos I find online, so I thought I’d ask myself
Does someone know why this happens, and how I can fix this issue?
Discarding the changes of the scene in git every time I reopen Unity is not really nice
Thanks in advance!