FAQ

Scriptable Variable

It seems like you might need to have every individual variable be its own scriptable object, so for 50 enemies, each enemy’s HP would be 50 different scriptable variables?

Yes. If you don’t want to create the variables beforehand, you can instantiate Scriptable variables at runtime containing the HP and pass it as a reference to your different systems (see Runtime Variables and Runtime Injectors).

However, maybe that is not the best approach. Scriptable variables are best for things that have dependencies with other systems but are singular. It suits more for things like Player HP or a Boss HP as well. For many enemies maybe it's overkill.

Most of the time, if there are a lot of enemies, you don’t really want or need to solve dependency with their individual stats. Instead, the most common use case is to create a Scriptable List of enemies that you can reference anywhere (solving most of your dependencies) and access their stats there.

OnValueChanged is not raised when I modify one field of my custom C# Class, how can raise it?

If you create the following class:

[System.Serializable]
public class Stats
{
    public float Speed;
    public int Damage;
}

And you create a Scriptable Variable of that class:

[CreateAssetMenu(menuName = "Examples/StatsVariable"))]
public class StatsVariable : ScriptableVariable<Stats>
{
    
}

You might expect that modifying any of the fields (e.g., Speed or Damage) would trigger the OnValueChanged event. However, by default, this does not happen. This is because Scriptable Variables check for reference equality, meaning the event is only triggered when the reference itself changes, not when fields inside the object are modified.

Solution: Implement IEquatable<T>

To make this work, your class should implement the IEquatable<Stats> interface. This allows the system to detect changes at the field level rather than relying solely on reference equality.

Here’s how you can modify your Stats class:

[System.Serializable]
public class Stats : IEquatable<Stats>
{
    public float Speed;
    public int Damage;

    public bool Equals(Stats other)
    {
       if (other == null)
          return false;
       return this.Speed == other.Speed && this.Damage == other.Damage;
    }
}

Now, when you register for the OnValueChanged event, changes to individual fields will now trigger the event:

[SerializeField] private StatsVariable _stats;
private void Start()
{
    _stats.OnValueChanged += OnStatsChanged; 
    _stats.Value.Speed += 1; //This will trigger OnValueChanged now!
    _stats.Value = new Stats { Speed = 1, Damage = 1 }; //This too!
}

private void OnStatsChanged(Stats value)
{
     Debug.Log($"Stats changed: Speed = {value.Speed}, Damage = {value.Damage}");
}

private void OnDestroy()
{
    _stats.OnValueChanged -= OnStatsChanged; 
}

Last updated