캐릭터 상태 머신 적용

이득우의 Unreal C++ 게임 개발 표준

구간에 상태기(state machine)가 적용되면서 캐릭터도 상태기(state machine)를 기반으로 동작하도록 변경된다.

  • AI와 플레이어가 같은 캐릭터를 사용하기 때문에 각각의 동작을 차별화해야 합니다.

다음과 같은 상태를 가집니다.

  • PREINIT: 캐릭터 생성 전 초기화 상태.
    캐릭터와 UI를 숨기고 피해를 입지 않습니다.
  • LOADING: 지정된 캐릭터 자산의 로딩 수준입니다.
    자산 로드가 완료될 때까지 컨트롤을 비활성화해야 합니다.
    게임이 이미 시작되었으므로 컨트롤러가 AI인지 플레이어인지 알 수 있습니다.
  • READY: 캐릭터 에셋 로딩이 완료되었습니다.
    이 시점부터 피해를 입을 수 있으며 플레이어 또는 AI가 캐릭터를 제어할 수 있습니다.
  • DEAD: HP가 0 이하로 떨어지면 사망한 상태.
    죽어가는 애니메이션을 재생합니다.
    체력 바 UI가 보이지 않게 되었습니다.
    충돌을 비활성화합니다.
    파손되지 않도록 처리하였습니다.
    스티어링을 비활성화합니다.
    플레이어가 죽으면 설정된 시간 후에 레벨이 다시 시작됩니다.

문자 클래스에서 새 열거형을 정의합니다.

  • BP에서 사용하려면 매크로를 UENUM(BlueprintType) 및 기본 유형으로 uin8로 설정해야 합니다.
  • 쉽게 참조할 수 있도록 캐릭터 클래스가 아닌 메인 게임 모듈 헤더의 아무 곳에나 선언하십시오.


캐릭터의 상태를 업데이트하는 함수와 이를 가져오는 함수를 선언합니다.


캐릭터의 자산 번호, 현재 상태, 플레이어 존재 및 플레이어/AI 컨트롤러 변수를 선언합니다.


사망 후 초 수를 저장하는 DeadTimer 변수와 타이머를 관리하는 FTimerHandle 유형의 핸들을 선언합니다.


필요한 헤더를 추가합니다.


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

기본 문자는 숫자 4라고 합니다.

  • 플레이어는 간단한 캐릭터로 진행하고 AI는 비동기식으로 캐릭터 자산을 임의로 로드합니다.

AActor::SetActorHiddenInGame() 함수를 사용하여 액터를 보이지 않게 만듭니다.

UWidgetComponent::SetHiddenInGame() 함수를 사용하여 위젯을 보이지 않게 만듭니다.

AActor::SetCanBeDamaged() 함수를 사용하여 손상을 방지합니다.

이러한 프로세스는 SetCharacterState(ECharacterState) 함수에서 구현할 수 있습니다.


폰이 레벨에 진입하는 과정은 다음과 같습니다.

  1. 컨트롤러 생성
  2. PostLogin() 호출
  3. 폰 만들기
  4. AController::OnPossess(APown*)
  5. APawn::PossessedBy(AController*)
  6. PostLogin 종료()
  7. 재생 시작()

현재 캐릭터에 설정된 EAutoPossessAI::PlacedInWorldOrSpawned 환경 설정으로 인해 AI 컨트롤러도 자동으로 플레이어에 연결됩니다.

  • 플레이어의 경우 PossessedBy() 함수가 두 번 호출되기 때문에 플레이어인지 정확하게 구분하기 어렵습니다.
  • 따라서 BeginPlay() 함수에서 플레이어가 있는지 없는지 확인합니다.

기존 BeginPlay() 콘텐츠를 제거하거나 주석 처리합니다.

IsPlayerControlled() 함수는 플레이어의 활성 여부를 저장하고 컨트롤러는 각 컨트롤러 변수에 저장됩니다.


UABCharacterSetting 클래스의 CDO(Class Default Object)에서 자산 목록을 가져오고 자산을 비동기적으로 로드합니다.

  • 에셋 로딩 시 LOADING 상태를 유지하다가 Delegate에 등록된 OnAssetLoadCompleted 함수 호출 시 READY 상태로 전환됩니다.



난독화 기능입니다.

  • 반환 유형에 const를 추가한다는 것은 반환 값을 가져올 때 const로 수락해야 하기 때문에 반환 값을 변경할 수 없다는 것을 의미합니다.
  • 함수 끝에 const를 추가하면 함수 내에서 멤버 변수의 값을 변경할 수 없습니다.


SetCharacterState() 함수에서 상태 머신을 본격적으로 작성하기 전에 ABAIController 클래스를 수정하여 상태에 따라 비헤이비어 트리를 수동으로 실행하고 중지합니다.

두 함수 모두 외부에서 호출할 수 있도록 공개 액세스 지정자를 설정합니다.


폰을 소유할 때 동작 트리를 자동으로 실행하는 부분을 주석 처리합니다.


비헤이비어 트리를 실행하는 함수와 중지하는 함수를 구현합니다.

  • UBrainComponent* 유형의 AAIController::BrainComponent는 동작 구성 요소이며 UBehaviorTreeComponent의 부모입니다.
  • UBehaviorTreeComponent::StopTree 함수로 비헤이비어 트리를 멈출 수 있습니다.

EBTStopMode

  • 안전: 안전하게 정지합니다.
  • 강제: 즉시 강제 중지합니다.


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

캐릭터 자산을 불러오는 LOADING 상태에서 플레이어라면 타이핑이 멈춥니다.

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

손상을 방지하기 위해 액터와 체력 막대를 숨깁니다.


READY 상태는 그리기 객체의 비동기 로딩이 완료된 후 본격적인 활동이 시작되는 상태입니다.

액터와 체력 바를 보이게 하고 피해를 입을 수 있습니다.

캐릭터 통계 컴포넌트의 OnHPIsZero 델리게이트에 DEAD 상태로 전환하는 함수를 등록합니다.

캐릭터 통계 구성 요소를 체력 바 UI에 바인딩합니다.

  • 캐릭터 통계 컴포넌트의 OnHPChanged 델리게이트에 상태 표시줄을 업데이트하는 함수를 등록합니다.

게이머라면 DIABLO 모드와 속도를 600으로 설정하고 입력을 활성화하십시오.

AI의 경우 NPC 모드와 속도를 400으로 설정하고 비헤이비어 트리를 실행합니다.


캐릭터가 죽으면 먼저 다음을 수행하십시오.

  • 충돌 비활성화
  • 그물을 숨기다
  • 체력 바 숨기기
  • 죽어가는 애니메이션을 재생합니다.
  • 피해를 입지 않도록 주의하십시오.

플레이어라면 입력이 불가능합니다.

AI의 경우 액션 트리를 멈춥니다.

플레이어인 경우 타이머를 사용하여 DeadTimer 초 후에 레벨을 다시 시작하거나 AI를 사용하는 경우 세계에서 액터를 제거하십시오.

  • APlayerController::RestartLevel() 함수를 사용하여 레벨을 다시 시작할 수 있습니다.
  • SetTimer() 함수의 첫 번째 인수는 FTimerHandle& 유형이므로 FTimerHandle()처럼 즉석에서 만들고 전달할 수 없지만 변수를 만들고 전달해야 합니다.


기존 PossessedBy() 함수의 내용을 주석 처리합니다.


상태 시스템을 캐릭터에 적용했습니다.