하하하! 하루 아침에 사라졌던 관전자 모드 복구를 결국 해결했다!
📎 [CH4-09] 캐릭터 죽으면 관전자 모드로 변경하기
이건 주말에 만들어놨던 관전자 모드...
1. 캐릭터가 죽으면:
2. 살아있을 때:
protected:
// 관전 모드 입력 핸들러
void SpectatorMove(const struct FInputActionValue& Value);
void SpectatorAscend(const struct FInputActionValue& Value);
// 1인칭 카메라 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Camera")
class UCameraComponent* FirstPersonCamera;
// 생성자에서 1인칭 카메라 생성
FirstPersonCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCamera->SetupAttachment(GetCapsuleComponent());
FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f));
FirstPersonCamera->bUsePawnControlRotation = true;
FirstPersonCamera->SetActive(false); // 기본으론 꺼두기
// 입력 바인딩
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorMove);
EIC->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADCCharacter::SpectatorAscend);
// 관전자 모드 이동
void ADCCharacter::SpectatorMove(const FInputActionValue& Value) {
if (!bIsDead) return;
// ... 이동 로직
}
// 죽었을 때 처리
void ADCCharacter::OnRep_IsDead() {
if (bIsDead) {
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);
}
}
}
}
void ADCCharacter::OnRep_IsDead() {
if (bIsDead) {
// 죽었을 때 처리
} else {
// 살아있을 때 처리
GetMesh()->SetVisibility(true);
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
GetCharacterMovement()->SetMovementMode(MOVE_Walking);
// 1인칭 카메라 꺼두기
if (FirstPersonCamera) {
FirstPersonCamera->SetActive(false);
}
}
}
// Character.h에 상태 변수 추가
protected:
UPROPERTY(BlueprintReadOnly, Category="Spectator")
bool bIsSpectatorMode = false;
UPROPERTY(BlueprintReadOnly, Category="Spectator")
bool bSpectatorFreeMove = false;
void ADCCharacter::SpectatorMove(const FInputActionValue& Value) {
if (!bIsDead || !bIsSpectatorMode) return;
const FVector2D Axis = Value.Get<FVector2D>();
if (Controller && Axis.SizeSquared() > 0.0f) {
const FRotator ControlRot = Controller->GetControlRotation();
if (bSpectatorFreeMove) {
// 자유 비행: 3D 이동
const FVector Forward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
const FVector Right = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);
AddMovementInput(Forward, Axis.Y);
AddMovementInput(Right, Axis.X);
} else {
// 지상 관전: 수평면만
const FRotator YawOnlyRot = FRotator(0, ControlRot.Yaw, 0);
const FVector Forward = FRotationMatrix(YawOnlyRot).GetUnitAxis(EAxis::X);
const FVector Right = FRotationMatrix(YawOnlyRot).GetUnitAxis(EAxis::Y);
AddMovementInput(Forward, Axis.Y);
AddMovementInput(Right, Axis.X);
}
}
}

// OnRep_IsSliding 수정: 죽었을 때는 슬라이딩 처리 안함
void ADCCharacter::OnRep_IsSliding() {
if (GetCharacterMovement() == nullptr || bIsDead) return; // bIsDead 체크 추가
if (bIsSliding) {
GetCharacterMovement()->MaxWalkSpeed = SlideSpeed;
} else {
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed; // 명시적 복구
}
}
// OnRep_IsDead에서 위치 조정
void ADCCharacter::OnRep_IsDead() {
if (bIsDead) {
// 현재 위치를 위로 올려서 땅에 묻히지 않게 함
FVector CurrentLocation = GetActorLocation();
SetActorLocation(CurrentLocation + FVector(0, 0, 100.f));
// 중력 및 관성 제거
GetCharacterMovement()->GravityScale = 0.0f;
GetCharacterMovement()->BrakingDecelerationFlying = 2000.0f;
// 회전 제한 해제
GetCharacterMovement()->bOrientRotationToMovement = false;
}
}
// SetupPlayerInputComponent: 중복 바인딩 제거
void ADCCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
// 하나의 함수에서 모든 상황 처리
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADCCharacter::Move);
EIC->BindAction(LookAction, ETriggerEvent::Triggered, this, &ADCCharacter::Look);
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ADCCharacter::HandleJumpInput);
}
}
// Move 함수에서 통합 처리
void ADCCharacter::Move(const FInputActionValue& Value) {
const FVector2D Axis = Value.Get<FVector2D>();
// 관전자 모드 처리
if (bIsDead && bIsSpectatorMode) {
if (Controller && Axis.SizeSquared() > 0.0f) {
const FRotator ControlRot = Controller->GetControlRotation();
if (bSpectatorFreeMove) {
// 자유 비행 모드
const FVector Forward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
const FVector Right = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);
AddMovementInput(Forward, Axis.Y);
AddMovementInput(Right, Axis.X);
} else {
// 지상 관전 모드
const FRotator YawOnlyRot = FRotator(0, ControlRot.Yaw, 0);
const FVector Forward = FRotationMatrix(YawOnlyRot).GetUnitAxis(EAxis::X);
const FVector Right = FRotationMatrix(YawOnlyRot).GetUnitAxis(EAxis::Y);
AddMovementInput(Forward, Axis.Y);
AddMovementInput(Right, Axis.X);
}
}
return;
}
// 일반 이동 처리
if (bIsDead || bIsDancing) return;
// ... 기존 이동 로직
}

// OnRep_IsDead에서 카메라 위치 수정
void ADCCharacter::OnRep_IsDead() {
if (bIsDead) {
// ... 기존 코드
// === 카메라 전환 ===
if (APlayerController* PC = Cast<APlayerController>(Controller)) {
if (FirstPersonCamera) {
FirstPersonCamera->SetActive(true);
PC->SetViewTarget(this);
// 카메라를 캐릭터 중심이 아닌 눈 높이로
FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f)); // 원래대로
}
}
} else {
// 부활 시에도 카메라 위치 복구
if (FirstPersonCamera) {
FirstPersonCamera->SetActive(false);
FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f));
}
}
}
죽은 펭귄 시점 → 1인칭 제대로 작용
생존한 펭귄 시점 → 생존자 수 제대로 반영되고 죽은 펭귄 메시 안 보임
// 헤더에 추가
void HandleJumpToggle(const FInputActionValue& Value); // Started용
void HandleJumpTriggered(const FInputActionValue& Value); // Triggered용
void HandleJumpStop(const FInputActionValue& Value); // Completed용
// 입력 바인딩
void ADCCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ADCCharacter::HandleJumpToggle);
EIC->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADCCharacter::HandleJumpTriggered);
EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &ADCCharacter::HandleJumpStop);
}
}
// 구현
void ADCCharacter::HandleJumpToggle(const FInputActionValue& Value) {
if (bIsDead && bIsSpectatorMode && !bSpectatorFreeMove) {
// 자유 비행 모드 토글 (한 번만)
SpectatorToggleFreeMove(Value);
}
}
void ADCCharacter::HandleJumpTriggered(const FInputActionValue& Value) {
if (bIsDead && bIsSpectatorMode && bSpectatorFreeMove) {
// 자유 비행 모드에서 지속적 상승
SpectatorAscend(Value);
} else if (!bIsDead && !GetCharacterMovement()->IsFalling()) {
// 살아있을 때는 일반 점프
Jump();
}
}
void ADCCharacter::HandleJumpStop(const FInputActionValue& Value) {
if (!bIsDead) {
StopJumping();
}
}
void ADCCharacter::SpectatorAscend(const FInputActionValue& Value) {
if (!bIsDead || !bIsSpectatorMode || !bSpectatorFreeMove) return;
AddMovementInput(FVector::UpVector, 1.0f);
}
짜잔~ 이제 스페이스 꾹 누르면 쭉~ 상승함!
관전자 모드 필요한 기능 전부 구현 완료!
너무 좋은글이네요 ^^