Do Once
Type: Branch (True / False)
Category: FLOW
Executes the True path only the very first time it is triggered. On all subsequent calls it executes the False path. Useful for one-time initializations or entry effects that must not repeat.
Properties
None — the node only maintains an internal flag.
Sockets
| Socket | Direction | Type |
|---|---|---|
| In | Input | Exec |
| True | Output | Exec (first call) |
| False | Output | Exec (subsequent calls) |
Behavior
if not hasattr(self, '_do1_<name>'):
self._do1_<name> = False
if not self._do1_<name>:
self._do1_<name> = True
# → True path
else:
# → False path
- First call: the flag doesn't exist → created as False → set to True → executes
True. - All subsequent calls: the flag is already True → executes
False.
The state persists as long as the object exists in the scene.
Typical usage
Blackboard initialization
[OnUpdate] → [Do Once]
├── True ──► [BT Set Blackboard: patrol_idx = 0]
│ → [BT Set Blackboard: initialized = True]
└── False ──► [normal logic]
Entry sound when entering a zone
[OnCollision: zone_trigger] → [Do Once]
├── True ──► [BTCustomTask: play_sound('alert')]
└── False ──► (silence)
Loot spawn on death (only once)
[BT Condition: ai_is_dead]
└── True ──► [Do Once]
├── True ──► [BTCustomTask: self.own.scene.addObject('Coin', self.own, 0)]
└── False ──► (already dropped)
Resetting the state
To reset Do Once from code:
# In a BTCustomTask:
var = '_do1_' + 'NodeName'.replace(' ', '_')
if hasattr(self, var):
delattr(self, var)
Or directly:
self._do1_NodeName = False
Notes
- The node name in the editor determines the variable name. Renaming the node is equivalent to resetting its state.
- If two Do Once nodes in the same component share the same name, they will share the variable — always give them unique names.
- The
Falsepath fires from the second call onwards, not only when the node has already been triggered. Connecting toFalseis useful for continuing normal execution without re-doing the initialization.