Rigidbody 를 추가하고 물체를 움직이다 보면, 내가 작성한 코드랑 별개로 움직이는 느낌을 받게 됩니다. 이것은 유니티가 렌더링용 Transform과 물리 엔진이 다루는 Transform 을 별도로 관리하기 때문입니다.
이번 글에서는 이 둘의 관계와 내부 동작 원리에 대해 설명하고, 직접 이동, 회전시키고 싶을 때 어떻게 처리해야 하는지를 예제와 함께 다뤄보겠습니다.
우리가 흔히 쓰고 있는 Transform (렌더링용)
transform.Translate(Vector3.forward * Time.deltaTime);
transform.Rotate(Vector3.up * 90f * Time.deltaTime);
이 코드는 일반적으로 잘 동작합니다.
하지만 Rigidbody가 붙어 있으면 다음과 같은 어색한 문제들이 발생합니다
• 오브젝트가 움직였다가 다시 원래 위치로 되돌아감
• 회전한 것처럼 보이지만 곧 원래 방향으로 복귀
왜 이런 일이 일어날까?
이유는 간단합니다.
Rigidbody 가 가지고 있는 transform 은 물리 연산에 의해 위치와 회전값이 변환되고, FixedUpdate() 에서 그 계산 결과를 렌더링용 transform 에 덮어씌우기 때문입니다.
내가 원하는 대로 움직이게 하려면?
물리 연산을 일시적으로 끊고 직접 덮어쓰기
// 물리 회전 무시
rb.freezeRotation = true;
// 내가 직접 코드로 제어
transform.Rotate(Vector3.up * 90f);
// 다시 물리 엔진에 제어권 넘김
rb.freezeRotation = false;
이 방식은 freezeRotation = true 일 때, Unity 내부에 내가 직접 코드로 회전시킨 transform.rotation 값을 Rigidbody.rotation 에도 복사해준다는 점입니다.
내부 동작은 다음과 같이 유추할 수 있습니다.
if (rb.freezeRotation)
{
// Transform 값을 물리 회전에 반영
rb.rotation = transform.rotation;
}
그런데 이 방법은...
• freezeRotation 을 반복적으로 켰다 껐다 하면 부하가 생길 수 있습니다.
• 물리 연산에 개입하여 강제로 회전시켰기 때문에, 자연스러운 동작이 어긋날 수 있습니다.
다른 방법은?
Rigidbody 가 제공하는 함수들을 쓰면 됩니다.
이 방법은 애초에 Rigidbody 가 사용하는 위치, 회전값을 직접 조작하여 물리 엔진이 이 결과값에 이어서 계산을 계속 이어나가기 때문에 렌더링과 물리 엔진의 값이 일치하게 됩니다.
// 위치 이동
rb.MovePosition(rb.position + transform.forward * speed * Time.fixedDeltaTime);
// 회전
Quaternion delta = Quaternion.Euler(0f, 90f * Time.fixedDeltaTime, 0f);
rb.MoveRotation(rb.rotation * delta);
이 방법은 자연스럽고 충돌도 잘 적용되며, Rigidbody 의 물리 연산과도 잘 맞습니다.
다만, 주의해야 할 점은 물리 연산에 의해 계산되기 때문에 물리 엔진의 계산 주기와 일치시켜야 합니다.
따라서,
• Time.DeltaTime 대신에 Time.fixedDeltaTime 을 써야 합니다.
• Update() 함수가 아닌 FixedUpdate() 함수에서 코드를 작성해야 합니다.
private void FixedUpdate()
{
ProcessInput();
}
private void ProcessInput()
{
if (trustAction.IsPressed())
{
rb.AddForce( transform.up * ( trustStrength * Time.deltaTime ) );
}
if (rotateAction.IsPressed())
{
float inputValue = rotateAction.ReadValue<float>();
inputValue *= -1.0f;
Quaternion rot = Quaternion.Euler(0, 0, inputValue * rotateSpeed * Time.fixedDeltaTime);
rb.MoveRotation(rb.rotation * rot);
}
}
그럼 어떤 것을 사용하면 좋을까?
Rigidbody.MoveRotation()
어떤 경우에는 물리 함수를 이용하는 것이 게임적으로는 불편할 수 있습니다.
예를 들어, 두 개의 물체가 있고, 그 중 하나를 MoveRotation 으로 돌리면 바닥이나 옆 물체가 회전 방향을 막아버려서 회전이 안됩니다.
Rigidbody.MoveRotation() 은 충돌 감지와 함께 작동하기 때문에, 회전하려는 방향에 다른 Collider가 있으면, Unity는 그걸 “부딪히는 상황” 으로 인식하고 회전을 제한하거나 물리 반발로 되돌려 버리기 때문입니다.
transform.Rotate()
rb.freezeRotation = true;
transform.Rotate(...);
rb.freezeRotation = false;
반면, freezeRotate = true; 로 놓고 transform.Rotate() 를 사용하면, 물리 연산을 아예 무시하기 때문에 충돌이든 반발력이든 무시하고 그냥 회전시켜 버립니다.
따라서 물리적으로는 말이 안되지만 게임적으로는 오히려 자연스럽게 보일 수 있습니다.
정리
• Rigidbody 가 있으면 transform.position과 transform.rotation 은 물리 공식으로 계산된 transform 으로 덮어씌워집니다. 덮어씌워지는 시점은 fixedupdate() 함수가 호출되는 순간입니다.
• transform 으로 직접 움직이려면 freezeRotation이나 freezePosition 으로 물리 개입을 끊어야 합니다.
• 물리 계산이 필요한 경우는 fixedDeltaTime, fixedUpdate() 와 같은 변수나 함수를 사용해야 합니다.
'Unity' 카테고리의 다른 글
| Unity의 FixedUpdate, Update, LateUpdate 완전 이해 (0) | 2025.10.31 |
|---|---|
| Cinemachine Camera : Dead Zone, Hard Limits, Damping (0) | 2025.07.27 |
| Cinemachine 카메라를 붙였더니 카메라가 말을 안 들어요 (0) | 2025.07.16 |
| 물리 공간과 오브젝트 공간에서의 물체 이동 (0) | 2025.07.14 |
| Input Sytem : InputAction 사용법 정리 (0) | 2025.07.14 |