프로그래밍

Behavior Tree 이론

우대비 2025. 4. 10. 21:11
반응형
행동 트리 (Behavior Trees) 이해하기

행동 트리 (Behavior Tree) 이해하기

행동 트리란 무엇인가?

행동 트리(Behavior Tree, BT)는 인공지능(AI)에서 캐릭터나 시스템의 행동을 모듈식으로 구성하고 제어하는 데 사용되는 강력한 도구입니다. 특히 게임 개발에서 NPC(Non-Player Character)의 복잡한 의사 결정을 체계적으로 구현하는 데 널리 사용됩니다. 트리는 계층 구조를 가지며, 각 노드는 특정 작업이나 제어 흐름을 나타냅니다. 실행은 루트 노드에서 시작하여 자식 노드로 전달되는 '틱(tick)' 신호를 통해 이루어집니다.

핵심 개념: 노드와 틱(Tick)

행동 트리의 모든 연산은 '틱'이라는 단위로 이루어집니다. 루트 노드에 틱이 전달되면, 해당 노드는 자신의 로직을 실행하고, 필요에 따라 자식 노드에게 틱을 전달합니다. 각 노드는 틱에 대한 응답으로 다음 세 가지 상태 중 하나를 반환합니다:

  • Success: 작업이 성공적으로 완료되었습니다.
  • Failure: 작업을 완료할 수 없거나 조건이 충족되지 않았습니다.
  • Running: 작업이 아직 진행 중이며, 완료되려면 시간이 더 필요합니다. 다음 틱에서 상태가 변경될 수 있습니다.

주요 노드 유형 및 다이어그램

행동 트리는 목적에 따라 다양한 노드로 구성됩니다. 주요 노드 유형은 다음과 같습니다.

1. 복합 노드 (Composite Nodes)

자식 노드들의 실행 흐름을 제어합니다.

  • 시퀀스 (Sequence) - ->

    자식 노드를 순서대로 실행합니다. 하나라도 Failure를 반환하면 즉시 중단하고 Failure를 반환합니다. 모든 자식이 Success를 반환해야 Success를 반환합니다.

    graph TD subgraph Sequence Success Case S_Root["Sequence (->)"] --> S_C1["Child 1 (Success)"] S_C1 --> S_C2["Child 2 (Success)"] S_C2 --> S_C3["Child 3 (Success)"] end style S_Root fill:#ccf,stroke:#333; style S_C1 fill:#cfc,stroke:#333; style S_C2 fill:#cfc,stroke:#333; style S_C3 fill:#cfc,stroke:#333;
    graph TD subgraph Sequence Failure Case F_Root["Sequence (->)"] --> F_C1["Child 1 (Success)"] F_C1 --> F_C2["Child 2 (Failure)"] F_C2 -.-> F_Stop((Stop!)) F_Root -.-> F_C3["Child 3 (Not Run)"] end style F_Root fill:#ccf,stroke:#333; style F_C1 fill:#cfc,stroke:#333; style F_C2 fill:#fcc,stroke:#333; style F_C3 fill:#eee,stroke:#aaa,stroke-dasharray: 5 5; style F_Stop fill:none,stroke:none,color:red;
  • 셀렉터 (Selector) - ? (Fallback)

    자식 노드를 순서대로 실행합니다. 하나라도 Success 또는 Running을 반환하면 즉시 중단하고 해당 상태를 반환합니다. 모든 자식이 Failure를 반환해야 Failure를 반환합니다.

    graph TD subgraph Selector Success Case S_Root["Selector (?)"] --> S_C1["Child 1 (Failure)"] S_Root --> S_C2["Child 2 (Success)"] S_C2 -.-> S_Stop((Stop!)) S_Root -.-> S_C3["Child 3 (Not Run)"] end style S_Root fill:#f9f,stroke:#333; style S_C1 fill:#fcc,stroke:#333; style S_C2 fill:#cfc,stroke:#333; style S_C3 fill:#eee,stroke:#aaa,stroke-dasharray: 5 5; style S_Stop fill:none,stroke:none,color:green;
    graph TD subgraph Selector Failure Case F_Root["Selector (?)"] --> F_C1["Child 1 (Failure)"] F_Root --> F_C2["Child 2 (Failure)"] F_Root --> F_C3["Child 3 (Failure)"] end style F_Root fill:#f9f,stroke:#333; style F_C1 fill:#fcc,stroke:#333; style F_C2 fill:#fcc,stroke:#333; style F_C3 fill:#fcc,stroke:#333;
  • 병렬 (Parallel) - =>

    자식 노드를 동시에 실행합니다. 성공/실패 조건은 정책에 따라 다릅니다 (예: M개 성공 시 성공, N개 실패 시 실패).

    graph TD P_Root["Parallel (=>)"] P_Root --> P_C1["Child 1"] P_Root --> P_C2["Child 2"] P_Root --> P_C3["Child 3"] P_Info((Ticks run concurrently)) --> P_Root style P_Root fill:#e9d8fd,stroke:#333; style P_C1 fill:#fff,stroke:#333; style P_C2 fill:#fff,stroke:#333; style P_C3 fill:#fff,stroke:#333; style P_Info fill:none,stroke:none,color:#555,font-style:italic;

2. 데코레이터 노드 (Decorator Nodes)

하나의 자식 노드를 가지며, 자식의 실행 조건이나 결과를 변경합니다.

데코레이터 유형설명
인버터 (Inverter)자식의 SuccessFailure로, FailureSuccess로 반전시킵니다 (Running은 유지).
서크시더 (Succeeder)자식의 결과와 상관없이 항상 Success를 반환합니다.
리피터 (Repeater)자식 노드를 지정된 횟수만큼 또는 무한히 반복 실행합니다.
언틸 페일 (Until Fail) / 언틸 석세스 (Until Success)자식이 특정 결과(Failure / Success)를 반환할 때까지 반복 실행합니다.
조건 (Condition Decorator)연결된 특정 조건이 참일 때만 자식 노드를 실행합니다. (실제로는 리프 노드의 조건 노드와 유사하게 사용되거나, 리프 노드와 조합됩니다.)

3. 리프 노드 (Leaf Nodes)

트리의 가장 말단에서 실제 행동이나 조건을 검사합니다. 자식 노드를 가지지 않습니다.

  • 액션 (Action)

    구체적인 행동을 수행합니다 (예: 이동, 공격, 상호작용). Success, Failure, 또는 Running 상태를 반환할 수 있습니다.

  • 조건 (Condition)

    특정 상태나 환경을 검사합니다 (예: 체력 확인, 적 감지). 보통 Success(참) 또는 Failure(거짓)를 반환합니다.

행동 트리 구성 예시 (Mermaid 그래프)

다음은 간단한 NPC 행동 로직을 시각적인 그래프로 표현한 행동 트리 예시입니다.

graph TD Root["Selector (?)
NPC 기본 로직"] --> SeqAttack["Sequence (->)
공격 로직"]; Root --> ActPatrol["Action
순찰하기"]; SeqAttack --> CondSeeEnemy{"Condition
적이 시야에 있는가?"}; SeqAttack --> CondInRange{"Condition
공격 가능 거리인가?"}; SeqAttack --> ActAttack["Action
공격 실행"]; %% 스타일 정의 (선택 사항) style Root fill:#f9f,stroke:#333,stroke-width:2px; style SeqAttack fill:#ccf,stroke:#333,stroke-width:1px; style ActPatrol fill:#cfc,stroke:#333,stroke-width:1px; style CondSeeEnemy fill:#ffc,stroke:#333,stroke-width:1px; style CondInRange fill:#ffc,stroke:#333,stroke-width:1px; style ActAttack fill:#cfc,stroke:#333,stroke-width:1px;

실행 흐름 설명:

  1. 루트 Selector가 틱을 받습니다.
  2. 첫 번째 자식인 Sequence (공격 로직)에게 틱을 전달합니다.
  3. Sequence는 첫 번째 자식 Condition (적이 시야에 있는가?)에게 틱을 전달합니다.
    • 만약 적이 시야에 없다면(Failure), Sequence는 즉시 Failure를 반환합니다. 루트 Selector는 다음 자식인 Action (순찰하기)에게 틱을 전달합니다.
    • 만약 적이 시야에 있다면(Success), Sequence는 다음 자식 Condition (공격 가능한 거리인가?)에게 틱을 전달합니다.
      • 거리가 멀면(Failure), SequenceFailure를 반환하고, 루트 SelectorAction (순찰하기)에게 틱을 전달합니다.
      • 거리가 가깝다면(Success), Sequence는 마지막 자식 Action (공격 실행)에게 틱을 전달합니다.
  4. Action (공격 실행)이 Success 또는 Running을 반환하면, Sequence도 같은 상태를 반환하고, 루트 Selector도 해당 상태를 반환합니다. (공격이 성공했거나 진행 중)
  5. Action (공격 실행)이 Failure를 반환하면, SequenceFailure를 반환하고, 루트 SelectorAction (순찰하기)에게 틱을 전달합니다.

장점

  • 모듈성: 행동 로직을 재사용 가능한 작은 단위(노드)로 나눌 수 있습니다.
  • 가독성: 트리 구조는 복잡한 행동 흐름을 시각적으로 이해하기 쉽게 만듭니다.
  • 유지보수성: 특정 행동을 수정하거나 추가하기 용이합니다.
  • 확장성: 새로운 노드 유형을 정의하여 기능을 확장할 수 있습니다.
반응형
LIST

'프로그래밍' 카테고리의 다른 글

인터페이스의 접근 제한자  (0) 2025.02.17
Github 협업 전략 [Github Desktop]  (0) 2025.02.06