The following code is stable for all angles. Both methods can be static. NormalizeAngle method fix the angles above 360 and under -360 degree and the ClampAngle method normalize the angles from -180 to 180 to make the angles clamp-able. You can call this method with e.g. ClampAngle(angle, 3450, -70), ClampAngle(angle, -70, 70), ClampAngle(angle, -470, 5604). Its stable! I used this code to limit the angles of my hover vehicle, to avoid turning around it.
/** Normalize angles to a range from -180 to 180 an then clamp the angle
* with min and max.
*/
protected float ClampAngle(float angle, float min, float max) {
angle = NormalizeAngle(angle);
if (angle > 180) {
angle -= 360;
} else if (angle < -180) {
angle += 360;
}
min = NormalizeAngle(min);
if (min > 180) {
min -= 360;
} else if (min < -180) {
min += 360;
}
max = NormalizeAngle(max);
if (max > 180) {
max -= 360;
} else if (max < -180) {
max += 360;
}
// Aim is, convert angles to -180 until 180.
return Mathf.Clamp(angle, min, max);
}
/** If angles over 360 or under 360 degree, then normalize them.
*/
protected float NormalizeAngle(float angle) {
while (angle > 360)
angle -= 360;
while (angle < 0)
angle += 360;
return angle;
}
Use it in your code to limit the angles:
transform.eulerAngles = new Vector3(0, ClampAngle(angle, -40, 40), 0);
Or like this for fake tilt to terrain normal:
// Raycat down to terrain, in my case the GameManager.TERRAIN_MASK is
// the terrain mask.
Vector3 linecastTo = new Vector3(transform.position.x, transform.position.y - 100, transform.position.z);
Vector3 linecastFrom = new Vector3(transform.position.x, transform.position.y + 20, transform.position.z);
bool hitSomewhat = Physics.Linecast(linecastFrom, linecastTo, out hitInfo, GameManager.TERRAIN_MASK);
float SwingSpeed = 1;
// Difference between our up vector and the terrain normal vector.
Quaternion tilt = Quaternion.FromToRotation(transform.up, hitInfo.normal);
Vector3 rot = transform.eulerAngles;
// Rotation for the next frame until this value ClampAngle(tilt.eulerAngles.x, -30, 30)
// has been achieved. Max tilt is 30, even on a 45 degree terrain mesh normal!
Quaternion qRot = Quaternion.Euler(
Mathf.LerpAngle(rot.x, ClampAngle(tilt.eulerAngles.x, -30, 30),
Time.deltaTime * SwingSpeed),
rot.y,
Mathf.LerpAngle(rot.z, ClampAngle(tilt.eulerAngles.z, -30, 30),
Time.deltaTime * SwingSpeed));
// RB is the rigidbody, smooth physical change because of the LerpAngle before.
RB.MoveRotation(qRot);
Happy coding!