응용반 4회차: 캐릭터 회전, UObject/Actor 생명주기, 컨트롤 모드

이채원·2026년 9월 17일

TIL

목록 보기
63/77

1. 캐릭터 회전 (옵션 4종 정리)

결국 모든 회전의 기준은 PlayerController가 쥐고 있는 ControlRotation(현재 바라보는 조준 방향)임. 이걸 누가, 어떤 방식으로 따라갈지가 핵심!

  • bUseControllerRotationYaw (APawn 옵션)
  • Pawn의 Yaw가 컨트롤러의 Yaw를 보간 없이 즉시 따라가게 만듦.
  • FPS 게임이나 TPS에서 총 쏠 때, 스트레이프(게걸음) 칠 때 씀.
  • bUseControllerDesiredRotation (CharacterMovement 옵션)
  • 얘도 컨트롤러 방향을 따라가긴 하는데, 차이점은 RotationRate를 써서 부드럽게(점진적으로) 돎.
  • bOrientRotationToMovement (CharacterMovement 옵션)
  • 이건 카메라(컨트롤러) 방향 말고, 실제 이동하는 가속도(Acceleration) 방향으로 몸을 돌림.
  • 기본 3인칭 게임(카메라는 자유롭게 돌고 걷는 방향으로 캐릭터가 쳐다봄)에서 필수.
  • RotationRate
  • 캐릭터가 얼마나 빨리 돌 건지 정하는 초당 회전 각도. MoveComp->RotationRate = FRotator(0.0f, 240.0f, 0.0f); 이런 식으로 씀.

💡 모아보기

옵션어디 소속?무슨 역할?휙 도나? 서서히 도나?
ControlRotationPlayerController컨트롤러가 바라보는 기준 방향-
bUseControllerRotationYawAPawn컨트롤러 Yaw를 몸체 회전에 씀즉시 확 돎
bUseControllerDesiredRotationCharacterMovement컨트롤러 방향 목표로 몸을 돎RotationRate 속도로 서서히
bOrientRotationToMovementCharacterMovement걷거나 뛰는 이동 방향으로 돎RotationRate 속도로 서서히
bUsePawnControlRotationSpringArm카메라가 Pawn의 컨트롤 회전을 쓸지-

3인칭 기본 이동 세팅 예시

APlayerCharacter::APlayerCharacter()
{
    // 캐릭터 몸체가 마우스 휙휙 돌릴 때마다 즉시 돌아가지 않게 막음
    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw   = false;
    bUseControllerRotationRoll  = false;

    UCharacterMovementComponent* MoveComp = GetCharacterMovement();

    // 포인터 체크 안 하면 나중에 무조건 크래시 터짐. 습관화할 것.
    if (MoveComp)
    {
        // 걷는 방향으로 몸 돌리기 ON
        MoveComp->bOrientRotationToMovement = true;

        // 카메라 방향으로 몸 돌리기 OFF
        MoveComp->bUseControllerDesiredRotation = false;

        // 초당 360도로 휙휙 돌게 세팅
        MoveComp->RotationRate = FRotator(0.0f, 360.0f, 0.0f);
    }
}

참고: AI한테 코드 짜달라고 할 때도 "포인터 쓸 때 무조건 예외 처리해 줘"라고 규칙 박아두면 편함. 바이오하자드처럼 제자리 카메라 회전, AAA 게임의 상하/좌우 분리 회전 등 게임마다 감각이 다르니 이 옵션들을 완벽히 알고 있어야 기획 의도대로 뽑을 수 있음.


2. 프레임워크 딥다이브: UObject vs AActor

언리얼 C++ 클래스 앞에 붙는 U, A 접두사 제대로 짚고 넘어가기. C++이라고 냅다 new 썼다가는 큰일 남.

  • UObject: 언리얼 객체의 뿌리. 월드에 굳이 배치될 필요 없는 애들(데이터, 로직 등) 만들 때 씀. NewObject<T>(Outer)로 생성함. (Outer는 이 객체가 어디 소속인지 알려주는 용도)
  • AActor: UObject를 상속받은 놈. 월드에 실제로 스폰돼서 위치(Transform)를 가짐. 월드 등록 절차가 필요해서 GetWorld()->SpawnActor<T>()로 생성.
뭘 만들 건데?무슨 함수 쓸까?
UObjectNewObject<T>(Outer)
ActorGetWorld()->SpawnActor<T>()
ComponentCreateDefaultSubobject<T>(TEXT("이름"))

생명주기와 생성자 함정

  • 흐름: 스폰 →\rightarrow Construction →\rightarrow 컴포넌트 초기화 →\rightarrow BeginPlay →\rightarrow 게임 시작 →\rightarrow EndPlay →\rightarrow GC
  • 생성자에다 로직 넣지 말 것! 에디터 켤 때나 CDO 만들 때도 막 불림. 기본값 세팅이나 컴포넌트 달아주는 것만 하고, 플레이어 찾기나 타이머 같은 진짜 로직은 무조건 BeginPlay에 넣어야 됨.
  • Destroy() ≠ delete: Destroy() 불렀다고 메모리 바로 안 날아감. 월드에서 빼내는 작업만 하고, 찐 메모리 청소는 GC(가비지 컬렉터)가 알아서 함. 그러니까 delete 절대 금지.
  • IsValid() 폼 미쳤음: 포인터 살아있는지 체크할 때 if (Ptr)은 단순 널체크만 되지만, IsValid()는 GC가 지우려고 찜해둔(Pending Kill) 객체까지 다 걸러줌. 크래시 내기 싫으면 무조건 쓸 것.

3. Owner (누구 거냐)

무기를 주웠을 때 Weapon->SetOwner(PlayerCharacter); 처럼 오너를 정해줄 수 있음.

근데 여기서 헷갈리면 안 되는 게, Owner ≠ 메모리 소유권 ≠ Attachment(부착) 임.

  • Owner는 "이 액터가 어느 플레이어/네트워크 커넥션 소속인가"를 따지는 게임플레이/네트워크 소유권임. (멀티 짤 때 RPC나 복제 기준이 됨)
  • 손에 무기 컴포넌트를 AttachToActor()로 붙였다고 해서 Owner가 자동으로 넘어가는 게 아님. 따로 세팅해 줘야 됨.

4. 실습: V키로 4종 컨트롤 모드 깎기

V키 누를 때마다 3인칭 ➔ 1인칭 ➔ 탑다운 ➔ 숄더뷰로 휙휙 바뀌게 만드는 실습.

왜 Enum을 쓰나?
그냥 0, 1, 2 숫자로 쳐도 되지만 블루프린트 연동이랑 유지보수를 위해 uint8 기반 Enum을 씀. (uint8인 이유는 플랫폼 상관없이 1바이트 고정이라 네트워크 터질 일 없고 블플 노출 조건임)

UENUM(BlueprintType)
enum class EPlayerControlMode : uint8
{
    ThirdPerson UMETA(DisplayName = "Third Person"),
    FirstPerson UMETA(DisplayName = "First Person"),
    TopDown     UMETA(DisplayName = "Top Down"),
    Shoulder    UMETA(DisplayName = "Shoulder / Aim")
};

설계 철학: 책임 분리(SRP)
모드 바꾸는 함수 하나에 다 때려 넣지 말고, 다음 모드가 뭔지 고르는 놈(ChangeControlMode)이랑, 그 모드 스펙 덮어씌우는 놈(ApplyControlMode)을 쪼갬.

// V키 바인딩 (입력 액션 프로퍼티는 GC 안 당하게 TObjectPtr + UPROPERTY 달아둠)
if (ChangeControlModeAction)
{
    EnhancedInputComponent->BindAction(
        ChangeControlModeAction,
        ETriggerEvent::Started,
        this,
        &AMyCharacter::ChangeControlMode
    );
}

void AMyCharacter::ChangeControlMode()
{
    switch (CurrentControlMode)
    {
    case EPlayerControlMode::ThirdPerson: ApplyControlMode(EPlayerControlMode::FirstPerson); break;
    case EPlayerControlMode::FirstPerson: ApplyControlMode(EPlayerControlMode::TopDown); break;
    case EPlayerControlMode::TopDown:     ApplyControlMode(EPlayerControlMode::Shoulder); break;
    case EPlayerControlMode::Shoulder:    ApplyControlMode(EPlayerControlMode::ThirdPerson); break;
    default:                              ApplyControlMode(EPlayerControlMode::ThirdPerson); break;
    }
}

모드별 세팅값 (ApplyControlMode 내부 로직)

주의: 아래 값들은 절대적인 정답이 아니라 베이스라인임. 자기 기획서나 게임 장르에 맞게 FOV, Arm 길이 등 직접 만져봐야 됨.

  • 방어 코드 필수: if (!MovementComponent || !CameraBoom || !FollowCamera) return; 깔고 시작할 것.
건드린 변수들3인칭 (이동 위주)1인칭 (조준 위주)탑다운 (이동 위주)숄더 (조준 위주)
SetUsingAbsoluteRotationfalsefalsetrue (캐릭터 돌아도 카메라는 월드 고정)false
bUsePawnControlRotationtruetruefalsetrue
bDoCollisionTesttruefalsetruetrue
TargetArmLength4000900220
TargetOffset(0,0,60)(0,0,BaseEyeHeight)Zero(0,0,60)
SocketOffsetZeroZeroZero(0,70,0) (어깨너머)
SetWorldRotation--(-60,-45,0) (내려다보기)-
SetFieldOfView(FOV)90907080
bUseControllerRotationYawfalsetruefalsetrue
bOrientRotationToMovementtruefalsetruefalse
SetOwnerNoSee(Mesh)falsetrue (내 몸 숨기기)falsefalse

꿀팁: 카메라 그냥 캐릭터에 갖다 붙이면 되지 왜 굳이 SpringArm 쓰냐고? 벽 등지고 섰을 때 카메라가 벽 뚫고 나가는 거나 반전되는 거 SpringArm이 알아서 보정해 줘서 씀.


요약

  • 회전 메커니즘: 누가 돌 건가(Pawn vs Movement) + 뭘 기준으로 돌 건가(Control vs Acceleration) + 바로 돌까 천천히 돌까(RotationRate)의 조합.
  • 생성/소멸 규칙: 만들 땐 용도에 맞춰 함수 가려 쓰고(NewObject/SpawnActor), 지울 땐 delete 대신 Destroy(). 널체크는 무조건 IsValid().
  • 오늘의 과제: 이동+회전 구현하고, 4종 모드 전환 직접 손에 익혀보기 (수업 코드 그냥 복붙하지 말고 수치 직접 다 비틀어볼 것)
  • 태도: AI가 짜준 코드라도 무조건 내 눈으로 읽고 이해해야 됨. 피드백이나 코드 리뷰 무서워하면 개발자 못 함. 맞으면서 크는 거임!
profile
초보 개발자

0개의 댓글