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

 

섹션에 상태 머신을 적용했던 것과 마찬가지로 캐릭터도 상태 머신 기반으로 동작하도록 변경한다.

  • AI와 플레이어가 같은 캐릭터를 사용하므로, 각 동작은 구분해야 한다.

 

아래와 같은 상태를 갖도록 한다.

  • PREINIT: 캐릭터 생성 전 초기화 상태.
    캐릭터와 UI를 숨겨두며 데미지를 입지 않는다.
  • LOADING: 지정된 캐릭터 에셋을 로딩하는 상태.
    에셋 로딩이 완료될 때까지 조종을 비활성화 해야한다.
    게임이 이미 시작된 시점이므로 컨트롤러가 AI인지 플레이어인지 구분할 수 있다.
  • READY: 캐릭터 에셋 로딩이 완료된 상태.
    이때부터 데미지를 입을 수 있으며 플레이어나 AI가 캐릭터를 조종할 수 있다.
  • DEAD: HP가 0 이하가 되어 죽은 상태.
    죽는 애니미에션 재생.
    체력바 UI 안 보이게 처리.
    충돌 기능 해제.
    데미지 입지 않도록 처리.
    조종 비활성화.
    플레이어가 죽은 경우 일정 시간 뒤에 레벨을 재시작한다.

 

캐릭트 클래스에 새로운 열거형을 정의한다.

  • BP에서도 사용하기 위해서는 UENUM(BlueprintType) 매크로를 지정해야 하고 기반 유형(Underlying type)을 uin8로 지정해야 한다.
  • 어디서든 참조하기 쉽도록 캐릭터 클래스 내부가 아닌, 주 게임 모듈의 헤더에 선언한다.

 

 

캐릭터의 상태를 갱신하는 함수와 가져오는 함수를 선언한다.

 

 

캐릭터 에셋 번호, 현재 상태, 플레이어 여부, 플레이어/AI 컨트롤러 변수를 선언한다.

 

 

죽은 이후 몇 초 뒤에 이후의 처리를 할 지 저장하는 DeadTimer 변수와 타이머를 관리하는 FTimerHandle 타입의 핸들을 선언한다.

 

 

필요한 헤더를 포함한다.

 

 

생성자에서 기본 상태를 PREINIT으로 지정한다.

 

기본 캐릭터는 4번으로 지정한다.

  • 플레이어는 기본 캐릭터로 진행하게 되고, AI는 랜덤으로 캐릭터 에셋을 비동기 로딩하게 된다.

 

AActor::SetActorHiddenInGame() 함수로 액터를 보이지 않게 처리한다.

UWidgetComponent::SetHiddenInGame() 함수로 위젯을 보이지 않게 처리한다.

AActor::SetCanBeDamaged() 함수로 데미지를 입지 않도록 처리한다.

 

이 과정들은 SetCharacterState(ECharacterState) 함수에 구현해도 된다.

 

 

폰이 레벨에 입장하는 과정은 다음과 같다.

  1. 컨트롤러 생성
  2. PostLogin() 호출
  3. 폰 생성
  4. AController::OnPossess(APawn*)
  5. APawn::PossessedBy(AController*)
  6. PostLogin() 종료
  7. BeginPlay()

 

현재 캐릭터에 설정된 EAutoPossessAI::PlacedInWorldOrSpawned 설정 때문에, 플레이어에도 AI 컨트롤러가 자동으로 붙게 된다.

  • 플레이어에서는 PossessedBy() 함수가 2번 호출되기 때문에 플레이어인지 정확히 구분하기가 어렵다.
  • 따라서, BeginPlay() 함수에서 플레이어 여부를 파악하도록 한다.

현재는 싱글 플레이기 때문에 모든 캐릭터의 초기화를 BeginPlay() 함수에서 하지만, 멀티 플레이 콘텐츠의 경우 초기화는 PossessedBy() 함수에 구현되어야 한다.

 

기존 BeginPlay() 내용은 제거하거나 주석 처리한다.

 

IsPlayerControlled() 함수를 통해 플레이어 여부를 저장하고 각 컨트롤러 변수에 컨트롤러를 저장한다.

 

 

UABCharacterSetting 클래스의 CDO(Class Default Object)에서 에셋 목록을 가져와 에셋을 비동기 로딩한다.

  • 에셋을 로딩하는 동안 LOADING 상태에 머물다가, 델리게이트에 등록된 OnAssetLoadCompleted 함수가 호출되면 READY 상태로 전환된다.

 

 

은닉화를 위한 함수다.

  • 반환 타입에 const를 붙이면 반환 값을 받을 때도 const로 받아야 해서 반환된 값을 수정할 수 없다는 뜻이다.
  • 함수 마지막에 const를 붙이면 이 함수 안에서는 멤버 변수의 값을 변경할 수 없다는 뜻이다.

 

 

SetCharacterState() 함수에서 본격적으로 상태 머신을 작성하기 전에 ABAIController 클래스에서 상태에 따라 수동으로 행동 트리를 실행하고 중지시킬 수 있도록 변경한다.

 

두 함수는 외부에서 호출할 수 있도록 public 접근 지정자를 설정한다.

 

 

폰을 소유할 때 자동으로 행동 트리를 실행하던 부분은 주석처리한다.

 

 

행동 트리를 실행하는 함수와 중지하는 함수를 구현한다.

  • UBrainComponent* 타입의 AAIController::BrainComponent는 행동을 담당하는 컴포넌트로서 UBehaviorTreeComponent의 부모 객체다.
  • UBehaviorTreeComponent::StopTree 함수로 행동 트리를 중지시킬 수 있다.

 

EBTStopMode

  • Safe: 안전하게 중지시킨다.
  • Forced: 강제로 즉시 중지시킨다.

 

 

다시 캐릭터 클래스로 돌아와서 SetCharacterState() 함수에 상태 머신을 설계한다.

 

캐릭터 에셋을 로딩 중인 LOADING 상태에서는 플레이어일 경우 입력을 멈춘다.

  • AI는 RunAI() 함수를 통해 수동으로 행동 트리를 실행한다.

 

액터와 체력바를 숨기며 데미지를 입지 않도록 처리한다.

 

 

READY 상태는 캐릭터 에셋 비동기 로딩이 완료된 후 본격적인 활동이 시작되는 상태이다.

 

액터와 체력바를 보이도록 하고 데미지를 입을 수 있게 된다.

 

캐릭터 스탯 컴포넌트의 OnHPIsZero 델리게이트에 DEAD 상태로 전환하도록 함수를 등록한다.

 

체력바 UI에 캐릭터 스탯 컴포넌트를 바인딩한다.

  • 캐릭터 스탯 컴포넌트의 OnHPChanged 델리게이트에 체력바를 갱신하는 함수를 등록한다.

 

플레이어일 경우, DIABLO 모드와 속도를 600으로 설정하고 입력을 활성화한다.

AI일 경우, NPC 모드와 속도를 400으로 설정하고 행동 트리를 실행한다.

 

 

캐릭터가 죽으면 우선 다음을 수행한다.

  • 충돌을 끈다.
  • 메시를 숨긴다.
  • 체력바를 숨긴다.
  • 죽는 애니메이션을 재생한다.
  • 데미지를 입지 않도록 처리한다.

 

플레이어일 경우, 입력을 비활성화한다.

AI일 경우, 행동 트리를 중지시킨다.

 

타이머를 이용해 DeadTimer초 이후 플레이어면 레벨을 재시작, AI면 액터를 월드에서 제거한다.

  • APlayerController::RestartLevel() 함수를 통해 레벨을 재시작할 수 있다.
  • SetTimer() 함수의 첫 번째 인자는 FTimerHandle& 타입이기 때문에 FTimerHandle()처럼 즉석에서 생성해 전달할 수 없고 변수를 만들어 전달해야 한다.

 

 

기존 PossessedBy() 함수의 내용은 주석 처리한다.

 

 

캐릭터에 상태 머신을 적용하는 데 성공했다.

 

profile

Make Unreal REAL.

@diesuki4

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

검색 태그