Action With Force Time Until Next Action

v1.0

An Action which inherits from ActionWithForceTimeUntilNextAction.cs will hold the ActionExecutor execution until it releases. This is used in WaitAction and WaitRandomAction and similar Actions.

This is likely not a very common Action type.

These Actions override ForceTimeUntilNextAction() to return true, which tells the ActionExecutor that the action itself controls when the next action can startβ€”timeBeforeNextAction becomes a forced value derived from the action's internal logic rather than a user-configurable setting.

The release mechanism works through three paths.

Natural completion occurs when the wait condition is satisfied and ExecuteInternal() finishes normallyβ€”for example, when a WaitAction reaches the end of its wait duration or a ConditionalStepActiondetects its condition has become true. The coroutine simply completes its loop and exits, signaling the ActionExecutor that the action is finished and the sequence can proceed to the next action.

Timeout happens when CheckForTimeoutOrRelease() detects that elapsed time has exceeded the configured timeoutDuration. When this occurs, the method triggers the configured TimeoutBehavior (Continue, Stop, Fail, or Restart) and returns true, causing the action to immediately yield break and end the coroutine. This provides a safety mechanism to prevent actions from waiting indefinitely when conditions never resolve.

Forced early release is triggered when ForceRelease() is called externally, which sets the forceEarlyRelease flag to true. The next time CheckForTimeoutOrRelease() is called in the wait loop, it detects this flag and behaves exactly like a timeoutβ€”triggering the configured TimeoutBehavior and returning true to end the coroutine. This allows external systems or user interactions to interrupt wait-style actions on demand.

What to override

ExecuteInternal()

❌ No

β€”

Execution flow is handled by the base class

OnActionStart()

βœ… Yes (primary hook)

Begin waiting logic

Subscribe to events, start timers, initialize state

Release() / ForceComplete()

β­• Optional

Manually unblock the executor

Call when your condition is satisfied

CanExecute()

β­• Optional

Prevent execution under certain conditions

Return false to skip the action

OnRestart()

β­• Optional

Reset state if the action restarts

Must be restart-safe

OnCleanupExecution()

β­• Optional

Unsubscribe / clean up

Always clean up event listeners

Execution lifecycle

  1. ActionExecutor begins the action

  2. Base class blocks execution

  3. OnActionStart() is called

    • set up timers

    • register callbacks

    • begin waiting logic

  4. One of the following occurs:

    • Release() is called manually

    • maximum forced time expires (if configured)

    • action is cancelled

  5. Action unblocks the executor

  6. Cleanup runs

  7. Next action begins

Critical Rules

  • Do not override ExecuteInternal()

    The base class controls timing and blocking behavior.

  • You must call Release() (or allow timeout)

    Forgetting this will permanently stall the action sequence.

  • Always unsubscribe in cleanup

    Event listeners must be removed to avoid leaks and duplicate callbacks.

  • Restarts must be idempotent

    If the action restarts, your OnRestart() logic must not double-subscribe or duplicate state.

Minimal Example

Common mistakes (and how to avoid them)

  • ❌ Overriding ExecuteInternal()

    β†’ Use OnActionStart() instead.

  • ❌ Forgetting to call Release()

    β†’ The action sequence will never continue.

  • ❌ Subscribing in OnActionStart() but not unsubscribing

    β†’ Leads to memory leaks or multiple callbacks on restart.

  • ❌ Using this for timed lerps or animations

    β†’ Use ActionOverTime instead.

Last updated