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.
Why OnValueChanged doesn't trigger when you modify a field in a custom C# class ? (And how to fix it)
If you define a class like this:
[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, this doesn't happen by default due to two important reasons:
ScriptableVariable
only triggers the event when the reference changesThe default implementation uses references to compare the current and new values. If the reference doesn’t change, the comparison considers it “equal” — even if internal fields were modified.
OnValueChanged
is only fired by the.Value
setterWhen you mutate a field directly, you're not reassigning
.Value
, so the setter isn't called and no event is fired.
For example:
var old = _stats.Value;
old.Speed = 10; // ❌ No setter call → no event
_stats.Value = old; // ✅ Setter call → event fires
âś… How to Trigger the Event on Field Mutation
If you're modifying a field like this:
_stats.Value.Speed += 1;
…the event won’t fire unless you explicitly notify the variable. There are two ways to fix this:
1. Reassign a New Object to .Value
.Value
You can manually assign a new instance of the class to .Value
with the updated data:
var copy = _stats.Value;
_stats.Value = new Stats { Speed = 10, Damage = copy.Damage }; // âś… Fires event
This works because you're assigning a new reference, and the setter compares the new value to the old one.
âś… 2. Call NotifyChanged()
After Mutation
NotifyChanged()
After MutationManually tell the Scriptable Variable that its value has changed:
_stats.Value.Speed = 10;
_stats.NotifyChanged(); // âś… Manually fires the event
Full Example:
[SerializeField] private StatsVariable _stats;
private void Start()
{
_stats.OnValueChanged += OnStatsChanged;
// ✅ Assignment via setter – event fires:
var copy = _stats.Value;
_stats.Value = new Stats { Speed = 10, Damage = copy.Damage };
// âś… or manually notifying ValueChanged after setting a field
_stats.Value.Speed = 10;
_stats.ValueChanged();
}
private void OnStatsChanged(Stats value)
{
Debug.Log($"Stats changed: Speed = {value.Speed}, Damage = {value.Damage}");
}
private void OnDestroy()
{
_stats.OnValueChanged -= OnStatsChanged;
}
đź’ˇ Optional: Use struct
Instead of class
struct
Instead of class
If you define your custom class can be defined as a struct instead
, every field mutation will require reassignment — which naturally triggers the setter. This avoids manual NotifyChanged()
calls and helps reduce heap allocations.
Last updated