← BlogScenario DSL (OpenSCENARIO)· 2/4

Anatomy of a Custom OSC Action — setup, tick, terminate

How domain actions like robot spawning, firing, and classification are built. The internal structure of a single custom action called by an OSC scenario — setup (once), update (every tick), terminate (cleanup).

~4 min
OpenSCENARIOpy_treesactionbehaviorextensibility

Anatomy of a Custom OSC Action

When an OSC scenario compiles into a Behavior Tree, each behavior — spawn, move, fire, classify — becomes a leaf node in the tree. So what does that leaf actually look like inside? You need to understand this structure to add a new domain action.

Three Phases of an Action

Every action is a leaf behavior with a setup → per-tick update → terminate lifecycle.

Rendering diagram…
  • setup / initialise — Runs once when the action starts. Prepares handles and publishers; reads parameters passed from the OSC scenario.
  • update — Called every tick. Advances one step and returns SUCCESS/RUNNING/FAILURE. Typical pattern: "RUNNING until goal reached, then SUCCESS."
  • terminate — Called when the action ends or is aborted. Releases resources.

For example, a "move to goal" action: setup reads the target; update advances one step per tick and returns RUNNING; on arrival it returns SUCCESS; terminate does cleanup.

Here is the skeleton of a single action in code.

# every OSC action = setup -> update(every tick) -> terminate
class MoveToAction(Behavior):
    def setup(self):     self.target = self.params["goal"]   # once
    def update(self):                                        # every tick
        step_toward(self.target)
        return SUCCESS if arrived() else RUNNING             # RUNNING until arrival
    def terminate(self): release()                           # cleanup

Parameters Come in as SI Types

OSC borrows its language and type system from ASAM, so parameters delivered to an action carry units — speed: 5.0mps, delay: 3.0s, angle: 90.0deg. The action receives these SI-typed values and operates on them.

💡 New action = one new leaf — To add a new domain behavior (e.g., "deploy smoke screen," "sonar classification"), implement one leaf with this setup/update/terminate structure and register it so the OSC parser can find it. Scenario authors then call it by name from OSC. Adding behaviors without touching the engine is the extension point that keeps the system open.

Common Pitfalls

⚠️ Don't try to complete an asynchronous operation in one tick — If you try to finish a time-consuming action like "move" inside setup or a single update call, the simulation will stall or you'll hit the bug of treating RUNNING as SUCCESS. The correct shape for a BT leaf is to advance incrementally over multiple ticks, returning RUNNING until done.

⚠️ Missing registration = silent failure — If you implement an action but forget to register it (connect it to the place the engine looks), the OSC scenario treats it as an unknown behavior and silently skips it. Adding a behavior is always an implementation + registration pair.

In a nutshell

📌 A custom OSC action is a BT leaf with a setup (prepare) → update (every tick, returns SUCCESS/RUNNING/FAILURE) → terminate (cleanup) lifecycle. It receives SI-typed parameters and advances incrementally over multiple ticks (never complete async work in one tick). New behaviors are added as an implementation + registration pair without touching the engine.

See standard test results with videos and verdict evidence on the imSim Portal.

View public results →