We solved this problem in the past by converting both the upper and lower limit, as well as the current angle, into a defined "clamping space" (e.g., 0<=limitRight<=360 and limitLeft<=limitRight). Using this method, we can supply our limits as an arbitrary number (e.g., even way outside the 0..360 interval), and it will still be interpreted correctly.
One of the main advantages is, that you can define any interval without twisting your brain thinking about wrap-around problems. For example, limitLeft=-10 and limitRight=10 maps to -10..10, but so does 350..10, or even -370..-350. On the other hand, it is also possible to map to the inverse interval: limitLeft=10 and limitRight=-10 maps to 10..350, and therefore allows only values >10 and <-10, and omits the range -10..10.
Furthermore, we added additional checks to a) correctly initialize the angle to the nearest border if it is outside the given interval, and b) if the current angle is set outside the allowed interval externally (e.g., by editing it directly in the Inspector), limit the interaction to one direction, the direction towards the nearest limit border (hard to explain, try it for yourself).
(Note that it can happen for large rotSpeed and the special case of "almost-full" 360 intervals (for example, 1 to 360) that the change in angle between two frames is too large and it "jumps" over the interval that is outside the allowed range (0 to 1))
using UnityEngine;
public class LimitedRotation : MonoBehaviour {
// limitLeft is the minimal allowed angle
public float limitLeft = -10;
// limitRight is the maximal allowed angle
public float limitRight = 10;
// rotSpeed is a general scaling factor
public float rotSpeed = 200;
private bool haveLimits;
void Start(){
float newAngle=0;
// prepare rotation limit tests (left/right)
if(limitLeft==limitRight){
// unrestricted rotation
haveLimits=false;
}else{
haveLimits=true;
// make sure 0<=limitRight<=360 and limitRight-limitLeft=(0..360) (-360<=limitLeft<=360)
while(limitRight<0)
limitRight+=360;
while(limitRight-limitLeft>=360)
limitLeft+=360;
while(limitLeft>limitRight)
limitLeft-=360;
if(limitLeft>0){
// 0 is not in allowed interval; initialize the angle to the nearest valid boundary
float rightLimit=Mathf.Min(limitRight,360-limitRight);
float leftLimit=Mathf.Min(Mathf.Abs(limitLeft),360-Mathf.Abs(limitLeft));
if(leftLimit>rightLimit)
newAngle = limitRight;
else
newAngle = limitLeft;
}
}
transform.localEulerAngles=new Vector3(0,0,newAngle);
}
void Update(){
float deltaAngle=Input.GetAxis("Horizontal");
float oldAngle = transform.localEulerAngles.z;
float newAngle = oldAngle - rotSpeed * deltaAngle * Time.smoothDeltaTime;
if(haveLimits){
while(oldAngle>limitRight)
oldAngle-=360;
if(oldAngle>=limitLeft)
{
// clamp to limit
while(newAngle>limitRight)
newAngle-=360;
if(newAngle<limitLeft)
if(deltaAngle<0)
newAngle=limitRight; // rotate left limit
else
newAngle=limitLeft; // rotate right limit
}else{
// we are outside the limit
// so only allow movement towards nearest limit
float distToRight=Mathf.Abs(oldAngle-limitRight);
distToRight=Mathf.Min(Mathf.Abs(oldAngle-limitRight-360),distToRight);
distToRight=Mathf.Min(Mathf.Abs(oldAngle-limitRight+360),distToRight);
float distToLeft=Mathf.Abs(oldAngle-limitLeft);
distToLeft=Mathf.Min(Mathf.Abs(oldAngle-limitLeft-360),distToLeft);
distToLeft=Mathf.Min(Mathf.Abs(oldAngle-limitLeft+360),distToLeft);
if(deltaAngle<0 && distToLeft>distToRight ||
deltaAngle>0 && distToLeft<distToRight)
newAngle=oldAngle;
}
}
while(newAngle<0) newAngle+=360;
while(newAngle>360) newAngle-=360;
transform.localEulerAngles=new Vector3(0,0,newAngle);
}
}
(yes, all "while()..." commands can be optimized by using an appropriate %/multiplication/fraction computation, instead of a stupid loop)
Note, in the code above limitLeft/limitRight can only be adjusted to a certain amount on-the-fly, since the initialization is done in Start(). If you want to adjust the limits freely (e.g., via script etc.), you should move the that initialization from Start() to an Update() function.
As a quick update to what I've tried, I also tried doing a simple check such as "if Z rotation is less than 10, increase, if greater than 350, decrease" but this again causes problems with the wrapping.
– anon25104582So, by bank, you mean a Z-rotation? When you bank, are you also turning that direction? Side-slipping (not changing Y-angle, but sliding a little sideways as you move forwards?) Bank+turn or move can drive you into the ground if you aren't careful.
– Owen-ReynoldsRight, z-rotation. There's no other turning happening, just a z-rotation.
– anon25104582