What I'm trying to do:
A ScriptableObject class to hold a single variable that can be subscribed to in an observer pattern to receive notifications when the value changes.
My intent is to have things like a UI display update when whatever they display changes, without having to manually trigger an event on every change.
Additionally, I want my class to have three features:
- Use try/catch in order to really decouple things and not make all listeners fail just because one did
- Have the option to log stuff for debugging
- Show the list of currently active observers in the inspector
I thought that's a few lines of code with Delegates, but it turns out nope, that simply doesn't work.
My first naive iteration was this:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
public event Action<float> get;
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
get?.Invoke(value);
}
}
}
My second iteration, which works functionally, but doesn't show me the list of observers in the inspector, was this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
[SerializeField] List<UnityAction<float>> listeners = new List<UnityAction<float>>();
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
foreach (UnityAction<float> action in listeners) {
action.Invoke(value);
}
}
}
public void AddListener(UnityAction<float> func) => listeners.Add(func);
public void RemoveListener(UnityAction<float> func) => listeners.Remove(func);
}
My third iteration, replacing UnityAction with UnityEvents, appears to work at first glance (the list shows up in the Inspector), but it never updates the list and it's always shown as empty, even though again functionally it works:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Sirenix.OdinInspector;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
public UnityEvent<float> listeners = new UnityEvent<float>();
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
listeners?.Invoke(value);
}
}
}