Odd LookRotation behaviour when "upwards" parameter isn't up

I’m revisiting my turret code as I’ve not got a “satisfactory” solution so far. The “usual” setup, immobile base, turret that rotates on one axis (left/right), gun which rotates in another axis (up/down). The fly in the ointment being the need to make this work regardless of the base’s orientation (i.e. it’ll work on the underside/top/side of a blimp/plane/tank/whatever).

Ideally the gun and turret wouldn’t know about each other, and be governed by their own scripts independently (previous solutions all had to know about all the parts of the turret which I’m not a fan of).

            // "target" is a vector3 position
            // "axis" is one of "transform.up" "transform.right"
            // caluclates a point on the local plane
            distanceToPlane = Vector3.Dot(axis, target - transform.position);
            planePoint = target - (axis * distanceToPlane);

            Quaternion rot = Quaternion.LookRotation(planePoint - transform.position, axis); // issue appears to be here
            transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, turretDegreesPerSecond * Time.deltaTime);

when this is applied to the turret (or gun, looks silly but works) and axis set to “transform.up” it works perfectly; and rotating the base of the turret also doesn’t upset the “left/right” rotation.

when this is applied to the gun (or turret, looks even more silly) and the axis set to “transform.right” things go awry.


(magenta line is planePoint-transform position, blue line is transform.forward, magenta, red and blue are in the same plane, all 90degs from green “up” (transform.up), small cyan arrows indicate resulting movement)


(yellow line is planePoint-transform position, blue line is transform.forward, green, yellow and blue are in the same plane, all 90degs from red “up” (transform.right), small cyan arrows indicate resulting movement)


(quick gif to show the planepoints caluclating correctly, last line in the code above is commented out to stop anything being actioned)


(orthographic view from front, red line is “transform.right” and should be the axis the gun is rotating around, target is in the same plane as the gun’s transform.forward and only moving up/down, planepoint vaguely visible inside the target)

as you can see from the gif when “upwards” in the LookRotation line is set to “transform.right” the resulting rotation isn’t only around “transform.right” but all three axis allowing the gun move incorrectly.

I’m having trouble seeing how/why this is the case. It should be the same calculation on a different set of axis (imagine knocking the tank in the first picture on it’s right side, you should get the exact same line layout as the second picture…) :face_with_spiral_eyes:

If it was some sort of inaccuracy creeping in wouldn’t it also break when the axis is set to transform.up? :eyes:

Is your “axis” value a Vector3? I think you want it to be a “live” value dug out of the transform for your tank.

I usually use transform.up as this “up” when I care about local space. No matter how your transform turns, that transform.up value will be its “up.”

yup, in the working case it’s “transform.up” (i.e. rotate around the local “up” to turn left/right) in the case where it’s going wrong it’s “transform.right” (i.e. rotate around the local “sideways” to pitch up/down)

had a look around to see if I could find out what the LookRotation function is doing, found

yey matrix and quaternion math :frowning: :frowning: but it does have an interesting caveat.

Forward and Upwards parameters must be orthogonal (at 90degs to each other)… (api mentions it but the wording didn’t suggest it would cause weird behaviour is thats not true).

got me to thinking, perhaps when “upwards” is the transform.right axis the “forwards” parameter being passed isn’t in the right orientation i.e. axis is “right is up” but forward vector is still in “up is up”, or visually in the second picture the yellow line is being processed as if it were between the red and green lines and on that plane.

forgot to update this after I got it working! doh!

after a fair amount of checking around I found that the only way to correct the output of the “lookrotation” function appeared to be passed Vector3.up into the normal, even if the axis being rotated around was transform.right, then I walked into issues using RotateTowards when the target was more than 90 degrees away (i.e. the cannon needs to go over the top and transform.up is pointing down). It would start to rotate along all three axis anyway as it attempted to get to the target.

Eventually I stumbled across an answers comment which basically said “if you want to rotate only in specific planes, stay away from the quaternion functions”, so i did that instead. Eventually boiled it down to

    void RotateAroundAxis(Transform obj, Vector3 axis, Vector3 target, float degreesPerSecond)
    {
        _localAxis = obj.InverseTransformDirection(axis);

        // caluclates a point on the local plane
        _distanceToPlane = Vector3.Dot(axis, target - obj.position);
        _planePoint = target - axis * _distanceToPlane;

        Vector3 direction = _planePoint - obj.position;

        float angle = Vector3.Angle(obj.forward, direction);
        float angleDirection = Mathf.Sign(Vector3.Dot(axis, Vector3.Cross(obj.forward, direction)));
        float angleToRotate = Mathf.Min(angle, degreesPerSecond) * angleDirection * Time.deltaTime;

        obj.Rotate(_localAxis * angleToRotate);
    }
1 Like