How do I aim a turret at a point?

I posted this in the Help Room a couple days ago but it looks like nobody answers questions in the help room so I decided to post it here.

I have a tank model that can move and rotate freely on every axis. On top of it, there is a turret, which should only be able to rotate along the local Y axis, and a gun barrel connected to the turret which should only be able to pivot up and down. I want to be able to aim the gun at any point without violating these constraints, but nothing I’ve tried so far has worked. I guess I just don’t understand quaternions :slight_smile:

Through trial and error, I got something that almost works. It works perfectly for any target that is approximately in front of the tank, but when the turret has to rotate a significant amount around the y axis, the gun no longer is at the right angle. I suspect this has something to do with gimbal lock but I’m not sure how to fix it.

Here’s my code:

        void FixedUpdate() {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                Debug.DrawLine(hit.point, Camera.main.transform.position, Color.red);
                Quaternion dir = Quaternion.LookRotation(gun.transform.position - hit.point, Vector3.up) * Quaternion.Euler(90, 0, 0);
                Vector3 localDir = (Quaternion.Inverse(transform.rotation) * dir).eulerAngles;
                turret.transform.localRotation = Quaternion.AngleAxis(-localDir.z, new Vector3(0, 0, 1));
                gun.transform.localRotation = Quaternion.AngleAxis(-localDir.x, Vector3.right);
                Debug.DrawLine(hit.point, gun.transform.position, Color.green);
            }
        }

So can anyone give me any pointers on how to do this? I’m pretty lost.

I experienced something similar, and ended up using float angles, relative to Vector3.forward. Then, just rotate around an horizontal angle(I call it like that since it’s the Oxz plane). once done, rotate the barrel.

Hope you find your way threw this thing, for having tried it, it’s hard :slight_smile:

Thanks, but I’m not sure I follow. If it’s not too much trouble, could you provide some example code?

I’m just working on something similar for a third controller camera :

void MoveCamera()
    {
        HorizontalAngle += rotationSpeed * Input.GetAxis("Mouse X") * Time.fixedDeltaTime;
        VerticalAngle -= rotationSpeed * Input.GetAxis("Mouse Y") * Time.fixedDeltaTime;
        HorizontalAngle = FitAngle(HorizontalAngle);

        if (VerticalAngle > maxAngle)
            VerticalAngle = maxAngle;
        if (VerticalAngle < minAngle)
            VerticalAngle = minAngle;
        Vector3 p = Quaternion.Euler(0, HorizontalAngle, 0) * (-1 * cameraDistance * Vector3.forward);
        Vector3 rot = Quaternion.Euler(0, 90, 0) * p;
        mainCam.position = position - Quaternion.AngleAxis(VerticalAngle, rot) * p;
        mainCam.LookAt(position);
    }

there, I just output the target point, but instead you can just turret.transform.rotation.y = HorizontalAngle and barrel.transform.rotation.x = VerticalAngle but that may generate error.

I did just figure something, use the lookAt !
There, the turret should look at turret.transform.position + p,
and the barrel at position - Quaternion.AngleAxis(VerticalAngle, rot) * p;

I’m not sure for the + or - there, just try a bit around and figure it out, for me, it put the camera on the right spot.

Hope this will help :slight_smile:

I couldn’t get that to work, unfortunately. Thanks though.

I finally found this code which worked after a little adaptation.

Guys, check out the “SimpleMouseRotater” script in the standard assets.
Using it, as well as proper GameObject parenting, you can get it to do what you want. :slight_smile:
At least, for player driven tanks, but I am sure you could dig into the code of the script, and figure out what is going on:

https://vimeo.com/173278168

EDIT:
Here is an example of a player tank using SimpleMouseRotator:

https://vimeo.com/173280451

This is the code you need.

var localAimDirection = transform.InverseTransformDirection(target.position - turret.position);\

var flatLocalAimDirection = new Vector3(localAimDirection.x, 0, localAimDirection.z);
turret.transform.localRotation = Quaternion.LookRotation(flatLocalAimDirection);

var turretLocalAimDirection = turret.transform.InverseTransformDirection(target.position - turretHead.position);
turretHead.localRotation = Quaternion.LookRotation(turretLocalAimDirection);

flatLocalAim is the base rotation (horizontal)

turretLocalAim is head rotation (vertical)

1 Like