비헤이비어 트리 개요

언리얼 엔진의 비헤이비어 트리 개념과 전통적인 비헤이비어 트리와의 차이점에 대해 설명합니다.

Choose your operating system:

Windows

macOS

Linux

언리얼 엔진 5(언리얼 엔진)에서 캐릭터에 사용할 인공 지능(AI)을 생성하는 방법은 다양합니다. 블루프린트 비주얼 스크립팅(Blueprint Visual Scripting)을 사용하면 캐릭터가 애니메이션을 재생하거나, 특정 위치로 이동하거나, 충돌에 반응하는 등 '무언가를 하도록' 지시할 수 있습니다. AI 캐릭터가 스스로 생각하고 결정을 내리도록 하려면 비헤이비어 트리(Behavior Tree) 의 도움을 받아 보세요. 아래 비헤이비어 트리는 AI 캐릭터가 순찰과 플레이어 추격 사이에서 행동을 전환하는 예시입니다.

이미지를 클릭하면 최대 크기로 볼 수 있습니다.

비헤이비어 트리 기본

비헤이비어 트리는 블루프린트와 유사한 시각적 방식으로 생성됩니다. 비헤이비어 트리 그래프(Behavior Tree Graph)에 기능이 탑재된 다양한 노드를 추가하고 연결하는 방식입니다. 비헤이비어 트리가 로직을 실행하는 한편, 비헤이비어 트리가 결정을 내리는 데 필요한 정보인 블랙보드 키(Blackboard Key)블랙보드(Blackboard) 라는 별도의 에셋에 저장됩니다. 일반적인 워크플로는 블랙보드를 생성하고, 블랙보드 키를 추가한 다음, 블랙보드 에셋을 사용하는 비헤이비어 트리를 생성하는 순서입니다. 블랙보드는 아래 이미지에서처럼 비헤이비어 트리에 할당됩니다.

비헤이비어 트리에 할당된 블랙보드

언리얼 엔진의 비헤이비어 트리는 왼쪽에서 오른쪽, 위에서 아래 순서로 로직을 실행합니다. 실행 순서는 그래프에 배치된 노드의 우측 상단에 표시되는 숫자로 확인할 수 있습니다. 아래 이미지의 비헤이비어 트리 그래프에서 가장 왼쪽의 샘플 분기에 연결된 노드들은 블랙보드 키 HasLineOfSight 가 설정된 경우 AI가 플레이어를 추격하도록 지시합니다.

실행 순서는 그래프에 배치된 노드의 우측 상단에 표시된 숫자로 확인할 수 있습니다.

위 이미지의 파란색 노드는 데코레이터(Decorator)라고 합니다. 다른 비헤이비어 트리 시스템의 조건문 에 해당합니다. 데코레이터는 컴포짓(Composite) 노드에 어태치되어 블랙보드 키가 True인지 검증하는 데 사용됩니다. 이를 통해 나머지 분기를 실행할지가 정해집니다. 보라색 노드는 태스크(Task) 노드이며, AI가 수행할 수 있는 액션입니다.

비헤이비어 트리를 생성 및 편집하는 방법에 대한 자세한 내용은 비헤이비어 트리 사용자 가이드를 참고하세요.

비헤이비어 트리와 로직을 생성했다면 이제 게임플레이 도중에 비헤이비어 트리를 실행해야 합니다. 보통은 폰(Pawn) 또는 캐릭터거나 다른 엔티티인 AI의 '바디'가 있고, 거기 연결된 AI 컨트롤러(AI Controller) 를 사용해서 폰을 제어하고 액션을 취하도록 지시하게 됩니다. 비헤이비어 트리를 실행하는 것도 액션 중 하나입니다. 아래에서는 폰에 커스텀 AI 컨트롤러 클래스를 할당했습니다.

폰에 할당된 커스텀 AI 컨트롤러 클래스

그리고 AI 컨트롤러에서는 컨트롤러가 폰에 '빙의'할 때 비헤이비어 트리를 실행합니다.

그리고 AI 컨트롤러에서는 컨트롤러가 폰에 '빙의'할 때 비헤이비어 트리를 실행합니다.

폰이 환경을 탐색하게 하려면 내비메시 바운드 볼륨(Nav Mesh Bounds Volume) 을 레벨에 추가해야 합니다.

AI가 비헤이비어 트리를 사용하도록 단계별로 구성하는 방법에 대한 자세한 내용은 비헤이비어 트리 퀵 스타트를 참고하세요.

언리얼 엔진 비헤이비어 트리의 차별점

이 섹션에서는 언리얼 엔진 비헤이비어 트리와 전통적인 비헤이비어 트리 시스템의 차이를 살펴봅니다.

이벤트 주도형 비헤이비어 트리

언리얼 엔진 비헤이비어 트리가 다른 비헤이비어 트리 시스템과 다른 점 중 하나는 이벤트로 주도되기 때문에 프레임마다 불필요한 작업이 수행되지 않는다는 점입니다. 관련 변경 사항이 발생했는지 지속적으로 확인하는 대신, 비헤이비어 트리는 트리 내 변경 사항을 트리거하는 데 사용할 수 있는 '이벤트'를 수동적으로 리스닝합니다. 아래 이미지에서는 블랙보드 키 HasLineOfSight? 를 업데이트하는 데 이벤트가 사용되었습니다. 이를 통해 우선순위가 낮은 태스크는 맨 왼쪽에 있는 우선순위가 높은 분기를 실행하기 위해 중단됩니다.

이미지를 클릭하면 최대 크기로 볼 수 있습니다.

이벤트 주도형 아키텍처가 있으면 퍼포먼스와 디버깅이 모두 개선됩니다. 하지만 이러한 개선 사항을 최대한 활용하기 위해서는 언리얼 엔진 비헤이비어 트리의 나머지 차이점도 이해하여 비헤이비어 트리의 구조를 적절하게 구성해야 합니다. 코드가 매 틱마다 전체 트리를 반복작업할 필요가 없기 때문에 퍼포먼스가 훨씬 뛰어납니다. 개념적으로는, 도착했는지 계속해서 묻지 않고 도착할 때까지 쉬다가 도착했다는 알림을 받는 것과 같습니다.

비헤이비어 트리의 실행 히스토리를 한 단계씩 앞뒤로 이동하면서 비헤이비어를 시각적으로 디버깅하는 경우, 연관성이 있는 변경 사항만 히스토리에 표시하고 무관한 사항은 표시하지 않는 것이 이상적입니다. 언리얼 엔진의 이벤트 주도형 구현 방식에서는 트리를 반복작업하여 이전과 동일한 비헤이비어를 선택하는 연관성 낮은 단계를 필터로 걸러낼 필요가 없습니다. 추가적인 반복 작업이 애초에 발생하지 않기 때문입니다. 대신에 트리 내에서의 실행 위치 또는 블랙보드 값의 변경만 고려하면 되고, 이러한 차이점을 표시하는 것은 쉽습니다.

조건문이 리프 노드가 아님

비헤이비어 트리 표준 모델에서의 조건문은 '태스크' 리프 노드로, 성공이나 실패 이외에는 아무것도 하지 않습니다. 물론 전통적인 조건문 태스크를 만들지 못할 이유는 없지만, 조건문에는 데코레이터(Decorator)를 사용하는 것을 강력하게 권장합니다.

조건문을 태스크가 아닌 데코레이터로 만들면 상당한 이점이 있습니다.

  1. 조건문 데코레이터는 비헤이비어 트리 UI를 보다 직관적이고 읽기 쉽게 만들어 줍니다.

  2. 모든 리프가 액션 태스크이므로 트리를 통해 실제 어떤 액션이 지시되는지 알기가 더 쉽습니다.

조건문은 자신이 제어하는 서브트리의 루트에 있으므로 조건이 충족되지 않은 경우 트리의 어떤 부분이 '닫혔는지' 바로 알 수 있습니다. 또한 모든 리프가 액션 태스크이므로 실제 액션을 알기가 더 쉽습니다. 전통적인 모델에서는 조건문이 리프 사이에 있으므로 어떤 리프가 조건문이고 어떤 리프가 액션인지 알아내는 데 시간이 걸렸습니다.

충분히 가까움과 블랙보드 데코레이터가 시퀀스 노드의 자손이 실행되는 것을 방지합니다

위 비헤이비어 트리 섹션에서는 충분히 가까움(Close Enough)블랙보드 데코레이터가 시퀀스 노드의 자손이 실행되는 것을 방지합니다. 조건문 데코레이터의 또 다른 이점은 트리 내 중요 노드에서 이벤트를 기다리는 관찰자 역할로 만들기 쉽다는 것입니다. 이 기능은 트리의 이벤트 주도형 속성을 최대한 활용하는 데 매우 중요합니다.

동시 발생 비헤이비어

표준적인 비헤이비어 트리는 보통 병렬 컴포짓 노드를 사용하여 동시 발생 행동을 처리하며, 이 병렬 노드는 모든 자손에서 동시에 실행되기 시작합니다. 자손 트리 중 하나 이상이 종료되었을 때 어떤 액션을 취할지는 (원하는 행동에 따라) 특수 규칙으로 결정합니다.

병렬 노드가 반드시 태스크를 동시에 실행하는 멀티 스레딩일 필요는 없습니다. 병렬 노드는 한 번에 여러 태스크를 수행하는 개념적 방식 중 하나일 뿐입니다. 흔히 같은 스레드에서 실행되고, 어떤 순서로 시작됩니다. 해당 순서는 모두 같은 프레임에 발생하는 것을 가정하기 때문에 관련이 없어야 하지만 여전히 중요한 경우도 있습니다.

언리얼 엔진 비헤이비어 트리는 복잡한 병렬 노드 대신 단순 병렬(Simple Parallel) 노드, 서비스(Services)라는 특수 노드, 데코레이터관찰자 중단(Observer Aborts) 프로퍼티를 사용하여 동일한 유형의 행동을 달성합니다.

단순 병렬 노드

단순 병렬 노드는 자손을 두 개만 갖습니다

단순 병렬 노드는 자손을 두 개만 갖습니다. 한 자손은 반드시 단일 태스크 노드여야 하며(데코레이터 선택 가능), 나머지 자손은 완전한 서브트리일 수 있습니다. 단순 병렬 노드는 'A를 수행하는 동안 B도 수행한다'로 생각하면 됩니다. 예를 들어 '적을 공격하는 동안 적을 향해 이동'하는 것입니다. 기본적으로 A는 주요 태스크이고, B는 A가 완료되기까지 기다리는 도중의 부가 태스크 또는 필러 태스크입니다.

부가 태스크(태스크 B)를 처리하는 방법에는 몇 가지 옵션이 있지만, 노드 개념은 전통적인 병렬 노드에 비해 비교적 단순합니다. 그러면서도 병렬 노드가 사용되는 일반적인 경우를 대부분 지원합니다. 단순 병렬 노드를 사용하면 이벤트 주도형 최적화를 활용하기 쉽지만, 완전 병렬 노드는 최적화하기 훨씬 까다롭습니다.

서비스

서비스는 셀렉터, 시퀀스, 단순 병렬 등 모든 컴포짓 노드와 연관되는 특수 노드로, 지정된 시간(초)마다 콜백을 등록하고 주기적으로 발생시킬 필요가 있는 다양한 유형의 업데이트를 수행합니다.

예를 들어 AI 폰이 현재의 적을 쫓는 비헤이비어 트리를 정상적으로 따라가는 동안 어느 적이 최적의 대상인지 결정하기 위해 서비스를 사용할 수 있습니다.

서비스는 자신이 추가된 컴포짓 노드의 서브트리에 실행이 머물러 있는 동안만 활성화됩니다.

관찰자 중단

표준 병렬 노드의 흔한 용도 중 하나는 조건을 지속적으로 확인하여 요구 조건이 False가 되는 경우 태스크를 중단하는 것입니다.

예를 들어 '쉭쉭거리기' 와 '달려들기' 시퀀스를 수행하는 고양이가 있다면, 쥐가 쥐구멍으로 도망치는 순간 즉시 포기하도록 하는 것이 좋습니다. 병렬 노드로는 쥐에게 달려들 수 있는지 확인하는 자손과 시퀀스가 수행할 자손을 가질 수 있습니다. 언리얼 엔진 비헤이비어 트리는 이벤트 주도형이므로 이렇게 하는 대신에 조건문 데코레이터에서 해당 값을 관찰하게 하고 필요시 중단시키는 방식으로 처리합니다. 이 예시에서는 '쥐에게 달려들 수 있는가?'를 들 수 있습니다. 이 경우, 시퀀스의 데코레이터에서 '관찰자 중단'을 '셀프(Self)'로 설정하면 됩니다.

동시 발생 비헤이비어에 대한 언리얼 엔진 접근법의 장점

언리얼 엔진에서 동시 발생 비헤이비어를 처리하는 방법에는 다음과 같은 세 가지 주요 장점이 있습니다.

  1. 명료성 - 서비스와 단순 병렬 노드를 사용하여 읽기 쉽고 이해하기 쉬운 단순한 트리를 만들 수 있습니다.

  2. 쉬운 디버깅 - 그래프가 명료하므로 디버깅도 쉽습니다. 또한 동시 실행 경로가 더 적어서 지금 어떤 것이 실행되고 있는지 파악하기 더 쉽습니다.

  3. 더 쉬운 최적화 - 이벤트 주도형 그래프는 동시에 실행되는 서브트리가 많지 않다면 최적화하기 더 쉽습니다.