본문 바로가기

레퍼런스

비헤이버트리 AI Perception

비헤이비어 트리 퀵 스타트 가이드 에서는 플레이어를 보고 반응하여 추격하는 적 AI를 구성하는 방법을 학습합니다. 아래 예시 영상에서 볼 수 있듯이, 플레이어가 시야에서 사라지면 AI는 몇 초 후(원하는 대로 조정 가능) 추격을 포기하고 플레이어를 다시 발견할 때까지 주변을 랜덤하게 배회합니다.

가이드에 따라 학습을 마치면 다음과 같은 시스템을 이해할 수 있습니다. 

  1. 블루프린트 비주얼 스크립팅(Blueprint Visual Scripting)
  2. AI 컨트롤러(AI Controller)
  3. 블랙보드(Blackboard)
  4. 비헤이비어 트리
  5. 비헤이비어 트리 서비스(Behavior Tree Service)
  6. 비헤이비어 트리 데코레이터(Behavior Tree Decorator)
  7. 비헤이비어 트리 태스크(Behavior Tree Task)

1 - 필수 프로젝트 구성

우선 AI 캐릭터가 환경을 배회하기 위해 필요한 에셋을 사용하여 프로젝트를 구성합니다. 

이 가이드에서는 새로운 블루프린트 삼인칭 템플릿(Blueprint Third Person Template) 프로젝트를 사용합니다.

  1. 콘텐츠 드로어(Content Drawer) 를 열고 ThirdPerson 폴더를 우클릭하여 새 폴더(New Folder) 를 생성하고 AI 로 명명합니다.
  2. ThirdPerson > Blueprints 폴더로 가서 BP_ThirdPersonCharacter  AI 폴더로 드래그하고 여기에 복사(Copy Here) 를 선택합니다.
  3. AI 폴더에서 AI 컨트롤러(AIController) 클래스를 기반으로 새로운 블루프린트 클래스(Blueprint Class) 를 생성합니다.
  4. AIController 블루프린트는 Enemy_Controller 로, BP_ThirdPersonCharacter 블루프린트는 Enemy_Character 로 이름을 변경합니다.
  5. Enemy_Character 를 열고 그래프의 스크립트를 모두 삭제합니다.
  6. 캐릭터 무브먼트(Character Movement) 컴포넌트를 선택하고 디테일(Details) 패널의 최대 걷기 속도(Max Walk Speed)  120.0 으로 설정합니다.
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  7. 이렇게 하면 AI 캐릭터가 플레이어를 뒤쫓지 않고 주변을 순찰 중일 때 이동 속도가 느려집니다.
  8. 툴바에서 클래스 디폴트(Class Defaults) 를 선택하고 디테일 패널에서 AI 컨트롤러 클래스(AI Controller Class)  Enemy_Controller 를 할당합니다.
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  9. AI를 월드에 배치할 것입니다. 월드가 로드된 후 AI를 스폰하는 경우에는 AI 자동 빙의(Auto Possess AI) 세팅을 Spawned 로 변경해야 합니다.
  10. 콘텐츠 드로어 에서 Enemy_Character 를 레벨로 드래그합니다. 
  11. 액터 배치(Place Actors) 패널에서 NavMeshBoundsVolume 을 레벨로 드래그합니다.
  12. NavMeshBoundsVolume 을 선택한 채 R 을 누른 다음 볼륨이 레벨 전체를 덮도록 스케일을 조절합니다.
    그러면 AI 캐릭터가 주변 환경을 돌아다닐 수 있는 내비게이션 메시(Navigation Mesh)가 생성됩니다. P 를 눌러 내비메시를 뷰포트에 표시할지 여부를 토글할 수 있습니다. 녹색 영역은 이동 가능한 위치를 나타냅니다.
  13. 게임플레이 중에는 Show Navigation 콘솔 명령으로 내비메시의 표시 여부를 토글할 수 있습니다.

프로젝트 구성이 끝났으니 다음 단계에는 블랙보드(Blackboard) 에셋을 구성하겠습니다.

2 - 블랙보드 구성

이번 단계에는 AI의 두뇌 역할을 할 블랙보드(Blackboard) 에셋을 구성합니다. AI가 알아야 하는 모든 요소는 블랙보드 키(Blackboard Key) 를 통해 참조할 수 있습니다. 여기서는 AI가 플레이어에 대한 직접적인 시야를 확보했는지 여부에 상관없이 플레이어를 트래킹하는 키를 생성하고, AI가 플레이어를 쫓지 않을 때 이동할 수 있는 위치를 확보하겠습니다.

  1. 콘텐츠 드로어(Content Drawer) 에서 +추가(+Add) 를 클릭하고 인공 지능(Artificial Intelligence) 에서 블랙보드 를 선택한 뒤 BB_Enemy 로 이름 짓습니다.
  2. BB_Enemy 블랙보드 안에서 새 키(New Key) 버튼을 클릭하여 오브젝트(Object) 를 선택합니다.
    블랙보드 에셋은 두 개의 패널로 구성됩니다. 블랙보드 패널에서는 블랙보드 키 (모니터링할 변수)를 추가하고 트래킹할 수 있으며, 블랙보드 디테일(Blackboard Details) 패널에서는 키의 이름을 정하고 키 타입을 지정할 수 있습니다.
  3. 오브젝트 키의 엔트리 이름(Entry Name)  EnemyActor 를 입력하고, 베이스 클래스(Base Class)  Actor 로 지정합니다.
  4. 키 타입(Key Type)  부울(Bool)   를 하나 더 추가하고 HasLineOfSight 로 이름 짓습니다.
    이 키는 AI가 플레이어에 대한 시야를 확보했는지 여부를 트래킹하는 데 사용됩니다.
  5. 키 타입  벡터(Vector)   를 하나 더 추가하고 PatrolLocation 으로 이름 짓습니다.
    이 키는 AI가 플레이어를 추격하는 중이 아닐 때 이동 가능한 레벨 내 위치를 트래킹하는 데 사용됩니다.

트래킹해야 하는 항목들이 반영된 블랙보드 가 구성되었습니다. 다음 단계에는 비헤이비어 트리(Behavior Tree) 의 레이아웃을 설정합니다.

3 - 비헤이비어 트리 레이아웃

이 단계에는 비헤이비어 트리(Behavior Tree) 의 흐름과 AI가 진입할 스테이트의 레이아웃을 구성합니다. AI에게 기대하는 스테이트를 갖춘 비헤이비어 트리 의 레이아웃을 시각적 흐름으로 구성하면 해당 스테이트에 진입하는 데 필요한 로직과 규칙의 타입에 관한 아이디어를 얻을 수 있습니다.

  1. 콘텐츠 드로어(Content Drawer) 에서 +추가(+Add) 를 클릭한 후 인공 지능(Artificial Intelligence) 에서 비헤이비어 트리 를 선택하고 BT_Enemy 로 이름 짓습니다.

    이름 규칙은 다양할 수 있지만, 일반적으로는 에셋 타입을 이름의 접두사로 붙이는 방식이 좋습니다.

  2. BT_Enemy 를 열고 BB_Enemy  블랙보드 에셋(Blackboard Asset) 에 할당합니다.
  3. 방금 생성한 블랙보드 키 를 찾을 수 없다면 노란색 화살표를 클릭하여 블랙보드 에셋 을 초기화한 다음 Enemy_BB 를 재할당하여 키를 새로고침합니다.
  4. 비헤이비어 트리 는 세 가지 패널로 구성됩니다. 비헤이비어 트리 그래프에서는 비헤이비어를 정의하는 분기와 노드의 레이아웃을 시각적으로 구성할 수 있고, 디테일(Details) 패널에서는 노드의 프로퍼티를 정의할 수 있으며, 블랙보드 에서는 게임이 실행 중일 때의 블랙보드 키 와 키의 현재 값을 확인하여 디버깅에 활용할 수 있습니다.
  5. 그래프에서 루트(Root) 를 좌클릭하고 드래그하여 Selector 노드를 추가합니다.
    컴포짓(Composite) 은 흐름 제어의 형태이며, 연결된 자손 분기의 실행 방식을 결정합니다.
    컴포짓설명
    셀렉터(Selector) 왼쪽에서 오른쪽 순서로 분기를 실행하며, 보통은 복수의 서브트리 중 하나를 선택하는 데 사용됩니다. 셀렉터는 성공적으로 실행할 수 있는 서브트리를 찾으면 이동을 멈춥니다. 예를 들어 AI가 플레이어를 성공적으로 추격하고 있다면, 분기의 실행이 완료될 때까지 해당 분기에 머물다가 셀렉터의 부모 컴포짓으로 이동하여 결정 흐름을 계속하게 됩니다.
    시퀀스(Sequence) 왼쪽에서 오른쪽 순서로 분기를 실행하며, 보통은 복수의 자손을 순서대로 실행하는 데 사용됩니다. 시퀀스는 셀렉터와 달리 실패하는 노드에 도달할 때까지 자손을 계속해서 실행합니다. 예를 들어 플레이어를 향해 움직여서 플레이어가 일정 거리 안에 들어왔는지 확인한 다음 회전하여 공격하게 하는 시퀀스가 있다고 합시다. 플레이어가 일정 거리 안에 들어왔는지 확인하는 부분이 실패하면, 회전 후 공격 액션은 실행되지 않습니다.
    단순 병렬(Simple Parallel) 단순 병렬에는 두 개의 '연결'이 있습니다. 첫 번째는 메인 태스크(Main Task)이며, 컴포짓이 아닌 태스크(Task) 노드에만 할당할 수 있습니다. 두 번째 연결인 백그라운드 분기(Background Branch)는 메인 태스크가 실행 중일 동안 실행됩니다. 단순 병렬은 프로퍼티에 따라 메인 태스크가 완료된 직후에 완료될 수도 있고, 아니면 백그라운드 분기도 완료될 때까지 대기할 수도 있습니다.
  6. Selector 노드의 디테일 패널에서 노드 이름(Node Name)  AI Root 로 변경합니다. 
    그래프에 있는 노드의 이름을 변경하면 노드의 역할을 한눈에 알아보기 좋습니다. 이번 예시에서 이 노드를 AI Root 로 변경하는 이유는 이 노드가 비헤이비어 트리의 실제 '루트'로서 자손 분기 사이를 전환하기 때문입니다. 비헤이비어 트리를 생성할 때 자동으로 추가되는 디폴트 루트 노드는 비헤이비어 트리의 프로퍼티를 환경설정할 때와 사용 중인 블랙보드 에셋을 할당할 때 사용됩니다. 
  7. AI Root 를 좌클릭하고 드래그하여 Sequence 노드를 추가하고 Chase Player 로 이름 짓습니다.

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

    여기서는 시퀀스 노드를 사용하여 AI에게 액션 시퀀스를 실행하도록 하겠습니다. 플레이어 방향으로 회전한 다음, 이동 속도를 변경하고, 플레이어를 추격하기 위해 이동하는 시퀀스입니다. 
  8. AI Root 노드를 좌클릭하고 드래그하여 Sequence 노드를 추가하고 Patrol 로 이름 짓습니다.

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

    AI에게 Sequence 노드를 사용하여 맵에서 랜덤 위치를 찾고, 그 위치로 이동하고, 그 위치에서 잠시 대기하다가, 다시 새로운 위치를 찾아 움직이는 프로세스를 반복시키겠습니다. 
  9. 이 숫자가 실행 순서를 나타냅니다. 비헤이비어 트리 는 왼쪽에서 오른쪽, 위에서 아래 순서로 실행되기 때문에 노드의 배열도 중요합니다. 보통 AI에게 중요도가 높은 액션을 왼쪽에 배치하고, 덜 중요한 액션 또는 예비 비헤이비어를 오른쪽에 배치하는 것이 일반적입니다. 자손 분기 역시 동일한 방식으로 작동하며, 자손 분기 중 하나라도 실패하면 전체 분기가 실행을 중지하고 트리를 다시 거슬러 올라갑니다. 예를 들어 Chase Player 분기가 실패하면 AI Root 를 거슬러 올라가 Patrol 이전으로 돌아가게 됩니다.
  10. 노드의 우측 상단에는 숫자가 있습니다.
  11. AI Root 에서 드래그하여 Wait 태스크를 Patrol 오른쪽에 추가하고 대기 시간(Wait Time)  1.0 으로 설정합니다. 

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

    이 노드가 보라색인 것은 태스크(Task) 노드라는 의미입니다. 태스크 노드는 비헤이비어 트리 가 수행할 액션입니다. Wait 태스크는 비헤이비어 트리 가 Chase Player와 Patrol에 모두 실패했을 때 취할 대기 액션입니다. 
  12. Chase Player 에서 드래그하여 Rotate to Face BBEntry 노드를 추가합니다.

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

     태스크 를 사용하면 원하는 블랙보드 엔트리(Blackboard Entry) 가 회전하도록 지정할 수 있습니다. 여기서는 Enemy Actor (플레이어) 방향으로 회전합니다. 이렇게 노드를 추가하고 디테일 패널을 보면 블랙보드 키 가 자동으로 EnemyActor 로 설정되는데, 액터 블랙보드 변수라는 필터를 적용한 목록의 첫 번째 변수이기 때문입니다. 정밀도(Precision) 옵션으로 성공 조건 범위를 조정할 수도 있고, 노드 이름 을 변경할 수도 있습니다.
  13. 툴바 에서 새 태스크(New Task) 버튼을 클릭합니다.
    여기서는 내장된 태스크를 사용할 수 있을 뿐만 아니라, 직접 커스터마이징하고 정의할 수 있는 추가적인 로직을 갖는 커스텀 태스크를 생성하여 할당할 수도 있습니다. 이 태스크는 플레이어를 추격할 수 있도록 AI의 이동 속도를 변경하는 데 사용됩니다. 새 태스크를 생성하면 새 블루프린트 가 자동으로 생성되고 실행됩니다.

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

  14. 콘텐츠 드로어 에서 새 에셋의 이름을 BTT_ChasePlayer 로 변경합니다.
    태스크(Task) , 데코레이터(Decorator) , 서비스(Service) 를 새로 생성한 뒤에는 바로 이름을 변경하는 것이 좋습니다. 적절한 이름 규칙은 생성한 에셋의 이름에 에셋 타입별 접두사를 추가하는 것입니다. 예를 들어 비헤이비어 트리 태스크에는 BTT , 비헤이비어 트리 데코레이터에는 BTD , 비헤이비어 트리 서비스에는 BTS 접두사를 지정하면 좋습니다. 
  15. BT_Enemy 안에서 BTT_ChasePlayer  Move To 태스크를 추가합니다. 

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

    새로 추가한 태스크에는 아직 아무 로직도 없지만, AI 캐릭터가 이동 속도를 변경하여 EnemyActor(플레이어)를 향해 이동하는 로직을 나중에 추가하겠습니다.
  16.  태스크 를 생성하여 BTT_FindRandomPatrol 로 이름 짓고 Patrol 에 연결합니다. 

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

  17. Move To 태스크를 추가하고 블랙보드 키  PatrolLocation 으로 설정합니다. 

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

    그러면 AI가 BTT_FindRandomPatrol 태스크에 설정된 PatrolLocation으로 이동하도록 지시하게 됩니다. 
  18. Move To 뒤에 Wait 태스크를 추가하고 대기 시간  4.0 으로, 랜덤 편차(Random Deviation)  1.0 으로 설정합니다. 

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

    그러면 AI에게 PatrolLocation에서 3~5초간 대기하도록 지시하게 됩니다(랜덤 편차로 인해 대기 시간에 1초의 편차가 추가됩니다).

비헤이비어 트리 프레임워크가 완성되었습니다. 다음 단계에는 AI의 이동 속도를 변경하고, AI가 순찰 중에 이동할 랜덤 위치를 찾고, AI가 플레이어를 추격할지 순찰할지 결정하는 로직을 추가하겠습니다.

4 - 태스크 구성 - 플레이어 추격

이번 단계에는 플레이어를 추격할 때 이동 속도를 변경하는 플레이어 추격 태스크(Chase Player Task) 를 구성하겠습니다.

  1. BTT_ChasePlayer 안에서 우클릭하고 이벤트 Receive Execute AI 노드를 추가합니다. 
    이벤트 Receive Execute AI 노드는 비헤이비어 트리 안에서 이 태스크가 활성화될 때 트리거됩니다. 
  2. 에이전트가 AI 컨트롤러인 경우 항상 AI 버전의 이벤트 Receive Execute , 이벤트 Receive Abort , 이벤트 Receive Tick 을 선택해야 합니다. 일반 이벤트 버전 및 AI 이벤트 버전이 모두 구현되어 있다면 더 적합한 버전 하나만 호출됩니다. 즉, AI의 경우에는 AI 버전이 호출되고 이외의 경우에는 일반 버전이 호출됩니다.
  3. Controlled Pawn 핀에서 Enemy_Character에 형변환(Cast to Enemy_Character) 노드를 사용합니다.
    Enemy_Character 라는 AI가 캐릭터 블루프린트(Character Blueprint) 에 액세스하도록 형변환(Cast) 노드를 사용하는 것입니다.
  4. 콘텐츠 드로어(Content Drawer) 에서 Enemy_Character 블루프린트를 실행하여 Update Walk Speed 라는 함수(Function) 를 추가합니다. 
    이 함수는 비헤이비어 트리에서 호출되어 AI의 이동 속도를 변경하는 데 사용됩니다. 
  5. 기술적으로는 Chase Player 태스크의 형변환 노드로부터 캐릭터 무브먼트(Character Movement) 컴포넌트에 액세스하여 해당 태스크 안에서 이동 속도를 조정할 수 있지만, 비헤이비어 트리가 서브 오브젝트의 프로퍼티를 직접 변경하게 하는 것이 권장되는 모범 사례라고 할 수는 없습니다. 그 대신 비헤이비어 트리가 캐릭터 안의 함수를 호출하여 필요에 따라 조정하도록 하겠습니다.
  6. Update Walk Speed 함수의 디테일(Details) 패널에서 NewWalkSpeed 라는 플로트 입력을 추가합니다. 
  1. 컴포넌트(Components) 탭에서 캐릭터 무브먼트 컴포넌트를 드래그합니다. 
  2. 캐릭터 무브먼트 핀을 클릭하고 드래그하여 액션 메뉴에 Set Max Walk Speed 를 입력한 다음 메뉴에서 Set Max Walk Speed 를 클릭합니다. 
  3.   
  4. Set Max Walk Speed 를 아래와 같이 연결합니다. 

이 함수를 비헤이비어 트리에서 호출할 때는 값을 새로운 속도로 사용하도록 전달할 수 있습니다.

  1. BTT_ChasePlayer 태스크로 돌아와 As Enemy Character 노드에서 Update Walk Speed 를 호출하고 500.0 으로 설정한 후 다음과 같이 연결합니다.
  2. 생성한 Update Walk Speed 함수가 보이지 않나요? Enemy_Character 블루프린트를 플레이어 추격 태스크 에 추가하려면 우선 컴파일(Compile) 해야 할 수도 있습니다.
  3. Update Walk Speed 뒤에 Finish Execute 노드를 두 개 추가하여 아래와 같이 연결합니다.여기서는 Enemy_Character로의 형변환이 성공하면 태스크가 성공한 것으로 판정합니다. 제어되는 폰이 Enemy_Character가 아닌 경우 태스크가 성공하지 못했다고 판정하여 태스크를 중단하도록 처리해야 합니다.
  4. New Walk Speed 핀을 우클릭하고 변수로 승격한 후 ChaseSpeed 로 명명합니다.
  5. ChaseSpeed  인스턴스 편집가능(Instance Editable) 옵션을 활성화합니다.
    인스턴스 편집가능 변수로 승격하면 최대 걷기 속도(Max Walk Speed) 값을 블루프린트 외부에서 설정할 수 있으며, 비헤이비어 트리 내부에서 프로퍼티로 활용할 수 있습니다. 
    이제 Enemy_Character 블루프린트로 전송되는 추격 속도(Chase Speed) 값을 쉽게 변경하여 AI가 플레이어를 추격하는 속도를 미세조정할 수 있습니다.

플레이어 추격 태스크(Chase Player Task) 를 완성했으니, 다음 단계에는 AI가 이동할 랜덤 위치를 구하는 랜덤 순찰 탐색 태스크(Find Random Patrol Task) 로직을 구성해 보겠습니다.

5 - 태스크 구성 - 랜덤 순찰 탐색

이 단계에는 AI가 플레이어를 추격하지 않을 때 랜덤 위치로 이동하도록 랜덤 순찰 탐색 태스크 를 구성해 보겠습니다. 

블루프린트 비헤이비어 트리 태스크를 구현하면 반복작업을 빠르게 처리하기 좋지만, 퍼포먼스가 중요하다면 네이티브 비헤이비어 트리 태스크로 구현할 것을 추천드립니다.

  1. BTT_FindRandomPatrol 안에서 이벤트 Receive Execute AI  Enemy_Character에 형변환 을 연결합니다.
  2. 다음 세팅을 사용하여 As Enemy Character 에서 Update Walk Speed 를 호출하고 New Walk Speed 를 변수로 승격한 후 Patrol Speed 로 이름 짓습니다.

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

    - 변수 이름(Variable Name) : PatrolSpeed
    - 인스턴스 편집가능 : 활성화
    - Patrol Speed(기본값) : 125.0
  3. 순찰 중일 동안에는 적의 이동 속도를 늦추기 위해서입니다.
  4. Controlled Pawn 에서 Get Actor Location 을 사용하고, Branch 와 연결한 Return Value 에서 GetRandomReachablePointInRadius 를 사용합니다.
  5. 다음 세팅을 사용하여 GetRandomReachablePointInRadius  Radius 를 변수로 승격합니다.

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

    - 변수 이름 : PatrolRadius
    - 인스턴스 편집가능 : 활성화
    - Patrol Radius(기본값) : 1000.0
  6. 그러면 적 캐릭터는 현재 위치에서 1,000유닛 이내의 랜덤 위치를 찾게 됩니다. 또한 Branch 노드를 사용하여 이동할 수 있는 랜덤 위치를 찾지 못하는 엣지 케이스를 처리합니다.
  7. Random Location 핀을 드래그하여 Set Blackboard Value as Vector 를 연결하고   PatrolLocation 이라는 변수로 승격합니다.
  8. Set Blackboard Value as Vector 노드를 하나 더 사용하고, Value  Get Actor Location 에서 얻습니다.
  9. 이전 단계에 이어 아래와 같이 두 노드를 연결하여 Finish Execute  Success 로 판정합니다.

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

    적은 이동할 만한 랜덤 위치를 찾으면 블랙보드에 자신이 이동할 위치로 저장합니다. 이동할 만한 위치를 찾지 못하면 현재 위치를 사용하고 가만히 있다가 새로운 위치를 찾는 시도를 합니다. 제어되는 폰이 Enemy_Character가 아닌 엣지 케이스도 처리해야 합니다.
  10. 형변환(Cast) 노드의 Cast Failed 핀에 Finish Execute 를 연결하고 Success 를 비활성화합니다.

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

    제어되는 폰이 Enemy_Character가 아닌 경우, 이 태스크는 성공하지 못한 것으로 판정되고 중단됩니다.

랜덤 순찰 탐색 태스크(Find Random Patrol Task) 가 완성되었습니다. 다음 단계에는 데코레이터(Decorator) 를 자세히 살펴보고, 데코레이터를 조건식으로 사용하는 방법과 AI 컨트롤러(AI Controller) 구성에 활용하는 방법을 알아보겠습니다.

6 - AI 컨트롤러 구성

이 단계에는 최종 단계를 위한 준비 작업으로 AI 컨트롤러 안에서 데코레이터(Decorator) 를 구성하여 비헤이비어 트리 의 어떤 분기로 진입할지 결정해 보겠습니다. 

  1. 콘텐츠 드로어(Content Drawer) 에서 Enemy_Controller 블루프린트를 열어 이벤트 On Possess 노드를 추가합니다. 
  2. 이벤트 On Possess  Run Behavior Tree 노드를 추가하고 BTAsset  BT_Enemy 로 설정합니다. 
    Run Behavior Tree 는 AI 컨트롤러 클래스 블루프린트를 타깃으로 하는 컨텍스트 함수 호출로, 할당된 비헤이비어 트리 에셋을 실행할 수 있게 해 줍니다.
  3. 컴포넌트(Components) 창에서 +추가 를 클릭하여 AI 퍼셉션 컴포넌트(AIPerception Component) 를 검색하고 추가합니다.
    AI 퍼셉션 컴포넌트  AI 퍼셉션 시스템(AI Perception System) 에서 자극 리스너를 생성하고 사용자가 반응하도록 등록된 자극(여기서는 시각 사용 가능)을 수집하는 데 사용됩니다. 이 기능을 통해 AI가 플레이어를 보고 반응하도록 구성이 가능합니다.
  4. AI 퍼셉션 컴포넌트  디테일(Details) 패널에서 AI 시야 구성(AI Sight Config) 을 추가하고 중립 탐지(Detect Neutrals) 를 활성화합니다.
    소속 감지(Detection by Affiliation) 프로퍼티를 사용하면 같은 소속 팀원들과 함께 상대 팀원들을 공격하는 팀 기반 AI를 구성할 수 있습니다.  액터 는 디폴트로 소속이 할당되지 않으며 중립으로 간주됩니다.
  5. 현재는 블루프린트를 통해 소속을 할당할 수 없으므로 플레이어를 찾아내려면 중립 탐지 플래그를 활성화해야 합니다. 여기서는 액터 태그 지정(Actor Tagging) 을 대신 사용하여 어떤 캐릭터가 플레이어인지 판단하고 AI 캐릭터가 플레이어로 태그 지정된 액터만 뒤쫓도록 구성합니다.
  6. AIPerception  이벤트(Events) 섹션에서 타깃 퍼셉션 업데이트 시(On Target Perception Updated) 옆의 + 표시를 클릭합니다.
  7. 그래프의 타깃 퍼셉션 업데이트 시 에서 Actor Has Tag 노드를 추가하여 Tag  Player 로 설정합니다.  
  8. Stimulus 핀에서 AIStimulus 분해(Break AIStimulus) 노드를 추가합니다.
  9. Branch 노드를 추가하여 Condition 을 아래와 같이 설정합니다. 
    여기서는 액터가 성공적으로 감지되었는지, 액터에 플레이어 태그가 있는지 확인합니다. 
  10. AIStimulus 분해 노드를 선택하고 디테일 패널에서 연결되지 않은 핀 숨김(Hide Unconnected Pins) 을 사용하여 위와 같이 그래프에서 연결되지 않은 핀을 모두 숨깁니다.
  11. Branch  False  Set Timer by Event 를 추가하고 Time  4.0 으로 설정합니다. 
  12. Time 을 우클릭하여 변수로 승격한 후 Line Of Sight Timer 로 이름 짓습니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  13. 이 변수에 할당된 값은 AI가 플레이어를 추격하길 그만두고 어태치된 이벤트가 실행되기 전까지의 시간을 결정합니다. 
  14. Set Timer by Event  Return Value 를 우클릭하고 변수로 승격하여 EnemyTimer 로 이름 짓습니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  15. 이렇게 하면 핸들(Handle) 을 통해 타이머에 대한 레퍼런스가 저장됩니다. 이 핸들은 스크립트를 통해 호출되어 자기 자신을 무효화하고 관련 이벤트를 모두 지움으로써 관련 이벤트가 실행되지 않도록 방지합니다. 나중에 Line of Sight Timer가 만료되기 전에 AI가 플레이어를 다시 발견하는 이벤트에서 사용할 수 있습니다. 그렇게 하면 AI가 플레이어를 시야에서 놓친 뒤 바로 추격을 포기하지 않도록 방지할 수 있습니다. 
  16. 커스텀 이벤트(Custom Event) 를 생성하여 StartEnemyTimer 로 이름 짓고 Set Timer by Event  Event 핀에 연결합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  17. 우클릭하고 Variables > AI 아래의 Get Blackboard 노드를 추가합니다.
  18. 블랙보드 에서 Set Value as Bool  Set Value as Object 를 사용하여 아래와 같이 연결합니다. 
    이렇게 하면 새로운 값으로 정의된 블랙보드 키(Blackboard Key) 를 업데이트할 수 있습니다. 
  19. Key Name 을 둘 다 우클릭하여 변수 로 승격한 후 각각 HasLineOfSight  EnemyActor 로 이름 짓습니다. 
  20. 블루프린트를 컴파일(Compile) 하고 Key Name  디폴트값(Default Values) 을 각각 HasLineOfSight  EnemyActor 로 설정합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  21. Branch  True 에서 Get EnemyTimer 를 사용하고 Clear and Invalidate Timer by Handle 에 연결합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  22. 그러면 AI가 플레이어를 발견할 때부터 플레이어를 다시 시야에서 놓칠 때까지, 즉 새로운 Line Of Sight Timer가 시작될 때까지 Line Of Sight Timer를 초기화합니다. 
  23. 이미지와 같이 블랙보드 노드, Set Value as  Key Name 노드를 복사하여 붙여넣습니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  24. Set Value as Bool 노드에서 Bool Value 를 활성화하고 이미지와 같이 액터(Actor) 핀을 Object Value 로 드래그합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  25. 그러면 Has Line Of Sight  블랙보드 키 값(Blackboard Key Values)  True 로, EnemyActor 를 우리가 인지한 (플레이어일 경우에만 트리거되도록 구성했던) 액터 로 설정합니다. 
  26. 컴파일 을 클릭하여 컴파일하고 블루프린트를 닫습니다.
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
  27. 최종 그래프는 위와 비슷하게 보여야 합니다.

7 - 데코레이터와 최종 구성

이 마지막 섹션에서는 플레이어 캐릭터와 적 캐릭터 블루프린트의 일부 세팅을 조정합니다. 또한 비헤이비어 트리 에서 특정 조건에 따라 진입할 분기를 정해 줄 데코레이터(Decorator) 를 구성합니다. 

  1. 콘텐츠 드로어(Content Drawer) 에서 콘텐츠(Content) > ThirdPersonBP > Blueprints 경로의 ThirdPersonCharacter 블루프린트를 실행합니다.
  1. 디테일(Details) 패널에서 태그 를 찾아 추가하고 Player 로 설정합니다. 
    이렇게 플레이어 태그를 설정 및 추가하면 AI는 이제 플레이어를 인지하고 반응할 수 있습니다. 
  2. ![ In the Details panel search for and add a Tag set to Playerbtqs-step-6-18.png)
  3. AI 폴더에서 Enemy_Character 블루프린트를 실행합니다. 
  4. 디테일 패널에서 회전(Rotation) 을 찾아 컨트롤러 회전 요 사용(Use Controller Rotation Yaw) 을 활성화합니다. 
    그러면 AI는 비헤이비어 트리 에서 Rotate to Face BB Entry 가 호출될 때 적절하게 회전할 수 있게 됩니다. 
  5. 폰 옵션이 보이지 않나요? 툴바에서 클래스 디폴트(Class Defaults) 버튼을 클릭해야 할 수도 있습니다.
  6. BT_Enemy 를 실행하여 Chase Player 를 우클릭하고 데코레이터 추가...(Add Decorator...) 에서 Blackboard 를 선택합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
    서브 노드설명
    데코레이터(Decorator) 조건식이라고도 합니다. 이 노드는 다른 노드에 어태치되어 트리의 분기나 단일 노드의 실행 여부를 결정합니다.
    서비스(Service) 이 노드는 태스크(Task)  컴포짓(Composite) 노드에 어태치하여 자신의 분기가 실행되는 동안 정의된 주기만큼 실행됩니다. 보통 블랙보드 의 확인 및 업데이트에 사용됩니다. 이 노드는 다른 비헤이비어 트리 시스템에서 전통적인 병렬(Parallel) 노드를 대체합니다.
    여기서는 블랙보드 데코레이터 를 사용하여 블랙보드 키 값을 결정하고 이것이 유효한 경우 해당 분기를 실행할 수 있도록 구성합니다. 
  7. 비헤이비어 트리 에서 노드를 우클릭하면 다음과 같은 추가 기능을 제공하는 서브 노드를 추가할 수 있습니다. 
  8. 추가된 Blackboard Based Condition 을 선택하여 디테일 패널에서 다음과 같이 설정합니다. 
    이미지를 클릭하면 최대 크기로 볼 수 있습니다.
    • 관찰자 중단(Observer aborts) : Both
    • 블랙보드 키(Blackboard Key) : HasLineOfSIght
    • 노드 이름(Node Name) : Has Line of Sight?
    HasLineOfSight 값이 Is Set (또는 true)인 경우 Chase Player 분기를 실행하게 하는 것입니다. Both 로 설정된 관찰자 중단 은 할당한 블랙보드 키 가 변경될 때 자기 자신인 Chase Player 와 우선순위가 자신보다 낮은 태스크를 모두 중단하게 합니다. 즉, HasLineOfSight 값이 변경되었으며 설정되지 않은 경우 자기 자신인 Chase Player 를 중단하고, 그 시점에 다음 분기인 Patrol 을 실행하는 것입니다. HasLineOfSight 값이 Is Set 이 되면 관찰자는 우선순위가 낮은 태스크를 중단하면서 Chase Player 분기를 다시 실행될 수 있게 합니다. 
  9. 컴파일(Compile)  비헤이비어 트리 를 닫고 에디터에서 플레이 합니다.

AIC_Controller에서 AI Perception의 LOS+Player Tag를 감지하면 HasLOS 키를 체크해주고 Object를 EnemyActor로 설정해준다. 아래 트리에서 HasLOS와 Tag가 true가 되면 Chase 시퀀스가 실행되는데 BTT_ChasePlayer에서는 추격을 위해 스피드를 빠르게 해주고 MoveTo로 이동한다. LOS가 없을 경우, BTT_FindRandomPatrol에서 이동가능한 지점을 찾아 이동하고 이걸 반복한다. ThirdPersonPlayer에 Tag를 달아줘야한다.

최종 결과

이제 AI를 테스트해 봐도 좋습니다. AI는 플레이어를 발견하면 따라올 것입니다.

AI는 플레이어가 시야에서 보이지 않게 되어도 플레이어를 추격하며 시야를 확보하려 할 것입니다. Line Of Sight Timer 값으로 입력한 시간이 지나면 AI는 추격을 포기하고 다시 순찰 상태로 돌아갑니다.

플레이 중에 비헤이비어 트리 가 작동하고, 서로 다른 분기 간의 전환이 이루어지는 것을 확인할 수 있습니다. 플레이 중에 비헤이비어 트리 안의 변수들을 지켜보며 AI가 현재 어떤 값을 갖고 있는지 알아볼 수도 있습니다.

8 - 직접 해 보기!

플레이어를 추격하다가 일정 시간 동안 시야에서 놓치면 다시 순찰로 돌아가는 AI 캐릭터가 생겼으니, 여기에 자신만의 비헤이비어 트리 로 다음과 같은 요소를 추가해 볼 수 있습니다!

  • 비헤이비어 트리 에서 두 개의 Move To 태스크에 선행 태스크 의 기능을 포함하는 서비스 를 새로 추가해 봅니다.
    • 원래의 비헤이비어 트리 는 별도의 태스크 를 사용하여 이동 속도를 조정하고(플레이어를 추격할 때), 순찰할 랜덤 위치를 찾을 때는 이동 속도를 낮추었습니다(순찰할 때). 이것을 서비스 로 변환하여 Move To 태스크에 어태치해 봅니다.
    • 힌트: 서비스 의 스크립트는 각 태스크 의 스크립트와 비슷하겠지만, 이벤트 Receive Execute AI 대신 이벤트 Receive Activation AI 를 사용해야 합니다. 이것들은 서비스 이기 때문에 Finish Execute 노드도 사용할 필요가 없습니다.
  • 랜덤 위치를 순찰하는 대신 순찰 경로 블루프린트(Patrol Path Blueprint) 를 생성하여 AI가 왕복할 수 있는 벡터 값 변수의 배열을 추가해 봅니다.
    • 힌트: 배열 내에서의 순환으로 다음 항목을 얻어 AI가 특정 위치 사이를 왕복하게 하고, 배열 내 현재 항목을 베이스로 블랙보드 키  PatrolLocation 을 업데이트합니다.
  • AI가 플레이어를 시야에서 놓친 경우, 플레이어의 현재 위치가 아니라 마지막으로 보았던 플레이어의 위치로 이동하도록 만들어 봅니다.
    • 힌트: Move To 명령으로 돌아서서 플레이어를 추격(Turning and Chasing the Player) 하려면, EnemyActor 대신 특정 위치를 지정해 줘야 합니다. 지금은 EnemyActor 의 위치, 즉 플레이어의 현재 위치를 얻도록 설정되어 있기 때문입니다.
  • AI가 플레이어에게 도달하면 공격하는 기능을 추가해 봅니다.
    • 힌트: 돌아서서 플레이어를 추격  백그라운드 태스크(Background Task) 에 새 컴포짓(Composite) 노드를 추가합니다. AI가 플레이어에게 도달한 후에 공격 애니메이션을 갖춘 태스크 노드를 추가합니다.

'레퍼런스' 카테고리의 다른 글

Behavior Tree EQS - Unreal Document  (0) 2024.04.08
designerd.tistory  (0) 2024.03.29
데미지 프레임워크  (0) 2024.02.03
SetViewPort 카메라 전환하기  (0) 2024.01.07
Montage Notify Interface  (0) 2023.12.31