언리얼 Character와 시점 구현 정리 (Enhanced Input, Control Rotation, BackView/QuarterView)

언리얼 Character 학습 노트. 강의 5.1~5.6 개념만. Enhanced Input 구조, 회전 옵션 관계, 시점 전환 헷갈릴 때 다시 보기.

Enhanced Input System (왜 쓰는가)

구 입력 시스템은 설정 후 게임 로직에서 입력 값을 처리 → 플레이 중 키 변경 대응 불가. Enhanced Input은 플레이 중에도 입력 설정 변경 가능.

구성 요소(데이터를 함수에 매핑하는 체계):

  • InputAction: 입력을 처리하는 주체. Value Type 지정(Vector2D 등)
  • Modifier: 들어온 입력을 재가공(AD/WS를 X/Y축으로, 값 반전 등)
  • Trigger: 입력 이벤트 활성화 조건(눌렀을 때/일정 이상 눌러야/축 이동 등)
  • InputMappingContext(IMC): 어떤 키가 어떤 액션에 매핑되는지. 플랫폼별로 따로 둘 수 있음
  • InputConfig: 액션들을 모아둔 DataAsset

플러그인 추가 시 .uproject 수정, 모듈 추가 시 해당 모듈의 .Build.cs 수정. (EnhancedInput 모듈을 ShooterX에 추가하려면 ShooterX.Build.cs)

Enhanced Input 연결 흐름

세 단계로 외워두면 됨:

  1. BeginPlay에서 PlayerController의 EnhancedInputLocalPlayerSubsystem을 얻어 AddMappingContext(IMC, 우선순위)
  2. SetupPlayerInputComponent에서 PlayerInputComponent를 UEnhancedInputComponent로 캐스팅
  3. BindAction(액션, ETriggerEvent::Triggered, this, 함수)로 함수 바인드

함정: 블루프린트에서 Override Input Component Class를 EnhancedInputComponent로 지정해야 캐스팅이 성공. 안 하면 캐스팅 실패로 입력이 안 먹음. 입력 처리 함수는 const FInputActionValue& 인자를 받고 Get() 등으로 값 추출.

ACharacter vs APawn

ACharacter는 APawn을 상속한, 인간형 폰을 효과적으로 구현하기 위한 클래스. Capsule/SkeletalMesh는 동일하지만 CharacterMovementComponent를 쓰는 게 핵심 차이.

CharacterMovementComponent vs FloatingPawnMovement:

  • 점프 등 중력 영향 받는 움직임 제공
  • 걷기 외 기어가기/날기/수영 등 MovementMode 제공
  • 멀티플레이에서 캐릭터 간 움직임 자동 동기화

MovementMode(EMovementMode): None(이동 끄기), Walking, Falling 등. MaxWalkSpeed(걷기 속도), JumpZVelocity(점프 속도).

설계 패턴: 캐릭터는 이후 PlayerCharacter/NonPlayerCharacter로 갈리므로 공통 부모 SXCharacterBase를 먼저 만들어둠. 캡슐 크기, 메시 위치/회전 보정, 무브먼트 기본값을 여기에.

Control Rotation: 플레이어의 의지 vs 폰의 물리

핵심 개념 구분:

  • 컨트롤 로테이션: 플레이어 컨트롤러가 관리. 물리 무시한 "플레이어의 의지"(어디를 보고 싶은가)
  • 폰의 속도: 물리 제약이 반영된 "폰의 실제 상황"

확인 방법: 뷰포트 클릭 후 틸드(~) 키로 콘솔 진입, DisplayAll PlayerController ControlRotation 입력.

회전 동기화 속성 관계 (가장 자주 헷갈리는 부분)

Controller ↔ Pawn:

  • bUseControllerRotationYaw(Pitch/Roll): true면 컨트롤 로테이션의 해당 축이 폰 회전에 즉시 동기화. Character는 기본 Yaw true

Pawn ↔ SpringArm:

  • bUsePawnControlRotation: true면 폰(컨트롤) 회전에 스프링암이 연동. false면 폰이 회전해도 스프링암 고정(컷씬용)
  • bInheritPitch/Yaw/Roll: 루트 컴포넌트 회전을 스프링암이 따라갈지
  • bDoCollisionTest: 폰과 카메라 사이 벽 있으면 카메라를 폰 쪽으로 당김

Pawn ↔ Camera:

  • bUsePawnControlRotation으로 결정되지만, 카메라가 스프링암 자식이면 스프링암을 따름

CharacterMovement 회전 옵션:

  • RotationRate: 회전 속도
  • bUseControllerDesiredRotation: 컨트롤 로테이션 방향으로 폰을 RotationRate 속도로 부드럽게 회전
  • bOrientRotationToMovement: 이동 방향으로 폰을 RotationRate 속도로 회전

가장 중요한 함정: 폰 회전 옵션끼리 충돌하면 움직임이 망가짐. bUseControllerRotationYaw(true)와 bOrientRotationToMovement(true)를 동시에 켜면 안 됨. 차이는, 전자는 시선=캐릭터 회전이 항상 동기화, 후자는 이동 키를 눌러야 동기화.

마우스 감도: UE4의 InputYawScale 등은 UE5에서 deprecated. Look 함수 인자 값에 계수를 곱해서 조절.

BackView 구현 핵심

GTA 같은 백뷰. 카메라가 회전한 뒤 그 방향으로 이동.

설정: bUseControllerRotationYaw true, 스프링암 bUsePawnControlRotation true(컨트롤 로테이션→스프링암 동기화), bInheritPitch/Yaw true, bDoCollisionTest true.

함정: bUsePawnControlRotation이 true라 컨트롤 로테이션이 결국 스프링암(=카메라) 회전이 됨. 그래서 SetRelativeRotation은 무의미해짐.

이동 방향 도출: 컨트롤 로테이션이 카메라 방향이므로, 컨트롤 로테이션의 Yaw만으로(Pitch는 비행기 아니니 무시) FRotationMatrix 생성 → X축이 Forward, Y축이 Right. 그 방향으로 AddMovementInput.

이동만 되고 회전이 안 되면 어색 → bOrientRotationToMovement true + RotationRate 지정으로 이동 방향으로 부드럽게 회전(이때 bUseControllerRotationYaw는 false로).

QuarterView 구현 핵심

설정: 스프링암 bUsePawnControlRotation false(폰 회전과 카메라 분리), TargetArmLength 길게, 위에서 내려보는 회전(-45도), bInherit 전부 false, bDoCollisionTest false.

이동 방향 도출이 BackView와 반대:

  • BackView: 컨트롤 로테이션(=카메라)으로 회전 행렬 → 이동 방향
  • QuarterView: 입력 X/Y로 방향 벡터 생성 → 이걸 캐릭터의 의지로 간주 → MakeFromX로 회전 행렬 만들어 컨트롤 로테이션에 대입

MakeFromX/Y/Z: 벡터 하나로 회전 행렬 생성. QuarterView는 입력 합산 벡터와 캐릭터 시선(X축)이 같아야 해서 MakeFromX 사용.

45도 단위로 끊겨 회전하면 → bUseControllerDesiredRotation true로 컨트롤 회전 방향으로 부드럽게 회전(이때 bUseControllerRotationYaw, bOrientRotationToMovement는 false).

함수 호출 순서 (중요)

입력 이벤트 함수 → 액터 Tick → 애니메이션 이벤트 함수.

이유: 플레이어 의지(입력)를 먼저 확인 → Tick에서 그에 대응할 최종 행동 결정 → 결과 보고 애니메이션 처리. QuarterView에서 입력은 변수에만 담고 실제 이동/회전은 Tick에서 처리하는 이유가 이 순서 때문.

함정: 입력 이벤트 함수는 그 입력이 있을 때만 매 프레임 호출됨. 입력 없으면 안 불림. 그래서 지속 처리가 필요한 로직을 입력 함수에만 두면 안 됨.

시점 전환 함정

BackView↔QuarterView 전환 시 회전 동기화 주체가 반대라 그냥 바꾸면 카메라가 튐.

  • BackView: 컨트롤 로테이션 → 스프링암에 동기화
  • QuarterView: 컨트롤 로테이션 ← 폰 회전에서 받아옴

그래서 전환 직전에 컨트롤 로테이션을 미리 맞춰줘야 함:

  • BackView→QuarterView: 컨트롤 로테이션에 폰 회전(GetActorRotation) 세팅
  • QuarterView→BackView: 컨트롤 로테이션에 스프링암 회전 세팅

부드러운 전환: 스프링암 길이/회전을 즉시 바꾸지 말고 목표값(DestArmLength 등)을 두고 Tick에서 InterpTo로 보간. float은 FInterpTo, FVector는 VInterpTo, FRotator는 RInterpTo. InterpTo는 등속으로 목표까지 가다 도달하면 멈춤.

TPS 시점 설정 정리

최종 프로젝트는 TPS. 생성자에서 한 번에 설정:

  • bUseControllerRotationYaw true, bOrientRotationToMovement true(이동 방향으로 캐릭터 회전), RotationRate/MaxWalkSpeed 지정
  • 스프링암 bUsePawnControlRotation true, bInheritPitch/Yaw true, bDoCollisionTest true, 약간 우측 상단 오프셋(어깨너머 시점)
  • 카메라 bUsePawnControlRotation false(스프링암 자식이라 스프링암 따름)
  • 이동은 컨트롤 로테이션 Yaw로 회전 행렬 만들어 Forward/Right 도출(BackView 방식과 동일)

+ Recent posts