아니 이것은 엔진 버그 또는 특정 회전 표현 의 인공물이 아닙니다 (이것도 발생할 수 있지만이 효과는 회전, 쿼터니언 포함)을 나타내는 모든 시스템에 적용됩니다.
3 차원 공간에서 회전이 작동하는 방식에 대한 실제 사실을 발견했으며 번역과 같은 다른 변형에 대한 직관에서 출발했습니다.
둘 이상의 축에서 회전을 구성 할 때 결과는 각 축에 적용한 총 / 순 값이 아닙니다 (번역을 기대할 수 있음). 우리가 회전을 적용하는 순서는 각 회전이 다음 회전이 적용되는 축 (객체의 로컬 축을 중심으로 회전하는 경우) 또는 개체와 축 사이의 관계 (세계의 회전하는 경우)를 이동함에 따라 결과를 변경합니다 축).
시간이 지남에 따라 축 관계가 변경되면 각 축이 "추정 된"작업에 대한 직감을 혼동 할 수 있습니다. 특히, 요와 피치 회전의 특정 조합은 롤 회전과 동일한 결과를 제공합니다!
우리가 요청한 축에 대해 각 단계가 올바르게 회전하는지 확인할 수 있습니다. 입력을 방해하거나 두 번째 추측하는 표기법에 엔진 글리치 나 아티팩트가 없습니다. 주위에 ". 그것들은 작은 회전을 위해 국부적으로 직교 할 수 있지만, 그것들이 쌓일 때 그것들은 세계적으로 직교하지 않다는 것을 알 수 있습니다.
이것은 위와 같은 90도 회전에 대해 가장 극적이고 명확하지만, 방황 축도 문제에서 입증 된 것처럼 많은 작은 회전으로 기어 들어갑니다.
그래서 우리는 어떻게해야합니까?
피치-요 회전 시스템이 이미있는 경우 원하지 않는 롤을 제거하는 가장 빠른 방법 중 하나는 회전 중 하나를 변경하여 객체의 로컬 축 대신 전체 또는 부모 변형 축에서 작동하는 것입니다. 이렇게하면 두 축 사이의 교차 오염을 막을 수 있습니다. 하나의 축은 절대적으로 제어됩니다.
위의 예제에서 롤이 된 것과 동일한 피치-요 피치 (pitch-yaw-pitch) 시퀀스가 있습니다. 그러나 이제 우리는 요오드를 오브젝트의 Y 축 대신 글로벌 Y 축 주위에 적용합니다.
따라서 1 인칭 카메라를 "Pitch Locally, Yaw Globally"라는 문구로 고정시킬 수 있습니다.
void Update() {
float speed = lookSpeed * Time.deltaTime;
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f, Space.Self);
}
곱셈을 사용하여 회전을 합성하는 경우 곱셈 중 하나의 왼쪽 / 오른쪽 순서를 뒤집어 동일한 효과를 얻을 수 있습니다.
// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation = yaw * transform.rotation; // yaw on the left.
// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.
(구체적인 순서는 환경의 곱셈 규칙에 따라 다르지만 왼쪽 = 더 전역 적 / 오른쪽 = 더 많은 로컬이 일반적인 선택입니다)
이것은 플로트 변수로 원하는 순 총 요와 총 피치를 저장 한 다음 항상 순 결과를 한 번에 적용하여이 각도에서만 단일 방향 오리엔테이션 쿼터니언 또는 행렬을 구성합니다 ( totalPitch
클램프 상태를 유지 한 경우 ).
// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;
또는 동등하게 ...
// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
Mathf.sin(totalPitch),
Mathf.cos(totalPitch) * Mathf.cos(totalYaw));
// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;
이 전역 / 로컬 분할을 사용하면 회전이 독립적 인 축 세트에 적용되므로 서로 합성하고 영향을 줄 수 없습니다.
우리가 회전하려는 세계의 물체라면 같은 아이디어가 도움이 될 수 있습니다. 지구와 같은 예를 들어, 우리는 종종 그것을 뒤집어 요를 로컬로 적용하고 (따라서 항상 극을 돌고 있습니다) 전 세계적으로 피치를가집니다 (그래서 호주를 향하거나 향하지 않고 우리의 시야를 향하여 팁에서 멀어집니다) , 그것이 가리키는 곳마다 ...)
한계
이 글로벌 / 로컬 하이브리드 전략이 항상 올바른 해결책은 아닙니다. 예를 들어, 3D 비행 / 수영이있는 게임에서 똑바로 위 / 똑바로 가리키면서도 모든 권한을 갖기를 원할 수 있습니다. 그러나이 설정을 사용하면 짐벌 잠금 장치를 사용할 수 있습니다. 요축 (글로벌 업)은 롤 축 (로컬 포워드)과 평행이되고 비틀 지 않고 왼쪽이나 오른쪽으로 볼 수있는 방법이 없습니다.
이와 같은 경우에 대신 할 수있는 것은 위의 질문에서 시작한 것처럼 순수한 로컬 회전을 사용하는 것입니다 (따라서 컨트롤은 원하는 위치에 상관없이 동일하게 느껴집니다). 우리는 그것을 정정합니다.
예를 들어, 로컬 회전을 사용하여 "정방향"벡터를 업데이트 한 다음 해당 정방향 벡터를 참조 "위"벡터와 함께 사용하여 최종 방향을 구성 할 수 있습니다. (예를 들어 Unity의 Quaternion.LookRotation 메서드를 사용하거나 이러한 벡터에서 직교 정규 행렬을 수동으로 구성) 업 벡터를 제어하여 롤 또는 비틀기를 제어합니다.
비행 / 수영 예제의 경우 이러한 수정 사항을 시간이 지남에 따라 점진적으로 적용하려고합니다. 너무 갑작스러운 경우 시야가 산만해질 수 있습니다. 대신 플레이어의 현재 업 벡터를 사용하여 뷰가 수평을 벗어날 때까지 프레임 단위로 세로 방향으로 힌트를 줄 수 있습니다. 턴 중에 이것을 적용하는 것은 때때로 플레이어의 컨트롤이 유휴 상태 일 때 카메라를 비틀는 것보다 메스꺼움을 덜 줄입니다.