While this did make the turret rotates as expected, the gun can now no longer rotate up or down, which is useless to me. How can I go about fixing this?

Secondly, I also need to prevent the gun from rotating too far downwards, to stop it from trying to aim through the bottom of the turret base. I only need to worry about the maximum downwards angle, as when the gun points directly up, the turret base rotates around to face the target, meaning the gun cannot rotate more than 90 degrees up. What’s the best way to go about sorting this out?

Here’s an excerpt from the Gun script I created for a tank game we worked on last year. In brief: it checks whether the aim point is left or right of a plane orthogonal to the local right of the rotating part of the turret (the turret base) and then checks whether the aim point is above or below a plane orthogonal to the local up of the elevating part of the turret (the gun). It could probably use some optimization, but you’re welcome to it…

/// <summary>
/// The transform to rotate around the X axis (elevate) when aiming.
/// </summary>
public Transform elevator;
/// <summary>
/// The maximum elevation, in degress, below the zero point.
/// </summary>
public float minElevation;
/// <summary>
/// The maximum elevation, in degress, above the zero point.
/// </summary>
public float maxElevation;
/// <summary>
/// The transform to rotate around the Y axis (rotate) when aiming.
/// </summary>
public Transform rotator;
/// <summary>
/// The maximum rotation, in degress, to the left of the zero point.
/// </summary>
public float minRotation;
/// <summary>
/// The maximum rotation, in degress, to the right of the zero point.
/// </summary>
public float maxRotation;
/// <summary>
/// The accuracy of the weapon, in meters.
/// </summary>
public float accuracy;
/// <summary>
/// The speed at which the weapon may be turned.
/// </summary>
public float turnSpeed;
/// <summary>
/// Rotates and elevates the weapon torwards the target.
/// </summary>
/// <param name="target">The target to aim at.</param>
public virtual void Aim(Vector3 target)
{
// Get a plane through the rotation of the weapon
Plane rot = new Plane(rotator.right, rotator.position);
if (Mathf.Abs(rot.GetDistanceToPoint(target)) < accuracy)
return;
// And rotate towards target
if (rot.GetSide(target))
Rotate(1.0f); //right
else
Rotate(-1.0f); //left
// Get a plane through the elevation of the weapon
Plane elev = new Plane(elevator.up, elevator.position);
if (Mathf.Abs(elev.GetDistanceToPoint(target)) < accuracy)
return;
// And elevate towards target
if (elev.GetSide(target))
Elevate(1.0f); //up
else
Elevate(-1.0f); //down
}
/// <summary>
/// Pivots the weapon up(+) and down(-).
/// </summary>
/// <param name="direction">The direction to pivot; up is positive, down is negative.</param>
public virtual void Elevate(float direction)
{
// Clamp the direction input between -1 and 1...
direction = Mathf.Clamp(-direction, -1.0f, 1.0f);
// Calculate the new angle...
float angle = elevator.localEulerAngles.x + direction * turnSpeed * Time.deltaTime;
if (angle > 180)
angle -= 360;
// Clamp the new angle between the given minimum and maximum...
angle = Mathf.Clamp(angle, -maxElevation, -minElevation);
// Update the transform...
elevator.localEulerAngles = new Vector3(angle, elevator.localEulerAngles.y, elevator.localEulerAngles.z);
}
/// <summary>
/// Pivots the weapon right(+) and left(-).
/// </summary>
/// <param name="direction">The direction to pivot; right is positive, left is negative.</param>
public virtual void Rotate(float direction)
{
// Clamp the direction input between -1 and 1...
direction = Mathf.Clamp(direction, -1.0f, 1.0f);
// Calculate the new angle...
float angle = rotator.localEulerAngles.y + direction * turnSpeed * Time.deltaTime;
if (angle > 180)
angle -= 360;
// Clamp the new angle between the given minimum and maximum...
if (Mathf.Abs(minRotation) + Mathf.Abs(maxRotation) > 0)
angle = Mathf.Clamp(angle, minRotation, maxRotation);
// Update the transform...
rotator.localEulerAngles = new Vector3(rotator.localEulerAngles.x, angle, rotator.localEulerAngles.z);
}

I have a couple of untested solutions for you. Usually for something like this, I test the solution, but I can’t right now. First take a look at my gun script in this answer:

It works by managing the gun only on the local rotation, and gun rotates only on its local ‘x’ axis. The code does this by “transporting” the point so it is effectively (in local coordinates) right in front of the gun so the gun is “forced” to only rotate on the ‘x’ axis.

For your use, given the turret can have any arbitrary rotation, things are more complex. For calculating the ‘Z’ distance, you will need to calculate the distance from the pivot point to the point you projected on the plane you used in my last answer. For the ‘y’ height it will be the signed distance above/below the plane the turret sits on. These two values can be to construct the local rotation of the gun using Quaternion.LookRotation().

As for limiting the rotation, my suggestion is to start with the gun (in the editor) at the midpoint of its travel range. So if the gun could travel up to 90 and down to -20, you would start the gun with an ‘x’ axis rotation of 35.0. Save the local rotation of the gun in a variable in Start(). Each time you calculate a new rotation, test it with Quaternion.Angle() against the local rotation you saved in Start(). If the angle is above the maximum travel angle (one side), then just don’t assign it to ‘transform.localRotation’.