← Blog시나리오 DSL (OpenSCENARIO)· 2/4

커스텀 OSC 액션 하나의 해부 — setup·tick·terminate

로봇 스폰·사격·분류 같은 도메인 액션은 어떻게 만들어지는가. OSC가 호출하는 커스텀 액션 하나의 내부 구조 — 준비(setup)·매 틱 실행(update)·정리(terminate).

약 3분
OpenSCENARIOpy_treesactionbehaviorextensibility

커스텀 OSC 액션 하나의 해부

OSC가 Behavior Tree로 컴파일될 때, 각 동작(spawn·move·fire·classify)은 트리의 잎(leaf) 이 된다고 했습니다. 그렇다면 그 잎 하나 — 커스텀 액션은 안에서 어떻게 생겼을까요? 새 도메인 액션을 추가하려면 이 구조를 알아야 합니다.

액션의 세 단계

모든 액션은 잎 행동(behavior)으로, 준비 → 매 틱 실행 → 정리의 수명주기를 가집니다.

도식 렌더링 중…
  • setup/initialise — 액션이 시작될 때 한 번. 필요한 핸들·퍼블리셔를 준비하고 OSC가 넘긴 파라미터를 읽습니다.
  • update매 tick 호출. 한 걸음 진행하고 SUCCESS/RUNNING/FAILURE를 반환합니다. "목표 도달까지 RUNNING, 도달하면 SUCCESS" 식으로.
  • terminate — 끝나거나 중단될 때. 자원을 정리합니다.

예를 들어 "목표까지 이동" 액션은: setup에서 목표를 읽고, update에서 매 틱 한 걸음 전진하며 RUNNING을 내다가, 도착하면 SUCCESS를 반환하고, terminate에서 정리합니다.

액션 하나의 골격을 코드로 보면 이렇습니다.

# 모든 OSC 액션 = setup -> update(매 tick) -> terminate
class MoveToAction(Behavior):
    def setup(self):     self.target = self.params["goal"]   # 한 번
    def update(self):                                        # 매 tick
        step_toward(self.target)
        return SUCCESS if arrived() else RUNNING             # 도착 전엔 RUNNING
    def terminate(self): release()                           # 정리

파라미터는 SI 타입으로 들어온다

OSC는 언어와 타입 시스템을 차용한다고 했습니다. 그래서 액션이 받는 파라미터는 단위가 붙은 값입니다 — speed: 5.0mps, delay: 3.0s, angle: 90.0deg. 액션은 이 SI-타입 파라미터를 받아 동작합니다.

💡 새 액션 = 잎 하나 추가 — 새 도메인 동작(예: "연막 전개", "소나 분류")이 필요하면, 이 setup/update/terminate 구조의 잎을 하나 구현하고 OSC 파서가 찾을 수 있게 등록하면 됩니다. 시나리오 작성자는 그 액션을 OSC에서 이름으로 부르기만 하면 되고요. 엔진을 안 건드리고 동작을 늘리는 확장 지점입니다.

흔한 함정

⚠️ 비동기 동작을 한 틱에 끝내려 하지 마라 — "이동"처럼 시간이 걸리는 동작을 setup이나 한 번의 update에서 완료시키려 하면, 시뮬이 멈추거나 RUNNING을 SUCCESS로 착각하는 버그가 납니다. 진행 중엔 RUNNING을 내고, 여러 tick에 걸쳐 조금씩 나아가는 게 BT 잎의 올바른 모양입니다.

⚠️ 등록 누락 = 조용한 실패 — 액션을 만들었는데 등록(엔진이 찾는 자리에 연결)을 빼먹으면, OSC가 그 액션을 "모르는 동작"으로 보고 조용히 건너뜁니다. 동작 추가는 구현 + 등록이 한 쌍입니다.

한 줄 정리

📌 커스텀 OSC 액션은 BT 잎으로, setup(준비) → update(매 tick, SUCCESS/RUNNING/FAILURE) → terminate(정리) 수명주기를 가진다. SI-타입 파라미터를 받아 여러 tick에 걸쳐 진행하며(비동기를 한 틱에 끝내지 말 것), 새 동작은 구현 + 등록 한 쌍으로 엔진 수정 없이 추가된다.