TIL: Unreal Engine FQuat와 FRotator의 차이점, 장점과 단점 이해하기
오늘 배운 내용
오늘은 Unreal Engine에서 회전을 표현할 때 사용하는 FRotator와 FQuat의 차이점에 대해 정리했다.
처음에는 둘 다 회전을 나타내는 자료형이라서 차이가 크게 와닿지 않았다.
하지만 정리해보면 다음과 같다.
FRotator
→ Pitch, Yaw, Roll 세 개의 각도로 회전을 표현하는 방식
FQuat
→ X, Y, Z, W 네 개의 값으로 회전을 표현하는 쿼터니언 방식
둘 다 회전을 나타내지만 사용하는 목적이 다르다.
FRotator는 사람이 읽고 이해하기 쉬운 회전 표현이고, FQuat는 내부 계산이나 보간에서 더 안정적인 회전 표현이다.
이번 정리의 핵심은 다음과 같다.
FRotator
→ 보기 쉬운 회전값
FQuat
→ 계산하기 좋은 회전값
FRotator의 장점
→ 직관적이고 디버깅하기 쉽다
FRotator의 단점
→ 짐벌락 문제가 생길 수 있다
FQuat의 장점
→ 짐벌락을 피하고 부드러운 회전 계산에 유리하다
FQuat의 단점
→ 사람이 직접 읽거나 수정하기 어렵다
즉 단순한 회전값 설정에는 FRotator가 편하고, 복잡한 회전 계산이나 부드러운 보간에는 FQuat가 더 적합하다.
1. FRotator의 의미
FRotator는 Unreal Engine에서 회전을 Pitch, Yaw, Roll 세 개의 각도로 표현하는 구조체다.
기본 형태는 다음과 같다.
FRotator Rotation(Pitch, Yaw, Roll);
예를 들어 다음 코드는 액터를 Yaw 방향으로 90도 회전시키는 코드다.
FRotator NewRotation(0.0f, 90.0f, 0.0f);
SetActorRotation(NewRotation);
여기서 각 값의 의미는 다음과 같다.
Pitch
→ 위아래로 고개를 드는 회전
Yaw
→ 좌우로 방향을 트는 회전
Roll
→ 몸이나 카메라가 옆으로 기울어지는 회전
즉 FRotator는 사람이 생각하는 회전 방식과 가깝다.
예를 들어 캐릭터가 오른쪽으로 90도 돌아야 한다면 다음처럼 바로 작성할 수 있다.
FRotator LookRightRotation(0.0f, 90.0f, 0.0f);
이 코드를 보면 대략 어떤 회전인지 바로 알 수 있다.
그래서 FRotator는 디버깅이나 간단한 회전 설정에 많이 사용된다.
2. FRotator의 장점
FRotator의 가장 큰 장점은 직관적이라는 점이다.
값이 Pitch, Yaw, Roll로 나뉘어 있어서 사람이 읽기 쉽다.
예를 들어 다음 코드를 보면 어떤 회전인지 바로 이해할 수 있다.
FRotator Rotation(30.0f, 90.0f, 0.0f);
이 코드는 다음과 같이 해석할 수 있다.
Pitch 30도
→ 위쪽으로 30도 회전
Yaw 90도
→ 오른쪽으로 90도 회전
Roll 0도
→ 기울어짐 없음
또 다른 장점은 에디터와 블루프린트에서 다루기 쉽다는 것이다.
Unreal Editor의 Details 패널에서도 Rotation 값은 사람이 보기 쉬운 각도 형태로 표시된다.
그래서 오브젝트의 회전값을 직접 확인하거나 수정하기 쉽다.
정리하면 FRotator의 장점은 다음과 같다.
1. 사람이 읽기 쉽다.
2. 디버깅하기 쉽다.
3. 에디터에서 확인하기 편하다.
4. 단순한 회전 설정에 적합하다.
5. 코드 작성이 간단하다.
예를 들어 액터를 특정 방향으로 바라보게 만드는 정도라면 FRotator만으로도 충분하다.
FRotator TargetRotation(0.0f, 180.0f, 0.0f);
SetActorRotation(TargetRotation);
이런 경우에는 굳이 FQuat를 사용할 필요가 없다.
3. FRotator의 단점
FRotator의 단점은 오일러 각 방식이라는 점이다.
오일러 각은 세 축의 회전을 순서대로 적용해서 최종 회전을 만든다.
이 방식은 직관적이지만, 특정 상황에서 축이 겹치면서 회전 자유도가 줄어드는 문제가 생길 수 있다.
이 문제를 짐벌락(Gimbal Lock)이라고 한다.
예를 들어 다음처럼 매 프레임 FRotator 값을 직접 더해서 회전시키는 코드는 문제가 생길 수 있다.
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FRotator CurrentRotation = GetActorRotation();
CurrentRotation.Pitch += 90.0f * DeltaTime;
CurrentRotation.Yaw += 90.0f * DeltaTime;
CurrentRotation.Roll += 90.0f * DeltaTime;
SetActorRotation(CurrentRotation);
}
이 코드는 겉으로 보기에는 세 축을 계속 회전시키는 코드처럼 보인다.
하지만 회전이 누적되면서 특정 각도에 도달하면 축이 겹치거나, 기대한 방향과 다르게 회전할 수 있다.
특히 카메라 회전, 비행체 회전, 우주선 회전처럼 여러 축을 자유롭게 돌리는 경우에는 FRotator만으로 회전을 누적하면 문제가 생길 수 있다.
FRotator의 단점은 다음과 같다.
1. 짐벌락 문제가 생길 수 있다.
2. 여러 축 회전을 계속 누적하면 예상과 다른 결과가 나올 수 있다.
3. 회전 보간이 부자연스러울 수 있다.
4. 같은 방향을 여러 개의 다른 각도 조합으로 표현할 수 있다.
5. 복잡한 3D 회전 계산에는 불리하다.
예를 들어 다음 두 회전값은 숫자는 다르지만 실제 방향은 같게 볼 수 있다.
FRotator RotA(0.0f, 360.0f, 0.0f);
FRotator RotB(0.0f, 0.0f, 0.0f);
숫자로는 다르지만, 방향으로 보면 둘 다 한 바퀴 돌아 원래 방향을 보는 것과 같다.
이런 점 때문에 회전값 비교나 보간에서 헷갈릴 수 있다.
4. FQuat의 의미
FQuat는 Quaternion, 즉 쿼터니언 방식으로 회전을 표현하는 구조체다.
기본 형태는 다음과 같다.
FQuat RotationQuat(X, Y, Z, W);
FRotator가 세 개의 각도를 사용하는 것과 달리, FQuat는 네 개의 값 X, Y, Z, W를 사용한다.
하지만 FQuat의 X, Y, Z, W는 사람이 직접 보고 이해하기 쉬운 값이 아니다.
예를 들어 다음 값을 봐도 어떤 방향으로 몇 도 회전한 것인지 바로 알기 어렵다.
FQuat Quat(0.0f, 0.0f, 0.7071f, 0.7071f);
이런 값은 수학적으로는 의미가 있지만, FRotator(0, 90, 0)처럼 직관적으로 읽히지는 않는다.
그래서 FQuat는 보통 직접 값을 넣기보다는 FRotator에서 변환하거나, 특정 축과 각도로 생성해서 사용한다.
예를 들어 FRotator를 FQuat로 바꾸는 방법은 다음과 같다.
FRotator Rotator(0.0f, 90.0f, 0.0f);
FQuat QuatA = FQuat(Rotator);
FQuat QuatB = Rotator.Quaternion();
반대로 FQuat를 다시 FRotator로 바꿀 수도 있다.
FQuat Quat = FQuat(FRotator(0.0f, 90.0f, 0.0f));
FRotator Rotator = Quat.Rotator();
즉 FQuat와 FRotator는 서로 변환할 수 있다.
FRotator
→ FQuat로 변환 가능
FQuat
→ FRotator로 변환 가능
하지만 중요한 점은 내부 계산에는 FQuat가 더 안정적인 경우가 많고, 사람이 확인할 때는 FRotator가 더 편하다는 것이다.
5. FQuat의 장점
FQuat의 가장 큰 장점은 복잡한 회전 계산에서 안정적이라는 점이다.
특히 여러 축의 회전을 계속 누적하거나, 두 회전 사이를 부드럽게 보간할 때 유리하다.
FQuat는 오일러 각 방식이 아니기 때문에 FRotator에서 발생할 수 있는 짐벌락 문제를 피하는 데 유리하다.
예를 들어 매 프레임 회전을 누적해야 하는 경우에는 다음처럼 FQuat를 사용할 수 있다.
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
const float DegreesPerSecond = 90.0f;
const float DeltaAngleRad = FMath::DegreesToRadians(DegreesPerSecond * DeltaTime);
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaQuat(FVector::UpVector, DeltaAngleRad);
FQuat NewQuat = DeltaQuat * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
}
이 코드는 현재 액터의 회전을 FQuat로 가져온 뒤, Z축 기준으로 회전을 추가하는 코드다.
여기서 중요한 부분은 다음이다.
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaQuat(FVector::UpVector, DeltaAngleRad);
FQuat NewQuat = DeltaQuat * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
FRotator처럼 Pitch, Yaw, Roll 값을 직접 더하는 것이 아니라, 쿼터니언끼리 곱해서 회전을 합성한다.
이 방식은 연속적인 회전에서 더 안정적인 결과를 낼 수 있다.
또 FQuat는 보간에도 자주 사용된다.
예를 들어 두 회전 사이를 부드럽게 회전시키고 싶다면 FQuat::Slerp를 사용할 수 있다.
FRotator StartRotator(0.0f, 0.0f, 0.0f);
FRotator EndRotator(0.0f, 180.0f, 0.0f);
FQuat StartQuat = StartRotator.Quaternion();
FQuat EndQuat = EndRotator.Quaternion();
float Alpha = 0.5f;
FQuat ResultQuat = FQuat::Slerp(StartQuat, EndQuat, Alpha);
FRotator ResultRotator = ResultQuat.Rotator();
이 코드는 시작 회전과 목표 회전의 중간 회전을 구하는 코드다.
여기서 Alpha가 0이면 시작 회전이고, 1이면 목표 회전이다.
Alpha = 0.0f
→ StartQuat
Alpha = 0.5f
→ 중간 회전
Alpha = 1.0f
→ EndQuat
즉 FQuat는 두 방향 사이를 자연스럽게 회전시키는 데 유리하다.
정리하면 FQuat의 장점은 다음과 같다.
1. 짐벌락 문제를 피하는 데 유리하다.
2. 연속적인 회전 누적에 안정적이다.
3. 회전 보간에 적합하다.
4. 복잡한 3D 회전 계산에 유리하다.
5. 카메라, 비행체, 투사체, 물리 회전 등에 적합하다.
6. FQuat의 단점
FQuat의 단점은 사람이 읽기 어렵다는 점이다.
FRotator는 다음처럼 보면 바로 이해할 수 있다.
FRotator Rotation(0.0f, 90.0f, 0.0f);
이 코드는 Yaw 방향으로 90도 회전이라는 것을 바로 알 수 있다.
하지만 FQuat는 다음처럼 봐도 무슨 회전인지 바로 알기 어렵다.
FQuat RotationQuat(0.0f, 0.0f, 0.7071f, 0.7071f);
이 값은 사람이 직접 수정하기에도 적합하지 않다.
X, Y, Z, W 중 하나를 마음대로 바꾸면 원하는 회전이 나오는 것이 아니라, 잘못된 회전값이 될 수 있다.
또 쿼터니언은 보통 단위 쿼터니언 상태를 유지해야 한다.
여러 계산을 반복하다 보면 부동소수점 오차가 누적될 수 있으므로, 필요하면 정규화를 해줘야 한다.
FQuat NewQuat = DeltaQuat * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
정리하면 FQuat의 단점은 다음과 같다.
1. 사람이 읽기 어렵다.
2. 직접 값을 수정하기 어렵다.
3. 수학적 개념이 필요해서 처음 이해하기 어렵다.
4. 회전 곱셈 순서가 헷갈릴 수 있다.
5. 디버깅할 때는 FRotator보다 불편하다.
그래서 FQuat는 내부 계산용으로 사용하고, 로그나 디버깅에서는 FRotator로 변환해서 보는 경우가 많다.
FQuat Quat = GetActorQuat();
FRotator DebugRotator = Quat.Rotator();
UE_LOG(LogTemp, Warning, TEXT("Rotation: %s"), *DebugRotator.ToString());
이렇게 하면 내부 회전은 FQuat로 관리하면서, 출력은 사람이 읽기 쉬운 FRotator 형태로 확인할 수 있다.
7. FRotator와 FQuat 변환 방법
FRotator와 FQuat는 서로 변환할 수 있다.
먼저 FRotator에서 FQuat로 변환하는 방법은 다음과 같다.
FRotator Rotator(0.0f, 90.0f, 0.0f);
FQuat Quat = Rotator.Quaternion();
또는 생성자를 사용할 수도 있다.
FRotator Rotator(0.0f, 90.0f, 0.0f);
FQuat Quat(Rotator);
반대로 FQuat에서 FRotator로 변환하려면 Rotator()를 사용한다.
FQuat Quat = FQuat(FRotator(0.0f, 90.0f, 0.0f));
FRotator Rotator = Quat.Rotator();
전체 흐름은 다음과 같다.
사람이 이해하기 쉬운 값이 필요함
→ FRotator 사용
안정적인 회전 계산이 필요함
→ FQuat로 변환
계산이 끝난 뒤 로그나 에디터 확인이 필요함
→ 다시 FRotator로 변환
예시 코드는 다음과 같다.
FRotator InputRotator(0.0f, 90.0f, 0.0f);
FQuat Quat = InputRotator.Quaternion();
FQuat AdditionalRotation(FVector::UpVector, FMath::DegreesToRadians(45.0f));
FQuat ResultQuat = AdditionalRotation * Quat;
FRotator ResultRotator = ResultQuat.Rotator();
SetActorRotation(ResultRotator);
이 코드는 처음에는 사람이 이해하기 쉬운 FRotator로 회전값을 만들고, 계산은 FQuat로 처리한 뒤, 다시 FRotator로 바꿔서 적용하는 예시다.
8. FRotator를 사용하기 좋은 경우
FRotator는 간단하고 명확한 회전 설정에 적합하다.
예를 들어 캐릭터를 특정 방향으로 돌리거나, 오브젝트의 초기 회전값을 설정할 때는 FRotator가 편하다.
void AMyActor::BeginPlay()
{
Super::BeginPlay();
FRotator StartRotation(0.0f, 90.0f, 0.0f);
SetActorRotation(StartRotation);
}
이 코드는 게임이 시작될 때 액터를 오른쪽 방향으로 회전시키는 코드다.
또 특정 방향을 바라보게 만들 때도 FRotator를 자주 사용한다.
FVector Direction = TargetLocation - GetActorLocation();
FRotator LookAtRotation = Direction.Rotation();
SetActorRotation(LookAtRotation);
이 코드는 타겟 위치를 향하는 방향 벡터를 구한 뒤, 그 방향을 FRotator로 변환해서 액터가 타겟을 바라보게 만든다.
FRotator를 사용하기 좋은 경우는 다음과 같다.
1. 특정 각도로 회전시키는 경우
2. 에디터에서 입력한 회전값을 코드에서 사용하는 경우
3. 캐릭터의 Yaw 회전처럼 단순한 회전이 필요한 경우
4. 로그로 회전값을 확인해야 하는 경우
5. 블루프린트와 연결해서 직관적으로 다루고 싶은 경우
예를 들어 일반적인 캐릭터 방향 전환은 FRotator로도 충분하다.
FRotator ControlRotation = GetControlRotation();
FRotator YawRotation(0.0f, ControlRotation.Yaw, 0.0f);
SetActorRotation(YawRotation);
이런 상황에서는 FQuat를 사용하면 오히려 코드가 더 복잡해질 수 있다.
9. FQuat를 사용하기 좋은 경우
FQuat는 복잡한 회전 계산이나 부드러운 회전 보간이 필요할 때 적합하다.
예를 들어 비행기, 드론, 우주선처럼 여러 축으로 자유롭게 회전하는 오브젝트는 FQuat가 더 안정적이다.
void AMyActor::AddYawRotation(float DeltaTime)
{
const float RotationSpeed = 90.0f;
const float DeltaAngleRad = FMath::DegreesToRadians(RotationSpeed * DeltaTime);
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaQuat(FVector::UpVector, DeltaAngleRad);
FQuat NewQuat = DeltaQuat * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
}
이 코드는 Z축 기준으로 회전을 누적한다.
또 목표 방향으로 부드럽게 회전시키고 싶을 때도 FQuat를 사용할 수 있다.
void AMyActor::RotateToTarget(float DeltaTime)
{
FQuat CurrentQuat = GetActorQuat();
FQuat TargetQuat = FRotator(0.0f, 180.0f, 0.0f).Quaternion();
const float InterpSpeed = 3.0f;
const float Alpha = FMath::Clamp(DeltaTime * InterpSpeed, 0.0f, 1.0f);
FQuat NewQuat = FQuat::Slerp(CurrentQuat, TargetQuat, Alpha);
NewQuat.Normalize();
SetActorRotation(NewQuat);
}
이 코드는 현재 회전에서 목표 회전까지 부드럽게 보간한다.
FQuat를 사용하기 좋은 경우는 다음과 같다.
1. 여러 축 회전을 동시에 처리해야 하는 경우
2. 회전을 매 프레임 누적해야 하는 경우
3. 짐벌락을 피하고 싶은 경우
4. 두 회전 사이를 부드럽게 보간해야 하는 경우
5. 카메라, 비행체, 투사체, 물리 기반 회전을 다루는 경우
즉 FQuat는 사람이 직접 입력하는 회전값이라기보다는, 회전 계산을 안정적으로 처리하기 위한 내부 표현에 가깝다.
10. 짐벌락이 헷갈렸던 이유
처음에는 짐벌락이 단순히 회전이 안 되는 버그처럼 느껴졌다.
하지만 실제로는 오일러 각 방식에서 축이 겹치면서 회전 자유도가 줄어드는 문제다.
FRotator는 Pitch, Yaw, Roll 세 개의 축 회전을 순서대로 적용한다.
이때 특정 각도에서는 두 축이 같은 방향을 가리키는 것처럼 되어, 한 축 회전이 사라진 것처럼 보일 수 있다.
흐름으로 보면 다음과 같다.
FRotator로 Pitch, Yaw, Roll 회전 적용
↓
특정 각도에서 회전축이 겹침
↓
한 축의 회전 자유도가 줄어듦
↓
원하는 방향으로 회전하지 않음
↓
짐벌락처럼 보임
그래서 단순히 회전값을 한 번 설정하는 경우에는 괜찮지만, 여러 축을 계속 누적해서 돌리는 경우에는 문제가 생길 수 있다.
예를 들어 다음 코드는 단순해서 문제가 없어 보인다.
FRotator Rotation = GetActorRotation();
Rotation.Pitch += 90.0f * DeltaTime;
Rotation.Yaw += 90.0f * DeltaTime;
Rotation.Roll += 90.0f * DeltaTime;
SetActorRotation(Rotation);
하지만 실제로는 세 축의 회전이 계속 누적되면서 예상과 다른 결과가 나올 수 있다.
이럴 때는 FQuat로 회전을 합성하는 방식이 더 적합하다.
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaPitch(FVector::RightVector, FMath::DegreesToRadians(90.0f * DeltaTime));
FQuat DeltaYaw(FVector::UpVector, FMath::DegreesToRadians(90.0f * DeltaTime));
FQuat DeltaRoll(FVector::ForwardVector, FMath::DegreesToRadians(90.0f * DeltaTime));
FQuat NewQuat = DeltaYaw * DeltaPitch * DeltaRoll * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
이 코드에서는 FRotator 값에 직접 각도를 더하지 않고, 쿼터니언을 곱해서 회전을 합성한다.
이 방식이 여러 축 회전 누적에는 더 안정적이다.
11. FQuat 곱셈 순서 주의하기
FQuat를 사용할 때 가장 헷갈리는 부분은 곱셈 순서다.
FQuat는 단순히 숫자를 더하는 방식이 아니라 곱셈으로 회전을 합성한다.
예를 들어 다음 두 코드는 결과가 달라질 수 있다.
FQuat NewQuatA = DeltaQuat * CurrentQuat;
FQuat NewQuatB = CurrentQuat * DeltaQuat;
두 코드 모두 회전을 합성하지만, 어느 좌표계를 기준으로 회전을 적용하느냐가 달라질 수 있다.
일반적으로 다음처럼 이해하면 된다.
DeltaQuat * CurrentQuat
→ 월드 축 기준 회전에 가깝게 사용
CurrentQuat * DeltaQuat
→ 로컬 축 기준 회전에 가깝게 사용
예를 들어 월드의 Z축을 기준으로 계속 회전하고 싶다면 다음처럼 쓸 수 있다.
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaQuat(FVector::UpVector, FMath::DegreesToRadians(90.0f * DeltaTime));
FQuat NewQuat = DeltaQuat * CurrentQuat;
SetActorRotation(NewQuat);
반대로 액터 자신의 로컬 축 기준으로 회전하고 싶다면 곱셈 순서를 바꿔야 할 수 있다.
FQuat CurrentQuat = GetActorQuat();
FQuat DeltaQuat(FVector::UpVector, FMath::DegreesToRadians(90.0f * DeltaTime));
FQuat NewQuat = CurrentQuat * DeltaQuat;
SetActorRotation(NewQuat);
이 부분은 처음에 가장 많이 헷갈릴 수 있다.
그래서 FQuat를 사용할 때는 다음을 항상 확인해야 한다.
1. 월드 축 기준 회전인지
2. 로컬 축 기준 회전인지
3. 곱셈 순서가 의도한 결과와 맞는지
4. 결과가 이상하면 곱셈 순서를 바꿔서 확인해볼 것
즉 FQuat는 안정적이지만, 곱셈 순서를 잘못 쓰면 원하는 회전이 나오지 않을 수 있다.
12. FRotator와 FQuat 비교 정리
FRotator와 FQuat를 비교하면 다음과 같다.
FRotator
→ Pitch, Yaw, Roll 세 개의 각도로 회전 표현
→ 사람이 읽기 쉬움
→ 간단한 회전에 적합
→ 짐벌락 문제가 생길 수 있음
→ 디버깅하기 쉬움
FQuat
→ X, Y, Z, W 네 개의 값으로 회전 표현
→ 사람이 읽기 어려움
→ 복잡한 회전 계산에 적합
→ 짐벌락을 피하는 데 유리함
→ 보간과 회전 누적에 안정적임
표처럼 정리하면 다음과 같다.
구분FRotatorFQuat
| 표현 방식 | Pitch, Yaw, Roll | X, Y, Z, W |
| 기반 개념 | 오일러 각 | 쿼터니언 |
| 사람이 읽기 | 쉬움 | 어려움 |
| 에디터 친화성 | 좋음 | 낮음 |
| 짐벌락 | 발생 가능 | 피하는 데 유리 |
| 회전 누적 | 불안정할 수 있음 | 안정적 |
| 보간 | 단순 보간은 가능하지만 한계 있음 | Slerp에 적합 |
| 사용 난이도 | 쉬움 | 어려움 |
| 주 사용 목적 | 간단한 회전 설정 | 복잡한 회전 계산 |
결국 둘 중 하나만 무조건 좋은 것이 아니라, 상황에 따라 다르게 써야 한다.
보기 쉽고 간단한 회전
→ FRotator
안정적인 회전 계산
→ FQuat
이렇게 구분하면 된다.
13. 실제 사용 기준
실제 프로젝트에서 사용할 때는 다음 기준으로 선택하면 된다.
먼저 FRotator를 사용하기 좋은 경우는 다음과 같다.
캐릭터가 좌우로 도는 정도의 회전
에디터에서 회전값을 직접 넣는 경우
특정 각도로 한 번만 회전시키는 경우
로그로 회전값을 확인해야 하는 경우
블루프린트와 연동해서 보기 쉽게 다루는 경우
예시는 다음과 같다.
FRotator SpawnRotation(0.0f, 90.0f, 0.0f);
GetWorld()->SpawnActor<AMyActor>(
AMyActor::StaticClass(),
SpawnLocation,
SpawnRotation
);
이런 스폰 회전값은 FRotator가 훨씬 직관적이다.
반대로 FQuat를 사용하기 좋은 경우는 다음과 같다.
회전을 매 프레임 누적하는 경우
여러 축으로 동시에 회전하는 경우
비행체나 우주선처럼 자유 회전이 필요한 경우
카메라를 부드럽게 회전시키는 경우
두 회전 사이를 자연스럽게 보간하는 경우
예시는 다음과 같다.
FQuat CurrentQuat = GetActorQuat();
FQuat TargetQuat = TargetRotation.Quaternion();
FQuat NewQuat = FQuat::Slerp(CurrentQuat, TargetQuat, 0.1f);
NewQuat.Normalize();
SetActorRotation(NewQuat);
이런 회전 보간은 FQuat가 더 적합하다.
정리하면 다음과 같다.
입력, 확인, 단순 설정
→ FRotator
계산, 누적, 보간
→ FQuat
이 기준으로 생각하면 둘을 언제 써야 하는지 훨씬 명확해진다.
14. FRotator와 FQuat를 같이 쓰는 방식
실제로는 FRotator와 FQuat 중 하나만 고집하는 것이 아니라, 둘을 같이 쓰는 경우가 많다.
사람이 입력하거나 확인할 때는 FRotator를 사용하고, 내부 계산은 FQuat로 처리하는 방식이다.
흐름은 다음과 같다.
사람이 이해하기 쉬운 회전값 입력
↓
FRotator 사용
↓
안정적인 계산을 위해 FQuat로 변환
↓
회전 합성 또는 보간
↓
필요하면 다시 FRotator로 변환
↓
로그 출력 또는 SetActorRotation 적용
예시 코드는 다음과 같다.
FRotator InputRotation(0.0f, 90.0f, 0.0f);
FQuat CurrentQuat = GetActorQuat();
FQuat InputQuat = InputRotation.Quaternion();
FQuat NewQuat = InputQuat * CurrentQuat;
NewQuat.Normalize();
SetActorRotation(NewQuat);
디버깅할 때는 다시 FRotator로 바꿔서 출력할 수 있다.
FRotator DebugRotation = NewQuat.Rotator();
UE_LOG(LogTemp, Warning, TEXT("Debug Rotation: %s"), *DebugRotation.ToString());
이렇게 하면 계산은 안정적으로 처리하면서, 확인은 사람이 이해하기 쉬운 방식으로 할 수 있다.
즉 실제 사용 방식은 다음과 같다.
FRotator
→ 사람이 보기 위한 회전 표현
FQuat
→ 엔진이 계산하기 좋은 회전 표현
이렇게 이해하면 된다.
15. 오늘 헷갈렸던 점
오늘 가장 헷갈렸던 부분은 FRotator와 FQuat가 둘 다 회전인데 왜 굳이 두 개가 필요한지였다.
처음에는 FRotator 하나만 있으면 충분해 보였다.
왜냐하면 Pitch, Yaw, Roll로 회전값을 직접 넣을 수 있고, 에디터에서도 보기 쉽기 때문이다.
하지만 계속 회전하거나 여러 축을 동시에 회전시키는 경우에는 FRotator 방식이 한계를 가진다는 것을 알게 되었다.
특히 짐벌락 문제 때문에, 복잡한 회전에서는 FQuat가 필요하다.
두 번째로 헷갈렸던 부분은 FQuat의 값이었다.
FRotator(0, 90, 0)은 바로 이해되지만, FQuat(0, 0, 0.7071, 0.7071) 같은 값은 직접 봐도 의미를 알기 어렵다.
그래서 FQuat는 직접 값을 수정하는 용도라기보다는, 회전 계산을 안정적으로 처리하기 위한 자료형으로 이해하는 것이 맞다.
세 번째로 헷갈렸던 부분은 FQuat 곱셈 순서였다.
처음에는 그냥 곱하면 되는 줄 알았지만, DeltaQuat * CurrentQuat와 CurrentQuat * DeltaQuat는 결과가 달라질 수 있다.
따라서 FQuat를 사용할 때는 월드 축 기준인지, 로컬 축 기준인지 확인해야 한다.
마지막으로 헷갈렸던 부분은 둘 중 어떤 것을 써야 하는지였다.
정리해보면 기준은 단순하다.
사람이 보고 입력하는 회전
→ FRotator
내부적으로 계산하는 회전
→ FQuat
이렇게 구분하면 된다.
오늘의 핵심 정리
- FRotator는 Pitch, Yaw, Roll로 회전을 표현해서 사람이 이해하기 쉽다.
- FQuat는 쿼터니언 방식이라 사람이 읽기는 어렵지만 회전 계산에 안정적이다.
- 단순한 회전 설정이나 디버깅에는 FRotator가 편하다.
- 여러 축 회전, 회전 누적, 부드러운 보간에는 FQuat가 더 적합하다.
- FRotator는 짐벌락 문제가 생길 수 있고, FQuat는 이를 피하는 데 유리하다.
결국 FRotator는 보기 쉬운 회전 표현이고, FQuat는 계산하기 좋은 회전 표현이다.
단순 설정과 디버깅
→ FRotator
복잡한 계산과 보간
→ FQuat
오늘 배운 내용은 둘 중 하나만 무조건 사용하는 것이 아니라, 상황에 따라 나눠서 사용해야 한다는 것이다.