First we'll make a couple utility functions:
// Map an angle into a standard range [-180, 180) degrees.
// (This matches Atan2 return range, avoids special cases for angles > 360 etc.,
// and makes it easy to rotate "the shortest way" between two angles.)
float NormalizeAngle(float degrees) {
degrees += 180f;
float fullTurns = Mathf.Floor(degrees/360f);
return degrees - fullTurns * 360f - 180f;
}
// Clamp an angle between a clockwise limit and counterclockwise limit (all in degrees),
// snapping out of bounds angles to whichever bound is closest around the circle.
float ClampAngle(float degrees, float cwLimit, float ccwLimit) {
// Get all inputs into standard range, to minimize wrap-around cases to deal with.
degrees = NormalizeAngle(degrees);
cwLimit = NormalizeAngle(cwLimit);
ccwLimit = NormalizeAngle(ccwLimit);
// If in bounds, return the angle. Two cases for whether the allowed
// range does not / does cross the -180/180 wrap-around point.
if (ccwLimit >= cwLimit) {
if (degrees <= ccwLimit && degrees >= cwLimit)
return degrees;
} else {
if (degrees >= cwLimit || degrees <= ccwLimit)
return degrees;
}
// If we get here, the angle is outside our range.
// Find its difference from each limit:
float cwDifference = Mathf.Abs(NormalizeAngle(degrees - cwLimit));
float ccwDifference = Mathf.Abs(NormalizeAngle(degrees - ccwLimit));
// Clamp to the closest of the two limits:
if (cwDifference <= ccwDifference)
return cwLimit;
else
return ccwLimit;
}
Now we can compute your new rotation angle like so:
float GetNewRotation(float degreesPerSecond, float deltaTimeSeconds) {
// Form a vector pointing from the turret to the player.
Vector2 toTarget = player.position - turret.position;
// Extract the bearing angle from this vector, in range [-180, 180).
float targetAngle = Math.Atan2(toTarget.y, toTarget.x) * Mathf.Rad2Deg;
// Clamp to within our turret's min/max range.
targetAngle = ClampAngle(targetAngle, turret.cwLimit, turret.ccwLimit);
// Find shortest signed angle between current and target rotation.
float difference = NormalizeAngle(targetAngle - turret.rotation);
// Work out maximum travel this frame, and clamp difference to those bounds.
float maxAngleStep = degreesPerSecond * deltaTimeSeconds;
// Can be replaced with a Clamp(value, min, max) function if you have one.
float step = Mathf.Min(Mathf.Max(difference, -maxAngleStep), maxAngleStep);
// Increment our rotation angle by this limited step, and return a normalized version.
return NormalizeAngle(turret.rotation + step);
}