Search code examples
c#unity-game-enginerotationquaternions

How to match another object's rotation delta?


I'm trying to have one object match another object's rotation delta. So if object A rotates 90 degrees around Y axis I want object B to also rotate 90 degrees around Y axis. Since I want to do it every frame, I save the initial rotations of both objects. I tried doing it like this, based on this forum post:

objectB.transform.localRotation = (initialObjectARotation * Quaternion.Inverse(objectA.transform.localRotation)) * initialObjectBRotation;

The issue is that now the rotation is inversed (I'm not sure this is the correct term, but if I rotate objectA right then objectB rotates left etc.). How can I fix this?


Solution

  • If you have the absolute rotation of B (the subject doing the matching) and A (the target being matched) from the previous frame and the new absolute rotation of A, you can solve for the new absolute rotation of B interpreting the A difference along the local axes of each object:

    Quaternion oldB; // previous global rotation of subject
    Quaternion oldA; // previous global rotation of target
    Quaternion newA = transformA.rotation; // current global rotation of target
    
    // solve for new global rotation of subject
    Quaternion newB = oldB * Quaternion.Inverse(oldA) * newA;
    

    If that gives you results you don't like you could try interpreting the diff along global axes:

    // solve for new global rotation of subject
    Quaternion newB = newA * Quaternion.Inverse(oldA) * oldB;
    

    Finally, if you really want it relative to the axes of each object's parent, it's a bit more complicated because it involves the previous parent rotations (absolute), the previous local rotations of the objects, and the current absolute rotation of A to give you the current absolute of B. :

    Quaternion oldBParent; // previous global rotation of subject's parent
    Quaternion oldBLocal; // previous local rotation of subject
    Quaternion oldAParent; // previous global rotation of target's parent
    Quaternion oldALocal; // previous local rotation of target
    
    // solve for new global rotation of subject
    Quaternion newB = oldBParent * Quaternion.Inverse(oldAParent) * newA 
            * Quaternion.Inverse(oldALocal) * oldBLocal;
    

    Note that if there is no parent, then the parent rotations are Quaternion.identity and it is equivalent to the global axes form. Neat!


    And of course once you have newB, you only need to apply the new rotation with transformB.rotation = newB;.