How to apply rotation of pivot to pivoted object?

Consider the following situation:

There are two GameObjects :

  • _pivot
  • _objectToRotate

_pivot is constantly performing some arbitrary rotation around itself.

How would I get _objectToRotate to perform exactly “the same” rotation that _pivot had performed but around _pivot as opposed to around itself?

Essentially, _pivot would act as a pivot for _objectToRotate. But also their orientations should align. Say, if _pivot.transform.forward == _objectToRotate.transform.forward before the rotation, then _pivot.transform.forward == _objectToRotate.transform.forward after the rotation.

The following code solves this problem if _pivot’s rotation is known:

// apply some rotation to _pivot
_pivot.transform.RotateAround(_pivot.transform.position, new Vector3(1, 1, 1), 90 * Time.deltaTime);
// apply the desired "pivoted" rotation to _objectToRotate
_objectToRotate.RotateAround(_pivot.transform.position, new Vector3(1, 1, 1), 90 * Time.deltaTime);

But how do I do this dynamically if I don’t know _pivot’s rotation?

Storing _pivot’s forward vector inside pivotForwardLastUpdate, I came very close to solving this, using the following snippet:

// determine _pivot's rotation around the axes since last update
float xRotationSinceLastUpdate = Vector3.SignedAngle(_pivot.transform.forward, pivotForwardLastUpdate, Vector3.right);
float yRotationSinceLastUpdate = Vector3.SignedAngle(_pivot.transform.forward, pivotForwardLastUpdate, Vector3.up);
float zRotationSinceLastUpdate = Vector3.SignedAngle(_pivot.transform.forward, pivotForwardLastUpdate, Vector3.forward);
// apply _pivot's rotation since last update to _objectToRotate (around the axes)
_objectToRotate.RotateAround(_pivot.transform.position, Vector3.right, -xRotationSinceLastUpdate);
_objectToRotate.RotateAround(_pivot.transform.position, Vector3.up, -yRotationSinceLastUpdate);
_objectToRotate.RotateAround(_pivot.transform.position, Vector3.forward, -zRotationSinceLastUpdate);

This makes _objectToRotate move along the correct trajectory with the correct orientation. But it moves along the trajectory faster than _pivot for some reason.

I’ve been struggling with this all day - help is much appreciated!

That being said, it’s also possible with a little bit of quaternion math :slightly_smiling_face:
I think that should do what you want :

void Update()
{
    Quaternion rotation = pivot.rotation * Quaternion.Inverse(oldOrientation);

    float angle = Mathf.Acos(rotation.w) * 2;
    Vector3 axis = new Vector3(rotation.x, rotation.y, rotation.z).normalized;
    transform.RotateAround(boundObject.position, axis, angle * Mathf.Rad2Deg);

    oldOrientation = pivot.rotation;
}

Or another way that relies on the initial orientation instead of the one from the previous frame (to avoid any floating point errors or things like that) :

public class TestRotation : MonoBehaviour
{
    public Transform pivot;
    private Quaternion frameOrientation;
    private Quaternion identityPoint;

    private void Start()
    {
        frameOrientation = Quaternion.Inverse(pivot.rotation) * transform.rotation;
        Vector3 position = transform.position - pivot.position;
        identityPoint = Quaternion.Inverse(pivot.rotation) * new Quaternion(position.x, position.y, position.z, 0) * pivot.rotation;
    }

    void Update()
    {
        transform.rotation = pivot.rotation * frameOrientation;
        Quaternion position = pivot.rotation * identityPoint * Quaternion.Inverse(pivot.rotation);
        transform.position = pivot.position + new Vector3(position.x, position.y, position.z);
    }
}

If the usecase allows for it, a workaround for this problem is to temporarily set _pivot as the parent of _objectToRotate by calling _pivot.transform.SetParent(_objectToRotate.transform, true).
Make sure to set the worldPositionStays flag of Transform.SetParent() if you want to prevent position changes before/after setting the parent.

I think the best way is to have _objectToRotate be a child of _pivot, this way when you rotate _pivot, Unity handles the math for rotating and moving _objectToRotate.

If it’s not possible, then add an empty object as a child of _pivot and set the rotation and position of _objectToRotate to the same as that empty object in Update() :slightly_smiling_face: