Rotating tank barrel on X axis while the parent Turret rotates on Y axis

I’ve been trying to get this to work for two days, most posts say to put x and z to zero but that causes the barrel to keep looking at the same spot regardless of the parent object rotating on the Y. I’m exhausted and frustrated that I haven’t been able to solve it. All the commented code is everything I’ve already tried. Let me know if you need any additional information. The turret rotates correctly but the barrel keeps rotating on its own y and z when it should only move up and down (on the x axis) to look at the target.

public Transform target;
public Transform barrel;
public float turretSpeed;
public Vector3 positionOffset;
// Start is called before the first frame update
void Start()
{
    
}

// Update is called once per frame
void Update()
{
    //Move Turret
    Vector3 turretVectorToTarget = target.position - (transform.position - positionOffset);
    turretVectorToTarget.y = 0;
    Quaternion turretVectorToQuaternion = Quaternion.LookRotation(turretVectorToTarget);
    float step = turretSpeed * Time.deltaTime;
    transform.rotation =  Quaternion.RotateTowards(transform.rotation, turretVectorToQuaternion, step);

    //Debug.Log("TurretVector(" + turretVectorToTarget.x + ", " + turretVectorToTarget.y + ", " + turretVectorToTarget.z + ")");
    Debug.DrawRay(transform.position, turretVectorToTarget, Color.red);

    //Move Barrel
    //if (transform.rotation == turretVectorToQuaternion)
    //{
        //Vector3 test = target.transform.position - new Vector3(transform.position.x, barrel.transform.position.y, transform.position.z);

        Vector3 barrelVectorToTarget = target.position - (barrel.transform.position - positionOffset);
        //Vector3 barrelVectorToTargetLevel = target.position - (barrel.transform.position - positionOffset);
        //barrelVectorToTargetLevel.y = 0;
       //float AngleIWantToRotateBy = Vector3.Angle(barrelVectorToTargetLevel, barrelVectorToTarget);

        //Debug.Log("Angle Size: " + AngleIWantToRotateBy);
        //Angle between the two
        //float sign = Mathf.Sign(barrelVectorToTarget.y - barrelVectorToTargetLevel.y);
        //float angleBetween = Vector3.Angle(barrelVectorToTarget, barrel.transform.forward) * sign;

        //barrel.transform.rotation = Quaternion.Euler(angleBetween, 0, 0);

        //Setting these to zero will result in the barrel pointing directly up or down
        //barrelVectorToTarget.x = 0;
        //barrelVectorToTarget.z = 0;

        //Get the rotation that matches the vector
        Quaternion barrelVectorToQuaternion = Quaternion.LookRotation(barrelVectorToTarget);

    barrel.transform.rotation = Quaternion.Euler(barrelVectorToTarget);
    //barrel.localEulerAngles = new Vector3(barrel.localEulerAngles.x, 0, 0);

    //barrel.transform.rotation = Quaternion.Euler(angleBetween, 0, 0);//
    //barrel.transform.rotation = Quaternion.RotateTowards(barrel.transform.rotation, barrelVectorToQuaternion, step);
    Debug.Log("BarrelVector(" + barrelVectorToTarget.x + ", " + barrelVectorToTarget.y + ", " + barrelVectorToTarget.z + ")");
    Debug.Log("BarrelVectorQuaternion(" + barrelVectorToQuaternion.x + ", " + barrelVectorToQuaternion.y + ", " + barrelVectorToQuaternion.z + ", " + barrelVectorToQuaternion.w + ")");

    Debug.DrawRay(barrel.transform.position, barrelVectorToTarget, Color.blue);
        //Debug.DrawRay(barrel.transform.position, barrelVectorToTargetLevel, Color.cyan);
    //}

So if I get this right, you have a turret that rotates on the y-axis, so just turns around on the playing field and a barrel that is attached to this that should only change an angle for up and down. Quaternions are tricky. Can you do it with transform.LookAt()? That way you can easily check if your target positioning is alright.


If you want to use transform.LookAt() for just one plane, you need to have the other axis at a constant value. Your turret will only rotate in the XZ-plane and will not look at the Y, that’s what the barrel is for. So this example can be used for your turret:

//This will be transforms of course, but just for the example:
Vector3 targetPosition = new Vector3(2, 2, 2);
Vector3 turretPosition = new Vector3(0, 0, 0);

/*This means the target is floating in the air, 2x2 units away.
You want the turret to ignore the fact that it is floating and only look at the 2x2 units away. As if the target was at 2,0,2.  Notice that the 0 is the same as the turretPosition. So the target will be at: */

Vector3 turretTargetPosition = new Vector3(targetPosition.x, turretPosition.y, targetPosition.z);

/*Now because the turret is already looking in the right direction, you can just use a simple lookAt for the barrel. The barrel and turrets forward direction will always be the same. The difference is that the barrel can move up while the turret won't*/

turret.transform.LookAt(turretTargetPosition);
barrel.transform.LookAt(targetPosition);

/* Because its a barrel you want it to rotate at the end, where it is attached to the turret. So you need to add another empty gameobject in your turret that acts as the pivot point. It will be positioned at local 0,0,0. This gameobject will point its forward direction towards the target, so your barrel will be a child of this object and needs to be attached in the forward direction also. The above two lines of code becomes:*/

turret.transform.LookAt(turretTargetPosition);
barrelPivot.transform.LookAt(targetPosition);

Because this is a simpler way of doing it, it’s easier to understand if something goes wrong. When you got the lookAt down, you can start changing the lookAt function into some nice smooth lerping quaternion stuff. Also I see you tried to set the eulerAngles, this is not forbidden but is not recommended.


If you don’t want to use the pure code quaternions, you could use the lookAt transforms as your target rotation. You just hide them, or use empty gameObjects. Those will look at the objects directly while you lerp your real barrel and turret towards those rotations. Just an idea. Sometimes using an extra object in your game will save you a lot of lines of code.

Thank you for your response. One of the variations I tried in order to rotate the barrel correctly was `
//Move Barrel
/*
* Only rotate the barrel up and down if the turret is looking at the player,
* even though the barrel can still rotate on all axis the y and z will already
* be aligned as a result of the turrets rotatation
*/
if (transform.rotation == turretVectorToQuaternion)
{

        //Get the direction from the barrel to the target
        Vector3 targetDir = target.transform.position - (barrel.transform.position - positionOffset);
        //targetDir.y = barrel.transform.rotation.y;

        //Convert the vector to a quaternion rotation
        Quaternion rotation = Quaternion.LookRotation(targetDir);

        //Rotate the barrel to look at the target
        barrel.transform.rotation = Quaternion.Lerp(barrel.transform.rotation, rotation, step);

        //Show the vector
        Debug.DrawRay(barrel.transform.position, targetDir, Color.blue);
    }`

and it seems to give the same result as what you provided, but the barrel is still able to rotate around the other axes if the target is directly above. So I guess the solution now would be to clamp the barrel so it can’t look directly up or down? However, I’m not sure how to clamp it correctly. Sorry for not providing more information about my problem in the original post.