[UE5] 적 AI 만들기 (1)

재석 블로그·2025년 2월 22일
post-thumbnail

기초적인 수준의 적 설계하기

  • 플레이어를 쫓아와 공격하는 간단한 로직
  • BP_BaseEnemyCharacter를 만들어 아래와 같이 2개의 커스텀 이벤트를 설정한다.
  • Chase Player - AI Move To로 플레이어에게 이동, 이동 성공시 커스텀 함수 Attack 호출 - 2초 Delay 후 자기 자신 호출
  • Attack - 지금으로선 그냥 공격 모션만. Play MontagePlay Anim Montage가 있는데 Play Montage가 아웃풋 핀이 더 다양해서 좋다.

Behavior Tree

  • 당연히 위와 같은 방식으로는 한계가 있으며.. 그저 정해진 대로만 행동하기 때문에 상황에 따라 스스로 판단하는 인공지능이라 할 수 없다.
  • 이를 위해 이미 학습한 바가 있는 Behavior Tree를 다시 만들어볼 것이다.

Selector 와 Sequence

  • SelectorSequence
    목적여러 하위 노드 중
    하나의 노드 실행
    모든 하위 노드를
    순차적으로 실행
    작동
    방식
    왼쪽부터 순차적으로 실행
    하나의 노드가 성공적으로 실행되면
    나머지 노드는 무시
    왼쪽부터 순차적으로 실행
    하나라도 실패시
    시퀀스 전체가 실패한 것으로 간주
    용도여러 행동 중 하나를 선택할 때
    예)공격, 방어, 도주 중 하나를 고르는 경우
    일련의 행동을 순차적으로 실행할 때
    예)경로를 따라 순찰 후 대기, 다시 순찰

이벤트 디스패처

  • BT를 위와 같이 생성했다. 위의 적 캐릭터의 이벤트그래프에 생성한 것과 똑같은 내용이다. 이동-공격을 순차적으로 반복할 것이나 막상 실행해보면 공격 모션이 실행되지 않는다.

  • 위를 보면 이유를 알 수 있다.
    - DefaultAttack 태스크는 Attack 함수를 실행하고 바로 태스크 실행을 종료한다.
    - Attack 함수 역시 별다른 설정 없이 몽타주를 실행만 하고 종료가 된다.
    - 즉 몽타주를 재생해라는 명령만 입력되고, 몽타주가 끝날 때까지 기다려라는 명령은 없어서 발생한 문제.
  • 이 문제를 해결하기 위해 이벤트 디스패처를 설정해보자.

Event Dispatcher

  • 이벤트 기반 프로그래밍 구현을 위해 사용되는 언리얼의 기능
  • C++ 에선 델리게이트로 불린다
  • 직접적으로 다른 객체를 참조하지 않고도 객체간 상호작용이 가능해짐

  • 적 캐릭터의 이벤트그래프에서 OnAttackEnd 라는 이름의 이벤트 디스패처를 생성한다. Attack함수의 몽타주 재생이 끝나거나, 방해받을 경우 이벤트 디스패처가 호출된다.
  • 어택 태스크로 돌아와 Attack 함수가 종료 된 후 FinishedAttacking이라는 이름의 이벤트 핸들러가 실행되도록 설정한다.

Focus 설정

  • 또다른 적이 공격하는 것까지 자연스럽게 되는데, 다른 문제가 생겼다. 적이 플레이어를 바라보지 않는다. 이동 완료로 판정되는 거리만 충족되면 방향 전환없이 계속 재생한다.
  • FocusTarget 태스크를 만들어서 SetFocus 노드를 사용하면 계속 플레이어를 쳐다보도록 할 수 있다.

  • 최종적으로 이러한 BT 구조를 갖게 된다.
  • 다만 공격 후 이동할 때도 계속 포커스를 유지하게 되니 ClearFocus 태스크를 이동 전에 붙여서 자연스럽게 이동하도록 한다.

커스텀 데코레이터 추가

  • 스켈레탈 메시에서 오른손 쪽에 소켓을 만들고 칼 에셋을 붙여본다.
  • 커스텀 함수 WieldSword를 만들어서 Attach Actor to Component 노드로 스켈레탈 메시의 소켓에 Spawn Actor from Class를 붙인다. 핀의 마지막엔 IsWieldingSword 변수가 true가 되도록 만든다.
  • 위는 WieldSword 태스크의 구조

  • IsWieldingSword의 상태를 체크하기 위해 커스텀 데코레이터를 만든다. 구조는 위와 같다. BP_BaseEnemyCharacterIsWieldingSword 변수 값에 따라 BTTask_WieldSword를 실행할지 말지가 결정된다.
  • 데코레이터를 커스텀이 가능하다는 것과 방법을 알 수 있었다.




Spline으로 순찰 경로 만들기

앞에서 순찰 경로를 만드는 법으로 Target Point 를 이용해 순찰 경로를 지정하는 것을 배웠다. 그러나 그렇게 할 경우 포인트가 많으면 그만큼 아웃라이너도 많아진다.
Spline을 이용하면 액터 하나가 전체의 순찰 경로와 포인트를 나타내기 때문에 좀 더 깔끔하게 나타낼 수 있다. (가시성도 조금 더 좋다)

이벤트그래프

  • BP_PatrolRoute라는 이름의 액터를 만든다. 컴포넌트는 Spline Component 하나만 추가한다.
  • 최종적으로 이벤트그래프의 이벤트 IncrementPointRoute는 위와 같이 된다.
  1. Target Point를 이용한 순찰 경로와 동일하게 Spline의 각각의 지점을 Index로 활용한다.
  2. 현재 포인트를 나타내는 Patrol Index와 순찰 방향을 나타내는 Direction을 만든다.
  3. Get Number of Spline Points 노드로 Spline의 포인트 갯수를 구하는데, 현재 인덱스가 이와 일치하게 되면 순찰 루트의 마지막에 도착했다는 뜻이니, Direction을 -1로 바꿔 다시 되돌아간다.
  4. 매번 현재 인덱스를 검사하여 Patrol Index가 0이라면 방향을 다시 1로 수정하여 순찰을 시작하도록 한다.

Get Spline Point as World Position

  • 이벤트그래프에서 순찰 방향에 관한 부분을 정의했으니, 이동을 위한 좌표를 구하는 함수를 하나 만든다. 이동할 Patrol Index가 어떤 좌표인지 알려줘야 한다.
  • 이 함수는 Get Location at Spline Point를 이용해 해당 지점의 위치를 vector로 반환한다.

순찰 태스크에서 합치기

  • 이제 위에서 만든 함수와 이벤트를 통해 순찰 태스크에서 실질적으로 Spline Point를 따라 순찰을 하도록 만들자.
  1. GetPatrolRoute는 인터페이스의 함수로, 적 클래스 공통된 기능이기에 인터페이스에서 정의하였다.
  2. GetPatrolRoute로 적 캐릭터가 가진 PatrolRoute의 Validity를 검사하고, 유효하다면 Get Spline Point as World Position에서 반환한 Patrol Index의 월드 좌표로 이동한다. (초기 인덱스는 0이다. 첫번째 포인트의 월드 좌표로 이동하는 것이다.)
  3. AI Move To로 이동하는데, 완료하거나 실패하면 IncrementPatrolRoute가 호출되어 현재 PatrolIndex가 1 증가한다.

  • 위에서 설정한 일련의 설정을 순찰 Behavior SubTree에 만든다.
    후에 Behavior Tree가 커지게 되면 가시성을 위해 트리를 쪼개게 된다. 이렇게 쪼갠 트리는 SubTree가 되며, 아래와 같이 다른 Behavior Tree의 노드가 될 수 있다.



이동속도 조절하기

적의 이동속도를 조절하는 것도 태스크로 만들어 매 패턴마다 이동속도를 다르게 지정해줄 수 있다. 이동 속도의 경우 유형을 Enum 타입으로 미리 지정해두었다.

  • 위와 같이 적의 이동속도는 4가지로 지정하였다.

  • 캐릭터 클래스에 SetMovementSpeed 함수를 작성한다. 이 함수는 Enum 타입을 입력받고, float타입의 속도를 출력할 것이다.
  • 캐릭터의 Character Movement 컴포넌트에서 Set Max Walk Speed를 꺼내, Max Walk Speed의 값이 Enum중에서 선택되도록 한다.
  • 캐릭터의 단계별 이동속도를 지정해준다. 그리하여 해당 값을 Speed로 반환하게 된다.
  • 이제 BTTask_SetMovementSpeed를 만들어, 태스크가 실행되면 위에서 만든 SetMovementSpeed가 실행되도록 한다.
  • 주의 사항 특정 패턴 동안만 이동속도를 조절하고 싶다면 패턴이 끝나고 이동속도를 되돌려줘야 한다. 아니면 모든 패턴의 처음에 이 태스크를 넣든가.


profile
게임 개발자 재석의 블로그입니다.

0개의 댓글