Delay
Type: Branch (True / False)
Category: FLOW
Executes the True path after N seconds have elapsed since the node was first activated. While waiting, executes the False path. With Repeat=True it re-fires every N seconds (heartbeat mode).
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| Delay (s) | Float (≥0.0) | 1.0 |
Seconds to wait before firing |
| Repeat | Bool | False |
If True, re-fires every N seconds indefinitely |
The Delay data socket can override the property at runtime.
Sockets
| Socket | Direction | Type |
|---|---|---|
| In | Input | Exec |
| Delay | Input | Data (Float) |
| True | Output | Exec (when time expires) |
| False | Output | Exec (while waiting) |
Behavior
First execution frame
Initializes the timer with the target fire time:
self._dly_<name> = Range.logic.getClockTime() + delay
self._dly_<name>_done = False
Subsequent frames
if not self._dly_<name>_done and Range.logic.getClockTime() >= self._dly_<name>:
if repeat:
self._dly_<name> = Range.logic.getClockTime() + delay # re-arm
else:
self._dly_<name>_done = True # fire once only
# → True path
else:
# → False path
Implementation with getClockTime()
Delay uses Range's absolute clock (getClockTime()), not deltaTime accumulation. This means:
- Precision is framerate-independent.
- The target time is fixed at the moment of initialization (first frame the node executes).
- Must be connected to OnUpdate to check the clock every frame.
Repeat = False (default)
Fires True exactly once and then enters silent mode (_done = True). After that, neither True nor False executes — the execution chain ends at that node.
Repeat = True
Every time it fires, it re-arms the timer with currentTime + delay. This produces a periodic heartbeat for as long as the node keeps receiving execution.
Typical usage
Wait 2 seconds before an action
[OnUpdate] → [Delay: 2.0s, Repeat=False]
├── True ──► [BTCustomTask: trigger_cutscene()]
└── False ──► (waiting)
Heartbeat every 0.5 seconds (periodic polling)
[OnUpdate] → [Delay: 0.5s, Repeat=True]
├── True ──► [Enemy Perception] # perception every half second
└── False ──► (nothing between ticks)
Dynamic delay from socket
# In a BT Set Blackboard or directly in the Data socket:
Value: self._bt_bb.get('respawn_time', 3.0)
[Delay: Delay = self._bt_bb['respawn_time']]
Combined with Do Once for a single delayed action
[OnUpdate] → [Do Once]
└── True ──► [Delay: 5.0s, Repeat=False]
└── True ──► [BTCustomTask: start_ambush()]
Blinking light with Flip Flop
[OnUpdate] → [Delay: 0.3s, Repeat=True]
└── True ──► [Flip Flop]
├── True ──► [BTCustomTask: lamp.energy = 5.0]
└── False ──► [BTCustomTask: lamp.energy = 0.0]
Comparison with BT Wait
| Delay | BT Wait |
|---|---|
| FLOW category | AI_BT category |
| Connected directly to OnUpdate | Connected inside BT tree |
| False path = "while waiting" | No waiting path |
| Can repeat with Repeat=True | Restarts only on completion |
Uses getClockTime() |
Accumulates deltaTime() |
Notes
- If
Delay = 0.0: the node may fire on the same frame it initializes (becausegetClockTime() >= getClockTime() + 0is true). To avoid this, use a minimum value of0.016. - With
Repeat=False: once_done = True, the node stops executing both paths — neither True nor False. Execution simply ends at that node. - With
Repeat=True: the timer re-arms from the moment of firing, not from the start. Small framerate variations can cause interval jitter — this is an approximation, not a precision timer. - For multiple independent delays, use Delay nodes with different names.