Behavior Tree 란?

비헤이비어 트리 (행동 트리)는 트리방식으로 행동을 정의하는 하나의 알고리즘이다.

각 노드에 셀렉터(Selector)와 시퀀스(Sequence) 노드를 이용하여 컨디션을 체크해 분기하고, 액션을 정의해 트리를 구성하게 된다.

전체 순회를 할 경우의 넘버링.
기본적인 트리 탐색과 비슷하게 흘러간다.
사진 출처 : https://kindtis.tistory.com/590

Selector

자식 노드를 실행하여, 하나라도 true를 리턴하면 true를 리턴한다.

무언가 하나라도 성공할 때, 트리에서 나오게 된다.

Sequence

모든 자식 노드가 true를 리턴할 때, true를 리턴하게 된다.

무언가 실패할 때, 트리에서 나오게 된다.

FSM과의 차이

비슷한 알고리즘으로는 FSM (Finite State Machine)이 있는데, 연결되는 스테이트들에 컨디션을 추가해주는 것과는 다르게, 행동 트리에서는 컨디션 체크가 자기 자신의 노드에 대해서만 이뤄진다.

그렇기 때문에 확장이 매우 자유롭다. 노드를 필요한 곳에 중복해서 사용해도 괜찮을 정도이다.

주의할 점이 있다면 행동 트리의 노드들은 추가된 순서에 따라 차례대로 호출되기 때문에, 노드의 순서를 조합하는 것이 정말 중요하다. 전체적인 흐름을 잘 파악해 결정된 순서대로 배치해야한다.

언리얼 엔진 4 에서의 행동 트리

언리얼 엔진에서는 행동 트리를 블루프린트처럼 노드단위로 가져와 사용할 수 있게 만들어 두었다.

언리얼 엔진 4에서는 행동 트리를 사용하기 위한 5가지 종류의 노드를 제공한다.

노드 유형설명
Composite컴포짓 – 분기의 루트를 정의하고, 그 분기가 어떻게 실행되는지에 대한 기본 규칙을 정의하는 노드다.
Task태스크 – 비헤이비어 트리의 잎에 해당하는 것으로, 어떤 작업을 하며, 출력 연결이 없는 노드다.
Decorator데코레이터 – Conditionals, 조건문이라고도 한다.
다른 노드에 붙어서 트리 내 분기 내지 노드 하나라도 그 실행 여부를 결정짓는다.
Service서비스 – 컴포짓 노드에 붙어, 그 분기 실행 도중 정해진 빈도에 따라 실행된다.
보통 블랙보드 업데이트나 검사를 하는 데 사용된다. 다른 비헤이비어 트리 시스템에서 전통적인 병렬 노드를 대체한다.
Root루트 노드는 비헤이비어 트리 고유의 것으로, 비헤이비어 트리 시작점이다.
하나의 연결만 가질 수 있으며, 데코레이터나 서비스를 붙일 수는 없다.
루트 노드 자체에는 프로퍼티가 없지만, 선택하면 디테일 패널에 비헤이비어 트리 프로퍼티가 표시되며, 거기서 비헤이비어 트리의 블랙보드 애셋을 설정할 수 있다.

기본 비헤이비어 트리 노드 전체 목록은 아래 노드를 참고하세요.

추가적인 애셋으로 Blackboard와 Behavior Tree 애셋이 존재하고, 여기서 블랙보드는 AI가 Behavior Tree를 진행하며, 사용할 키 값을 저장해두는 공간이라고 보면 된다.

더 자세한 설명은 공식 문서에 나와있다.

컴포짓 : https://api.unrealengine.com/KOR/Engine/AI/BehaviorTrees/NodeReference/Composites/index.html

데코레이터 : https://api.unrealengine.com/KOR/Engine/AI/BehaviorTrees/NodeReference/Decorators/index.html

서비스 : https://api.unrealengine.com/KOR/Engine/AI/BehaviorTrees/NodeReference/Services/index.html

태스크 : https://api.unrealengine.com/KOR/Engine/AI/BehaviorTrees/NodeReference/Tasks/index.html

언리얼 엔진 4와 표준적인 행동 트리의 차이점

이벤트 주도형 (Event Driven) 행동 트리이다.

언리얼의 행동 트리는 관련된 변화가 일어났다 지속적으로 확인하는 대신, 트리 내부에 변화를 발동시키는 이벤트를 수동적으로 기다린다.

이렇게 함으로써 퍼포먼스와 디버깅 양쪽 측면이 모두 개선되었다.

언리얼 행동 트리는 상당히 잘 최적화되고 언리얼 엔진에 잘 맞아들어가게 최적화된 행동 트리이다.

조건문이 리프 노드(Leaf Node)가 아니다.

트리에서 탐색하는 하나하나의 노드를 리프 노드라고 하는데, 언리얼 엔진의 행동트리에서 액션을 갖고있는 노드는 태스크 (Task) 리프 노드이다. true와 false 반환 이외에는 아무것도 하지 않는다.

이 경우 Close Enough 과 Blackboard 데코레이터가 시퀀스 노드의 자손 노드 실행을 막을 수 있다.

언리얼에서는 조건문을 데코레이터 (Decorator)로 제작한다. 이에는 몇 가지 엄청난 장점이 있다.

조건문 데코레이터는 행동 트리 UI를 직관적이고 읽기 쉽게 만들어준다. 조건문은 자신이 제어하는 서브 트리의 루트에 있기 때문에, 조건이 충족되지 않은 경우, 트리의 어느 부분에서 false가 발생하는지 바로 알 수 있다.

또한, 모든 리프 노드가 액션 태스크이기 때문에, 트리를 통해 실제 어떤 액션이 내려졌는지 알기 쉬워진다.

표준 모델에서 조건문은 리프 사이 사이에 끼어있어서, 어떤 리프가 조건문이고 어느 리프가 액션인지 알기가 조금 귀찮았다.

조건문 데코레이터의 또다른 장점은, 그 데코레이터를 트리 내 중요 노드에서 (이벤트를 기다리고있는) 관찰자 역할을 하도록 만드는 것이 쉽다는 것이다.

이 기능은 언리얼 행동 트리의 이벤트 주도형 속성의 장점을 최대한 이끌어내는 것에 있어 매우 중요하다.

동시발생 하는 행동 트리에 대한 특수 처리

표준적인 행동 트리는 종종 병렬(Parallel) 컴포짓 노드로 동시발생 행동 트리를 처리한다.

병렬 노드는 그 모든 자손의 실행을 동시에 시작시킨다. 특수한 규칙을 통해 그 자손 트리중 하나 이상이 끝났을 때, 원하는 행동에 따라 어떻게 대처할 것인지 결정한다.

표준 행동 트리의 복잡한 병렬 노드 대신, 언리얼 엔진에서 행동 트리는 단순 병렬 노드와 서비스라는 독자적인 특수 노드 유형을 사용하여 거의 비슷한 행동을 하고 있다.

병렬 (Parallel) 노드를 사용하지 않는 이유

병렬 노드는 비교적 단순한 행동들도 매우 햇갈릴 수 있고, 퍼포먼스 최적화가 힘들어진다. 언리얼의 이벤트 주도형 트리 제작에는 더 어렵다.

UE4 에서 병렬 노드 대신 사용하는 것

단순 병렬 노드

단순 병렬 노드는 자손을 딱 두개만 허용한다. 하나는 단일 태스크 노드, 나머지는 완결된 서브트리여야 한다.

단순 병렬 노드는 A 를 하는 도중 B 도 해라 라고 하는 작업을 생각해볼 수 있다.

예를 들어, 적을 공격하는 도중에는, 적을 향해 움직여라. 같은 것이다.

기본적으로 A 는 주요 태스크이고, B 는 A 가 완료되기까지 기다리는 도중의 부차적 또는 필터링 태스크이다.

부차적인 사이 (B) 태스크 처리 관련 몇 가지 옵션이 있는 반면, 노드는 전통적 병렬 노드에 비할 때 개념적으로 비교적 단순하다. 그렇다고는 해도, 흔히 병렬 노드가 사용되는 경우를 다수 지원한다.

단순 병렬 노드 덕에 이벤트 주도형 최적화가 쉽게 가능해졌다. 최대 병렬 노드는 최적화에 훨씬 복잡했을 것이다.

서비스

서비스는 컴포짓 노드와 연관된 특수 노드로, X 초마다 콜백 등록을 한 다음 주기적으로 발생시킬 필요가 있는 다양한 유형의 업데이트를 수행한다.

예를 들어 AI 폰이 현재 적을 쫒아가는 행동 트리를 정상적으로 따라가는 와중에 어느 적이 최적의 대상인지 결정하는 데 서비스를 사용할 수 있다.

서비스는 거기에 관련된 컴포짓 노드에 루트를 둔 서브트리에 실행이 머무르는 동안만 활성화된다.

데코레이터의 Observer Aborts 프로퍼티

표준 병렬 노드가 자주 쓰이는 경우중 한 가지는, 조건을 지속적으로 검사하여 요구 조건이 false가 되는 경우 태스크를 중단하는 것이다.

예를 들어, 꼬리 흔들기 와 덮치기 시퀀스를 수행하는 고양이가 있는 경우, 쥐가 쥐구멍으로 탈출한 순간 즉시 포기하도록 하는 것이 좋을 것이다.

병렬 노드로는 쥐를 덮칠 수 있는지 검사하도록 하는 자손 하나에, 수행할 시퀀스를 또다른 자손에 넣어둘 것이다.그런데, 언리얼의 비헤이비어 트리는 이벤트 주도형이므로 이것을 어떻게 처리하냐면, 조건문 데코레이터에게 그 값을 관찰 시키다가 필요한 때 중단시키도록 한다.

(이 예제에서는 시퀀스 자체에 쥐를 덮칠 수 있는가? 하는 데코레이터를 두고, 관찰자 중단(Observer Aborts) 은 본인으로 설정해 두었다. )

동시 발생 행동 트리에 대한 언리얼 엔진 4의 접근법에서의 장점

명확성

서비스와 단순 병렬 노드로 이해하기 쉬운 단순한 트리를 만든다.

디버깅 편의

명확한 그래프가 디버깅도 아주 쉽다. 추가로, 동시 실행 경로가 적다는 것은 그래프에서 실제로 무엇이 벌어지는지 알아보는 데 있어 엄청난 장점이다.

쉬운 최적화

이벤트 주도형 그래프는 동시 실행 서브트리가 많지 않다면, 최적화가 더욱 쉽다.