본문 바로가기
Unity

Rigidbody 를 사용하면서, transform 코드가 제대로 동작을 안할 때

by Oz Driver 2025. 7. 16.

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; 
}

 

그래서 이렇게 하면 다음 FixedUpdate() 에서도 내가 코드로 작성한 값이 유지되고, 물리 엔진은 그 회전값을 기준으로 연산을 이어갑니다.
 

그런데 이 방법은...

•   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() 와 같은 변수나 함수를 사용해야 합니다.