Overriding Action Values at Runtime

v1.0

Runtime overrides let you change the values an Action uses without modifying the asset. This is ideal for:

  • Contextual tweaks (e.g., speed up a move if the player is wounded)

  • Live tuning from UI sliders / difficulty systems

  • One-off variations per target

Overrides live on the ActionExecutor’s items, not on the ScriptableObject assets. Changing an override affects the next instantiation of that Action in the sequence. It does not mutate the asset and does not change a currently-running instance.


Prerequisites

  • Action fields must be marked with [CanOverride] or [MustOverride] in the Action class.

  • Your executor must contain an ActionItem that references that Action.

  • You can call the API directly on ActionExecutor, or through convenience wrappers on ActionRunner (shown below).


Override Methods

These are the main methods available on ActionExecutor:

public bool SetFieldOverride<TAction>(string fieldName, object value, bool turnOverrideOn = true) where TAction : Action;
public bool SetFieldOverride(System.Type actionType, string fieldName, object value, bool turnOverrideOn = true);

public bool SetFieldOverrideByActionName(string actionName, string fieldName, object value, bool turnOverrideOn = true);

public int  SetFieldOverrideForAllActions<TAction>(string fieldName, object value, bool turnOverrideOn = true) where TAction : Action;
public int  SetFieldOverrideForAllActions(System.Type actionType, string fieldName, object value, bool turnOverrideOn = true);

public bool SetFieldOverrideEnabled<TAction>(string fieldName, bool enabled) where TAction : Action;
public bool SetFieldOverrideEnabled(System.Type actionType, string fieldName, bool enabled);

public bool SetFieldOverrideEnabledByActionName(string actionName, string fieldName, bool enabled);

public bool TryGetFieldOverride<TAction, TValue>(string fieldName, out TValue value) where TAction : Action;

public bool IsFieldOverrideEnabled<TAction>(string fieldName) where TAction : Action;
public bool IsFieldOverrideEnabled(System.Type actionType, string fieldName);

public bool ClearFieldOverrides<TAction>() where TAction : Action;
public bool ClearFieldOverrides(System.Type actionType);

public List<string> GetOverridableFieldNames<TAction>() where TAction : Action;
public List<string> GetOverridableFieldNames(System.Type actionType);

Example – change a float at runtime

// Assume MoveAction has [CanOverride] float "speed"
// Assume healthMotion field is an ActionExecutor
healthMotion.SetFieldOverride<MoveAction>("speed", 7.5f);          // set & enable
healthMotion.SetFieldOverrideEnabled<MoveAction>("speed", true);   // (optional) explicitly enable

Example – drive from a UI Slider

public class HealthIndicatorSpeedUI : MonoBehaviour
{
    public ActionExecutor healthMotion;
    public void OnSliderChanged(float value)
    {
        healthMotion.SetFieldOverride<MoveAction>("speed", value, true);
    }
}

How overrides are applied

  • When an item executes, the executor instantiates a runtime copy of the Action asset.

  • The item’s ActionOverrides are then applied to that instance before execution:

    // (inside ActionExecutor)
    var actionInstance = Instantiate(actionItem.actionAsset);
    actionItem.overrides.ApplyToAction(actionInstance);
  • This means any change you make to the item’s overrides affects the next instantiation.

  • Already-running instances keep the values they were instantiated with.

If you need the new value to take effect immediately for an always-running sequence, call RestartActions() on the ActionExecutor (or ActionRunner.RestartAlwaysRunningActions()), which will cancel current instances and start fresh using the updated overrides.


Choosing a lookup strategy

Use one of these approaches to target actions:

1

By type

Target the first action of a given Action subclass.

runner.SetFieldOverride<MoveAction>("speed", 8f);
2

By name

Target an action by its exact asset name.

runner.SetFieldOverrideByActionName("Move Action Fast", "speed", 10f);
3

All actions of type

Bulk update every instance of a type in the executor.

int changed = runner.SetFieldOverrideForAllActions<MoveAction>("speed", 6f);

Use “by type” for simple cases, “by name” when multiple variants of the same type exist in your sequence.


Enabling / disabling an override flag

Setting a value does not always imply enabling its override. Use the turnOverrideOn parameter or explicit enable:

runner.SetFieldOverride<MoveAction>("speed", 8f, turnOverrideOn: true);
// or toggle explicitly
runner.SetFieldOverrideEnabled<MoveAction>("speed", true);

Disable to fall back to the Action’s current serialized value:

runner.SetFieldOverrideEnabled<MoveAction>("speed", false);

Inspecting overrides

if (runner.TryGetFieldOverride<MoveAction, float>("speed", out var v))
{
    Debug.Log($"Speed override is {v}");
}

bool isOn = runner.IsFieldOverrideEnabled<MoveAction>("speed");

Get a list of overridable fields for a given Action type:

var fields = runner.GetOverridableFieldNames<MoveAction>();
// e.g., ["speed", "accel", "decel"]

Clearing overrides

runner.ClearFieldOverrides<MoveAction>();                 // first MoveAction in the list
runner.ClearFieldOverrides(typeof(MoveAction));           // non-generic

Timing & restarts (important)

  • Overrides affect future instantiations. If your Action is long-running, you won’t see changes immediately.

  • To apply new overrides to a long-running loop or always-running set:

    • Call ActionExecutor.RestartActions(); or

    • If using ActionRunner: runner.RestartAlwaysRunningActions();

The restart flow updates the run-key, cancels the current sequence, calls OnRestart() on each live Action instance, and then starts fresh using the latest overrides.


Flow diagram (how a new override takes effect)


Tips & gotchas

  • Field names are case-sensitive and must match the serialized field name on the Action.

  • [MustOverride] fields should have an override set before execution; otherwise the Action may fail validation.

  • Use types that match the field (e.g., float for floats). The system attempts conversions when possible, but explicit types are safest.

  • For UI: batch updates and restart once to avoid choppy restarts on every slider tick.

Last updated

Was this helpful?