Search Unity

How to rotate a rigidbody with torque around only the XY axes?

Discussion in 'Physics' started by namcap, Jan 16, 2021.

  1. namcap

    namcap

    Joined:
    Jan 8, 2016
    Posts:
    49
    I'm trying to rotate a rigidbody, based on mouse movement, using torque. If the mouse moves horizontally, the rigidbody should rotate horizontally (around the Y axis). If the mouse moves vertically, the rigidbody should rotate vertically (around the X axis). The Z axis of the rigidbody should never change (always 0).

    I've tried doing this in a few different ways, but I can't figure out the math to get the proper axes to rotate around. The below code works when rotating around the X axis. However, rotating around the Y axis results in the Z rotation changing if the X rotation is not 0. If the X rotation is 0, then the Y rotation works as expected.

    Using the local Y (transform.up) also will give the incorrect results as the object will rotate in all directions if the X rotation is not 0.

    What would be the proper axis to rotate around for the Y rotation?

    Code (CSharp):
    1. public class TestRotater : MonoBehaviour {
    2.  
    3.      private Rigidbody rb;
    4.      private float rotX;
    5.      private float rotY;
    6.      public float speed = 10;
    7.  
    8.      void Awake() {
    9.          rb = GetComponent<Rigidbody>();
    10.      }
    11.  
    12.      void FixedUpdate() {
    13.          rotY = Input.GetAxis("Mouse X") * speed;
    14.          rotX = Input.GetAxis("Mouse Y") * speed;
    15.          rb.AddTorque(transform.right * rotX + Vector3.up * rotY);
    16.      }
    17. }
     
  2. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    Your problem is coming from the rb maintaining angular momentum from previous torque applications. The quick and dirty fix for this is to change your code to this:

    Code (CSharp):
    1. public class TestRotater : MonoBehaviour
    2. {
    3.  
    4.     private Rigidbody rb;
    5.     private float rotX;
    6.     private float rotY;
    7.     public float speed = 1f;
    8.  
    9.     void Awake()
    10.     {
    11.         rb = GetComponent<Rigidbody>();
    12.     }
    13.  
    14.     void FixedUpdate()
    15.     {
    16.         rb.angularVelocity = Vector3.zero; // <-------------------
    17.         rotY = Input.GetAxis("Mouse X") * speed;
    18.         rotX = Input.GetAxis("Mouse Y") * speed;
    19.         rb.AddTorque(transform.right * rotX + Vector3.up * rotY);
    20.     }
    21. }
    Obviously, this means your angular velocity will not be reliably influenced by external physics forces (it will get reset and over-ridden each FixedUpdate). Equally, you should be able to see that if any velocity exists from a previous rotation you are going to have a very tricky time regaining control of the new angular velocity.
     
    namcap likes this.