Wait Conditions¶
Poll game state until a condition is true (or times out). Works in Play Mode only.
Overview¶
Wait conditions let you synchronize your test script with game events — enemy dies, door opens, animation completes — without arbitrary WAIT sleeps. The engine polls at 50ms intervals until the field matches or the timeout expires.
Two entry points: WAIT_UNTIL (DSL inside run_playtest) and wait_until (direct MCP tool). See Playtest Guide for the overall DSL.
Built-in Conditions¶
All comparison operators from the Playtest Guide apply: ==, !=, >, <, >=, <=, contains (substring match on string fields).
Syntax (DSL):
Examples:
WAIT_UNTIL /Enemy|AI|IsDead == true TIMEOUT 10
WAIT_UNTIL /Player|PlayerController|Health < 50
WAIT_UNTIL /Door|Door|State contains open TIMEOUT 8
WAIT_UNTIL /Enemy|AI|IsPatrolling == true TIMEOUT 5 ABORT
Direct tool call:
await wait_until("/Enemy", "AI", "IsDead", "true", timeout=10.0)
await wait_until("/Player", "PlayerController", "Health", "0", negate=True) # wait until != 0
Combining Conditions¶
Combine multiple conditions in a single WAIT_UNTIL with AND or OR. Cannot mix both in the same line.
AND — all conditions must be true:
OR — at least one condition must be true:
Poll interval: 50ms. All sub-conditions evaluated on each tick.
Custom Conditions¶
Use method dispatch to call any public C# method as a condition:
WAIT_UNTIL /Inventory|Inventory|HasItem(sword) == true TIMEOUT 5
WAIT_UNTIL /Player|Movement|DistanceTo(5,0,3) < 1.0 TIMEOUT 8
WAIT_UNTIL /Grid|GridController|IsCellFree(3,5) == true
Syntax: path|Component|MethodName(arg1,arg2) — method invoked via reflection on each poll tick.
A Vector3 argument reads three comma-separated values. Zero-arg methods: MethodName().
Timeout and Error Handling¶
Default timeout: 5 seconds per WAIT_UNTIL step.
Override per step:
Global override — all steps inherit script timeout:
On timeout: | Mode | Behavior | |------|----------| | Default | Step marked FAIL, script continues | | ABORT token | Play Mode stops immediately | | ABORT_ON_FAIL directive | All timeouts stop Play Mode |
# Stop on first timeout
ABORT_ON_FAIL
WAIT_UNTIL /Enemy|AI|IsDead == true TIMEOUT 10
# Stop only this step
WAIT_UNTIL /Player|HP|value > 0 TIMEOUT 5 ABORT
Python buffer: wait_until tool adds 5s to the Unity timeout to prevent the Python side from timing out before Unity responds.
Examples¶
Wait for enemy to die after combat¶
SECTION "Combat"
INVOKE /Enemy HealthComponent TakeDamage 100
WAIT_UNTIL /Enemy|HealthComponent|CurrentHealth == 0 TIMEOUT 5
ASSERT /Enemy|AI|IsDead == true
ASSERT_CONSOLE_CLEAN
Gate on multiple conditions (level transition)¶
SECTION "Level Complete"
WAIT_UNTIL /Player|Score|value >= 500 AND /LevelTimer|Timer|IsRunning == false TIMEOUT 20
ASSERT /UI/WinScreen|Canvas|enabled == true
Fail fast on unexpected death¶
ABORT_ON_FAIL
SECTION "Boss Fight"
INVOKE /Boss BossController StartPhase2
WAIT_UNTIL /Boss|BossController|Phase == 2 TIMEOUT 15 ABORT
ASSERT /Boss|BossController|IsVulnerable == true
WAIT_UNTIL /Boss|BossController|IsDead == true OR /Player|HP|value == 0 TIMEOUT 60
ASSERT /Player|HP|value > 0 AS "player must survive boss"
Tips¶
- Start with
TIMEOUT 30on first run — tighten after you know the baseline. - Use
ABORTon critical gates to stop the entire playtest early. - Combine AND conditions for multi-field checks instead of chaining WAIT_UNTIL lines.
- For GameObjects with slashes in their names, use
\/(e.g.,WAIT_UNTIL /Day\/Night|Health == 100). See Playtest Guide for full path escaping rules.
See also: Playtest Guide — full DSL, path escaping, fire-and-forget pattern, report compression.