Make Unreal REAL.
article thumbnail
이득우의 언리얼 C++ 게임 개발의 정석

 

행동 트리의 추적 분기에서 공격 기능을 추가하기 위해 셀럭터와 시퀀스를 추가하고 아래와 같이 변경한다.

 

 

이번에는 블랙보드 데코레이터를 사용하지 않고 플레이어가 공격 범위 내에 있는지 판단하는 데코레이터를 직접 만들어보도록 한다.

 

 

새로운 클래스를 생성했으니 CoreMinimal.h를 ArenaBattle.h로 변경한다.

 

 

데코레이터 클래스는 CalculateRawConditionValue() 함수를 실행시켜 조건을 확인한다.

  • 이 함수는 const로 선언되어 클래스의 멤버 변수 값을 변경할 수 없다.

 

 

필요한 헤더를 포함한다.

 

 

생성자에서 데코레이터 이름을 설정한다.

 

 

행동 트리를 실행 중인 ControllingPawn과 블랙보드 키에 저장된 플레이어 Target의 거리가 200 이하인지 확인한다.

  • AActor::GetDistanceTo(AActor) 함수를 이용해 액터 사이의 거리를 계산할 수 있다.

 

 

양쪽 하위 노드에 플레이어를 공격할지 추격할지에 대한 데코레이터를 추가한다.

  • 우측 데코레이터는 Inverse Condition에 체크해 false일 때 분기를 타도록 한다.

 

 

플레이어를 공격하기 위한 새로운 태스크를 생성한다.

 

 

CoreMinimal.h를 ArenaBattle.h로 변경한다.

 

 

공격 태스크는 공격 애니메이션이 끝날 때까지 대기해야 한다.

 

따라서, 일단 InProgress를 반환하고 공격이 끝났을 때 FinishLatentTask() 함수를 호출해 태스크를 종료시켜야 한다.

  • 이 함수를 통해 종료시켜주지 않으면 계속 현재 태스크에 머물게 된다.

 

Tick 기능을 활성화하고 IsAttacking 값이 false가 되었을 때 FinishLatentTask() 함수로 태스크를 종료시키도록 한다.

 

 

생성자에서 태스크의 Tick 기능을 활성화하고 IsAttacking 변수의 기본값을 설정한다.

  • bNotifyTick 값이 true이면 TickTask() 함수가 매 Tick마다 실행된다.

 

 

나머지 함수를 작성하기 전에 캐릭터 클래스로 이동해 외부에서도 Attack() 함수를 실행할 수 있도록 하고 공격이 끝났을 경우 태스크에 알릴 수 있어야 한다.

 

캐릭터의 Attack() 함수를 public으로 변경하고 공격이 끝났을 때 발동할 OnAttackEnd 델리게이트를 선언한다.

 

 

Anim Instance의 OnMontageEnded 델리게이트에 등록되어 있는 OnAttackMontageEnded() 함수에 OnAttackEnd 델리게이트에 등록된 함수를 실행하는 부분을 추가한다.

 

 

Attack 태스크로 돌아와 헤더를 추가한다.

 

 

Attack 태스크가 실행되면 캐릭터 클래스의 Attack() 함수를 실행하고 IsAttacking 변수를 true로 만든다.

 

공격이 끝나기 전 태스크가 종료되면 안 되므로, 일단 InProgress를 반환하고 공격이 끝났을 때 IsAttacking 변수를 false로 만들도록 한다.

 

 

IsAttacking 값이 false가 될 때까지 대기하다가 공격이 끝나 true가 되면 FinishLatentTask() 함수를 실행해 태스크를 종료시킨다.

 

 

행동 트리에 Attack 태스크를 추가한다.

 

 

AI가 플레이어를 공격하지만 공격을 시작했을 때의 방향으로만 계속 공격하는 문제가 있다.

 

 

공격 중에 AI의 방향을 플레이어를 보도록 돌리기 위해 새로운 태스크를 생성한다.

 

 

CoreMinimal.h를 ArenaBattle.h로 변경한다.

 

 

생성자와 ExecuteEvent() 함수를 오버라이드 하도록 선언한다.

 

 

헤더를 추가한다.

 

 

생성자에서 태스크의 이름을 변경한다.

 

 

LookVector는 현재 AI가 플레이어를 바라보는 방향이다.

  • Z축은 0으로 만들어 XY 평면으로 정사영시킨다.

 

AI의 앞 방향을 LookVector와 맞추고 싶기 때문에 MakeFromX() 함수를 사용한다.

  • MakreFrom[X, Y, Z] 함수는 정규화된 벡터를 전달하지 않아도 된다.

 

FMath::RInterpTo() 함수를 이용해 2의 속력으로 현재 회전값에서 플레이어를 바라보는 방향으로 DeltaTime만큼 회전했을 때의 회전값을 가져온다.

  • 이 함수는 타이머나 쓰레드를 Run & Forget 방식으로 실행시켜 알아서 돌려주는 함수가 아니다.
  • 현재 Turn 태스크는 나중에 Simple Parallel 컴포짓을 통해 Attack 태스크가 실행 중인 동안 지속적으로 실행되게 된다.
  • 이 함수는 시작과 끝에 Ease가 존재하므로 일정한 속도로 회전하길 원한다면 FMath::RInterpConstantTo() 함수를  사용하도록 한다.

 

AActor::SetActorRotation()으로 회전이 가능한 이유는 옵션에서 Controller의 회전을 따르지 않도록 설정했기 때문이다.

  • Controller의 회전을 따르는 중이라면 AContorller::SetControlRotation() 함수를 사용해야 한다.

 

 

기존의 시퀀스를 Simple Parallel 컴포짓으로 교체한다.

  • 좌측 보라색에 연결된 Main Task가 실행 중인 동안 검은색에 연결된 하위 노드를 백그라운드에서 실행한다.
  • Main Task는 말 그대로 Task만 실행시킬 수 있지만, 우측에는 또 다른 컴포짓도 등록할 수 있다.
  • 우측 하위 노드는 하나의 가지만 가질 수 있다.

 

 

태스크, 데코레이터를 만들고 행동 트리를 활용해 공격 기능을 구현하는 데 성공했다.

 

profile

Make Unreal REAL.

@diesuki4

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그