1. 개요
언리얼 엔진에서 캐릭터의 회전을 제어할 때 APawn의 bUseControllerRotation 계열 옵션과 UCharacterMovementComponent의 bUseControllerDesiredRotation 옵션은 매우 혼동하기 쉽다. 두 옵션 모두 컨트롤러(AController)의 회전 값을 액터(Actor)의 회전에 반영한다는 공통점이 있지만, 동작 방식과 주체, 그리고 결과에서 명확한 차이를 보인다.
2. APawn::bUseControllerRotation (Pitch, Yaw, Roll)
이 속성들은 APawn 클래스에 직접 정의되어 있으며, bUseControllerRotationPitch, bUseControllerRotationYaw, bUseControllerRotationRoll 세 가지 bool 플래그로 구성된다. 이 플래그가 true로 설정되면, 폰의 회전은 매 틱(Tick)마다 컨트롤러의 회전(ControlRotation)으로 즉시, 강제로 설정된다.
2.1. 내부 구조 및 구현 방식 (엔진 코드 레벨)
bUseControllerRotation의 핵심 로직은 APawn::FaceRotation 함수 내에 있다.
APawn::Tick(): 매 프레임 폰의Tick함수가 호출될 때, 특정 조건 하에서FaceRotation()함수를 호출한다.APawn::FaceRotation(FRotator NewControlRotation, float DeltaTime): 이 함수는 컨트롤러의 새로운 회전 값을 인자로 받는다.- 플래그 확인: 함수 내부에서는
bUseControllerRotationPitch,bUseControllerRotationYaw,bUseControllerRotationRoll플래그를 각각 확인한다. - `SetActorRotation()`: 변경된 회전 값(CurrentRotation)이 AActor::SetActorRotation() 함수를 통해 액터에 직접 적용된다. 이 과정에는 어떠한 보간(Interpolation)도 포함되지 않으므로, 회전은 즉각적으로 반영된다
// Engine/Source/Runtime/Engine/Private/Pawn.cpp
void APawn::FaceRotation(FRotator NewControlRotation, float DeltaTime)
{
// 현재 액터의 회전 값을 가져옴
FRotator CurrentRotation = GetActorRotation();
// bUseControllerRotationYaw 플래그가 true이면, 컨트롤러의 Yaw 값을 액터의 Yaw 값으로 설정
if (bUseControllerRotationYaw)
{
CurrentRotation.Yaw = NewControlRotation.Yaw;
}
// Pitch와 Roll도 동일한 방식으로 처리
if (bUseControllerRotationPitch)
{
CurrentRotation.Pitch = NewControlRotation.Pitch;
}
if (bUseControllerRotationRoll)
{
CurrentRotation.Roll = NewControlRotation.Roll;
}
// 최종적으로 계산된 회전 값을 액터에 적용
if (CurrentRotation != GetActorRotation())
{
SetActorRotation(CurrentRotation);
}
}
2.2. 동작 계층 구조
bUseControllerRotation의 동작은 다음과 같은 계층을 통해 이루어진다.
- 사용자 입력 계층 (Hardware/OS): 플레이어가 마우스, 키보드, 게임패드 등으로 입력을 생성한다.
- 입력 처리 계층 (PlayerController):
APlayerController가 하드웨어 입력을 받아AddControllerYawInput,AddControllerPitchInput등의 함수를 통해 자신의ControlRotation멤버 변수를 업데이트한다. - 폰 틱 계층 (Pawn):
APawn::Tick()함수가 실행되면서Controller->GetControlRotation()으로 업데이트된ControlRotation을 가져온다. - 회전 적용 계층 (Pawn):
APawn::FaceRotation()함수가bUseControllerRotation플래그를 확인하고,true일 경우SetActorRotation()을 호출하여 폰의 트랜스폼을 직접 수정한다.
이 방식은 컨트롤러의 회전이 폰의 회전을 1:1로, 지연 없이 '지배'하는 구조다.
3. UCharacterMovementComponent::bUseControllerDesiredRotation
이 속성은 ACharacter가 기본으로 사용하는 UCharacterMovementComponent에 정의된 bool 플래그이다. 이 플래그가 true가 되면, 캐릭터는 컨트롤러의 ControlRotation을 '목표(Desired) 회전'으로 설정하고, 현재 회전 값에서 목표 회전 값까지 부드럽게 보간하며 회전한다.
3.1. 내부 구조 및 구현 방식 (엔진 코드 레벨)
bUseControllerDesiredRotation의 로직은 UCharacterMovementComponent의 틱 함수, 특히 PhysicsRotation에서 처리된다.
UCharacterMovementComponent::TickComponent(): 컴포넌트의 틱 함수는 캐릭터의 이동 및 회전을 포함한 물리 계산을 총괄한다.ControlledCharacterMove(): 틱 함수 내에서 호출되며, 여기서 회전 방향이 결정된다.bUseControllerDesiredRotation이true이면, 컨트롤러의 회전(ControlRotation)이 목표 회전으로 설정된다.UCharacterMovementComponent::PhysicsRotation(float DeltaTime): 실제 회전 보간이 일어나는 곳이다.RotationRate:UCharacterMovementComponent의FRotator RotationRate멤버 변수는 각 축(주로 Yaw)의 최대 회전 속도를(degrees/sec)단위로 정의한다.PhysicsRotation은 이 값을 사용하여FMath::RInterpTo함수로 점진적인 회전을 구현한다.
// Engine/Source/Runtime/Engine/Private/CharacterMovementComponent.cpp
void UCharacterMovementComponent::ControlledCharacterMove(const FVector& InputVector, float DeltaSeconds)
{
// ... 다른 이동 로직 ...
// bUseControllerDesiredRotation이 true이고 캐릭터가 움직일 때
if (bUseControllerDesiredRotation && !InputVector.IsNearlyZero())
{
// 컨트롤러의 회전을 목표 회전으로 설정
DesiredRotation = CharacterOwner->GetControlRotation();
}
// ...
}
// Engine/Source/Runtime/Engine/Private/CharacterMovementComponent.cpp
void UCharacterMovementComponent::PhysicsRotation(float DeltaTime)
{
// ...
// 목표 회전(DesiredRotation)이 현재 회전과 다른 경우에만 실행
if (!CurrentRotation.Equals(DesiredRotation, 0.01f))
{
// 이번 프레임에 회전할 수 있는 최대 각도
const float TurnSpeed = GetMaxTurnSpeed(); // RotationRate.Yaw 값 기반
// RInterpTo는 현재 값에서 목표 값까지 일정 속도로 보간하는 함수
FRotator NewRotation = FMath::RInterpTo(CurrentRotation, DesiredRotation, DeltaTime, TurnSpeed);
// MoveUpdatedComponent를 통해 부드럽게 회전 적용
MoveUpdatedComponent(FVector::ZeroVector, NewRotation, true);
}
}
3.2. 동작 계층 구조
bUseControllerDesiredRotation의 동작 계층은 다음과 같다.
- 사용자 입력 계층 (Hardware/OS): 동일.
- 입력 처리 계층 (PlayerController): 동일.
ControlRotation업데이트. - 컴포넌트 틱 계층 (CharacterMovementComponent):
UCharacterMovementComponent::TickComponent()가 실행된다. - 목표 설정 계층 (CharacterMovementComponent):
bUseControllerDesiredRotation플래그를 확인하고,ControlRotation을 '목표 회전'으로 내부 변수에 저장한다. - 회전 보간 계층 (CharacterMovementComponent):
PhysicsRotation()함수가 현재 회전과 목표 회전, 그리고RotationRate를 사용하여 이번 프레임에 적용할 회전 값을 '계산'한다. - 이동 및 적용 계층 (CharacterMovementComponent):
MoveUpdatedComponent()함수가 계산된 회전 값을 캐릭터에 적용하여 부드러운 회전을 만들어낸다.
이 방식은 CharacterMovementComponent가 중간에서 '중재자' 역할을 하여, 컨트롤러의 회전 목표를 점진적으로 따라가는 구조다.
4. 핵심 차이점 비교 및 사용 사례
| 구분 | bUseControllerRotation |
bUseControllerDesiredRotation |
|---|---|---|
| 적용 주체 | APawn |
UCharacterMovementComponent |
| 동작 방식 | 직접적, 강제적 (Direct, Forced) | 간접적, 보간 (Indirect, Interpolated) |
| 결과 | 즉각적이고 기계적인 회전 | 부드럽고 자연스러운 회전 |
| 관련 속성 | 없음 | RotationRate (회전 속도) |
| 주요 제어 축 | Pitch, Yaw, Roll 모두 가능 | 주로 Yaw (캐릭터의 좌우 회전) |
4.1. 장단점 및 사용 사례
bUseControllerRotation
- 장점:
- 최고의 반응성: 입력과 동시에 회전이 완료되므로 지연이 전혀 없다.
- 간단한 설정: 플래그 하나만 켜면 즉시 동작한다.
- 단점:
- 부자연스러움: 회전 보간이 없어 움직임이 딱딱하고 기계적으로 보인다.
- 애니메이션과 부조화: 부드러운 회전 애니메이션과 함께 사용하기 어렵다.
- 주요 사용 사례:
- 1인칭 슈팅(FPS) 게임: 플레이어의 시점(카메라)과 캐릭터의 상체가 컨트롤러 방향과 항상 일치해야 할 때. 보통
bUseControllerRotationYaw는false로 두고 캐릭터의 회전은MovementComponent에 맡기고,bUseControllerRotationPitch만true로 하여 카메라의 상하 회전을 구현한다. - 비행 시뮬레이션: 조종사의 시점이 기체의 회전과 직접 연결될 때.
- 관찰 카메라(Spectator Pawn): 맵을 자유롭게 둘러보는 카메라처럼 즉각적인 제어가 필요할 때.
- 1인칭 슈팅(FPS) 게임: 플레이어의 시점(카메라)과 캐릭터의 상체가 컨트롤러 방향과 항상 일치해야 할 때. 보통
bUseControllerDesiredRotation
- 장점:
- 자연스러운 움직임:
RotationRate에 따라 캐릭터가 현실적으로 몸을 돌리는 듯한 느낌을 준다. - 애니메이션 친화적: 회전 속도에 맞춰 발을 움직이는 등 자연스러운 회전 애니메이션을 블렌딩하기 용이하다.
- 자연스러운 움직임:
- 단점:
- 느린 반응성:
RotationRate값에 따라 반응이 한 박자 느리게 느껴질 수 있다. - 추가 설정 필요: 원하는 느낌을 얻기 위해
RotationRate값을 세심하게 조정해야 한다.
- 느린 반응성:
- 주요 사용 사례:
- 3인칭 슈팅(TPS) / 액션 게임: 카메라 방향에 따라 캐릭터가 자연스럽게 몸을 돌려야 할 때. 예를 들어, 오른쪽으로 카메라를 돌리면 캐릭터가 그 방향으로 스르륵 몸을 트는 연출.
- AI 캐릭터: AI가 목표물을 향해 몸을 돌릴 때 자연스러운 움직임을 부여하고 싶을 때.
- 탑다운 뷰 게임: 마우스 커서나 조이스틱 방향으로 캐릭터가 부드럽게 회전해야 할 때.
4.2. 두 옵션의 조합
두 옵션은 종종 함께 사용된다. 가장 대표적인 예는 3인칭 게임 캐릭터이다.
bUseControllerRotationYaw = falseGetCharacterMovement()->bOrientRotationToMovement = true또는GetCharacterMovement()->bUseControllerDesiredRotation = trueAPawn::bUseControllerRotationPitch = true(카메라 암(Camera Boom)에 적용)
위와 같이 설정하면,
- 캐릭터의 좌우 회전(
Yaw)은 컨트롤러 방향을 부드럽게 따라가거나(bUseControllerDesiredRotation), 혹은 캐릭터가 이동하는 방향을 바라보게(bOrientRotationToMovement) 된다. - 카메라의 상하 회전(
Pitch)은 컨트롤러의 움직임에 즉각적으로 반응하여, 플레이어가 위아래를 조준하는 대로 카메라 각도가 바로 변경된다.
5. 요약
두 옵션의 가장 큰 차이는 "회전을 즉시 적용하느냐, 아니면 목표로 삼고 부드럽게 따라가게 하느냐" 입니다.
bUseControllerRotation은 폰의 회전을 컨트롤러의 회전 값에 매 프레임 강제로 '고정'시키는 옵션입니다. 그래서 1인칭 게임처럼 카메라와 몸이 즉각적으로 함께 돌아야 할 때처럼 아주 빠른 반응이 필요할 때 사용합니다.
반면에 bUseControllerDesiredRotation은 캐릭터 무브먼트 컴포넌트가 컨트롤러의 회전을 '목표 지점'으로 삼고, 우리가 설정한 회전 속도(RotationRate)에 맞춰 캐릭터를 부드럽게 그쪽으로 회전시키는 방식입니다. 3인칭 게임에서 플레이어가 카메라를 돌렸을 때 캐릭터가 현실적으로 몸을 스르륵 트는 모습을 생각하시면 쉽습니다.
따라서 즉각적이고 기계적인 반응이 필요하면 bUseControllerRotation을, 자연스럽고 사실적인 회전 연출이 필요하면 bUseControllerDesiredRotation을 사용한다고 정리할 수 있습니다.
'Unreal' 카테고리의 다른 글
| 캐릭터의 상대 위치 판단 방법 (0) | 2025.11.28 |
|---|---|
| 언리얼 엔진 가비지 컬렉션 (0) | 2025.11.17 |
| NetMode, NetConnection, NetDriver, NetRole (0) | 2025.11.14 |
| 콜리전 필터링 Collision Filtering (0) | 2025.11.03 |
| UE5 Delegate와 Event의 차이점 (0) | 2025.10.31 |
