[CH4-09] 캐릭터 죽으면 관전자 모드로 변경하기

김여울·2025년 9월 22일

내일배움캠프

목록 보기
81/139

원하는 것 1

  1. 캐릭터가 죽으면 → 메쉬 꺼지고 캡슐 충돌 꺼지고 MovementMode = Flying (이미 되어 있음)

  2. 거기서 스페이스(점프 키) 로 상승하고 WSAD 키로 자유롭게 날아다니면서 구경 모드

  3. 카메라는 블루프린트에 있으니까 C++에서는 굳이 건드리지 않고 조작 로직만 C++ 으로 처리

    ➡ 지금 코드에 관전자 같은 입력 처리를 추가

수정 방법 1

Character.h

  • 죽었을 때 입력 제어할 함수를 추가
void SpectatorMove(const struct FInputActionValue& Value);
void SpectatorAscend(const struct FInputActionValue& Value);

Character.cpp

1️⃣ 입력 바인딩 추가 (SetupPlayerInputComponent)

// 관전 모드 이동 (죽었을 때만 동작)
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorMove);
EIC->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorAscend);

❗MoveAction은 원래 이동에도 쓰이니까 bIsDead 체크로 분기

2️⃣ 함수

void ADCCharacter::SpectatorMove(const FInputActionValue& Value)
{
    if (!bIsDead) return;  // 살아있으면 무시

    const FVector2D Axis = Value.Get<FVector2D>();
    if (Controller)
    {
        const FRotator ControlRot = Controller->GetControlRotation();
        const FVector Forward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
        const FVector Right   = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);

        AddMovementInput(Forward, Axis.Y);
        AddMovementInput(Right, Axis.X);
    }
}

void ADCCharacter::SpectatorAscend(const FInputActionValue& Value)
{
    if (!bIsDead) return;  // 살아있으면 무시

    // 점프 키를 위쪽(Z축 양수) 이동으로 사용
    AddMovementInput(FVector::UpVector, 1.0f);
}

3️⃣ 죽을 때 처리(이미 있음)

void ADCCharacter::OnRep_IsDead()
{
    if (bIsDead)
    {
        GetMesh()->SetVisibility(false);
        GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

        // 죽으면 비행 모드로 전환
        GetCharacterMovement()->SetMovementMode(MOVE_Flying);
    }
}

원하는 것 2

그런데 코드 수정했더니 살아있을 때도 1인칭으로 바뀜

  1. 살아있을 때는 블루프린트로 세팅한 3인칭 카메라 사용
  2. 죽으면 자동으로 1인칭 카메라로 전환 (C++ 코드에서 APlayerController->SetViewTarget 사용)
  3. 동시에 이동 모드 → MOVE_Flying, 입력 처리 → SpectatorMove, SpectatorAscend 사용

수정 방법 2

Character.h

protected:
    /** 입력 핸들러 */
    void Move(const struct FInputActionValue& Value);
    void Look(const struct FInputActionValue& Value);
    void Dance(const struct FInputActionValue& Value);
    void Attack(const struct FInputActionValue& Value);
    void StartSlide(const struct FInputActionValue& Value);

    // 관전 모드 전용 입력 핸들러
    void SpectatorMove(const struct FInputActionValue& Value);
    void SpectatorAscend(const struct FInputActionValue& Value);

    // 1인칭 카메라 컴포넌트
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Camera")
    class UCameraComponent* FirstPersonCamera;

Character.cpp

1️⃣ 생성자에서 카메라 생성

#include "Camera/CameraComponent.h"
#include "GameFramework/PlayerController.h"

ADCCharacter::ADCCharacter()
{
    PrimaryActorTick.bCanEverTick = true;

    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.f);

    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw   = false;
    bUseControllerRotationRoll  = false;

    GetCharacterMovement()->bOrientRotationToMovement = true;
    GetCharacterMovement()->RotationRate = FRotator(0.f, 500.f, 0.f);
    GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;

    // 1인칭 카메라 컴포넌트 생성
    FirstPersonCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
    FirstPersonCamera->SetupAttachment(GetCapsuleComponent()); 
    FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f)); // 눈 위치쯤
    FirstPersonCamera->bUsePawnControlRotation = true;

    // 기본으론 꺼두기
    FirstPersonCamera->SetActive(false);
}

2️⃣ 관전 모드 이동

void ADCCharacter::SpectatorMove(const FInputActionValue& Value)
{
    if (!bIsDead) return;

    const FVector2D Axis = Value.Get<FVector2D>();
    if (Controller)
    {
        const FRotator ControlRot = Controller->GetControlRotation();
        const FVector Forward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
        const FVector Right   = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);

        AddMovementInput(Forward, Axis.Y);
        AddMovementInput(Right, Axis.X);
    }
}

void ADCCharacter::SpectatorAscend(const FInputActionValue& Value)
{
    if (!bIsDead) return;
    AddMovementInput(FVector::UpVector, 1.0f);
}

3️⃣ 죽었을 때 처리(OnRep_IsDead) 수정

void ADCCharacter::OnRep_IsDead()
{
    if (bIsDead)
    {
        // ---- 죽었을 때 ----
        UDCGameInstance* GameInstance = GetWorld() ? Cast<UDCGameInstance>(GetWorld()->GetGameInstance()) : nullptr;
        FString Nickname = GameInstance ? GameInstance->GetNickname() : TEXT("알 수 없는 플레이어");
        UE_LOG(LogTemp, Warning, TEXT("RepNotify: 플레이어 %s가 사망했습니다!"), *Nickname);

        GetMesh()->SetVisibility(false);
        GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

        GetCharacterMovement()->SetMovementMode(MOVE_Flying);

        // 1인칭 카메라 전환
        if (APlayerController* PC = Cast<APlayerController>(Controller))
        {
            if (FirstPersonCamera)
            {
                FirstPersonCamera->SetActive(true);
                PC->SetViewTarget(this);
            }
        }
    }
    else
    {
        // ---- 살아있을 때 (부활 or 시작 시점) ----
        GetMesh()->SetVisibility(true);
        GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);

        GetCharacterMovement()->SetMovementMode(MOVE_Walking);

        // 1인칭 카메라는 꺼두고, BP의 3인칭 카메라 유지
        if (FirstPersonCamera)
        {
            FirstPersonCamera->SetActive(false);
        }

        // 필요하다면 여기서 다시 블루프린트 카메라를 ViewTarget으로 지정
        if (APlayerController* PC = Cast<APlayerController>(Controller))
        {
            PC->SetViewTarget(this); // BP 카메라가 따라붙어 있으면 여기서 3인칭 복귀
        }
    }
}
  1. else 블록을 넣어서 살아있을 때는 1인칭 카메라를 꺼주고, 블루프린트에 세팅된 3인칭 카메라만 활성화되도록 함
  2. 죽을 때 → 1인칭 카메라 활성화
  3. 살아있을 때 → 1인칭 카메라 비활성화

4️⃣ 입력 바인딩 (SetupPlayerInputComponent)도 분기

→ Spectator 전용으로 실행되도록

EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::Move);
EIC->BindAction(LookAction, ETriggerEvent::Triggered, this, &ADCCharacter::Look);

// 죽었을 때 관전 모드 입력
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorMove);
EIC->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorAscend);

여기서 SpectatorMove가 죽었을 때만 동작하도록 if (!bIsDead) return; 으로 막아놔서 충돌 안 남

5️⃣ 이동/점프 분기

  • 살아있을 때 → 기존 Move/Jump 동작
  • 죽었을 때 → 관전자 모드(SpectatorMove, SpectatorAscend)만 동작
void ADCCharacter::Move(const FInputActionValue& Value)
{
    if (bIsDead) return; // 죽었으면 무시
    if (bIsDancing) return;

    const FVector2D Axis = Value.Get<FVector2D>();
    ...
}

void ADCCharacter::SpectatorMove(const FInputActionValue& Value)
{
    if (!bIsDead) return;

    const FVector2D Axis = Value.Get<FVector2D>();
    ...
    AddMovementInput(Forward, Axis.Y);
    AddMovementInput(Right, Axis.X);
}

void ADCCharacter::SpectatorAscend(const FInputActionValue& Value)
{
    if (!bIsDead) return;

    AddMovementInput(FVector::UpVector, 1.0f);
}

// 입력 바인딩
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::Move);
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorMove);

EIC->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorAscend);

관전자 모드 성공 ~

0개의 댓글