- Home /
Limit local rotation
Hi,
i have an issue i am not able to resolve myself. I have looked around topics here, but i couldnt find exactly what i am looking for.
I have an object, which is rotating around its axis, i.e. its orbiting on place. But i need to limit the rotation when it stops. So it means the object can have local y rotation anything between 0 and 360. Lets say its current y rotation is 100. I need to limit in +/- 40 interval. I.e. 60 - 140. Thats not problem, i am clamping it using eulerAngle.y. But when the current rotation is for example 20, then the issue comes up as it the interval is -20 to 60. Which causes issues as the rotation is only 0 - 360. So when the current rotation gets to this break point, whole limit code brakes and it does some funky stuff. Is there anyway how to prevent it from going in 0-360 interval and have negative values for rotation ? Or any other solution ?
Thanks a lot for any reply.
Luke
This is really just a coding question, so you should post your code.
Hi! Good news everyone! I'm using Unity 5.3.4 and there's no need for any angle conversion or special clamping. Quaternion.Euler() takes care of it. And it understands negative angles. So you just say $$anonymous$$athf.Clamp(angle, $$anonymous$$, max)
and you're done. Have a nice day!
Answer by aldonaletto · Jul 11, 2011 at 04:50 AM
This is really a big problem: the limits below 0 and above 360 can't become negative or higher than 360; to further complicate matters, if you rotate the object past 359, it returns to 0; if you rotate the object below 0, it returns to 359. But I created a function similar to Mathf.Clamp which I think may solve your problem:
function ClampAngle(angle: float, min: float, max: float): float {
if (angle<90 || angle>270){ // if angle in the critic region...
if (angle>180) angle -= 360; // convert all angles to -180..+180
if (max>180) max -= 360;
if (min>180) min -= 360;
}
angle = Mathf.Clamp(angle, min, max);
if (angle<0) angle += 360; // if angle negative, convert to 0..360
return angle;
}
This function has some limitations: it uses Mathf.Clamp if angle is in the range 90 to 270, and convert everything to the -180..+180 when angle is outside this range. In any case, the result is converted to 0..360 (the range eulerAngles accepts).
You can use it this way:
transform.eulerAngles = Vector3(0, ClampAngle(angle, ang0-40, ang0+40), 0);
Please notice that this function has a "capture" range: if the parameter angle is between -90 and +90 degrees around the center of the range, it will be clamped correctly; if its out of this range, the function may "think" it is at the "other side" of the circle, and clamp to the wrong limit - for example: if the range is centered at 120 degrees, an angle lower than 270 will be clamped to 160 degrees, but above 270 it will be clamped to 80 degrees. This will not be a problem if you're clamping the angle returned by eulerAngles after a small rotation (what seems to be your case), since it will not be too away from the range. If you want to clamp an arbitrary angle to the limits, however, this problem may arise.
Iam having small rotation, so its not problem. I have tested it very intensly and it works very smoothly withouth any issue so far. So thanks a lot again :).
I was trying to do something similar, but for clamping an arbitrary angle to the limits, so the problem that aldonaletto mentioned in his comment occurred. I started a new question thread about it here: http://answers.unity3d.com/questions/226639/limit-local-rotation-around-x-y-and-z-axes-when-ro.html
I saw this been looking for ages for something good. This is absolutely brilliant work thanks very much
Answer by mkgame · Apr 19, 2016 at 11:22 PM
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!
mkgame. Seeing your code, i think you don't need to check for negative values after calling NormalizeAngle.
Answer by immerhart · Oct 19, 2012 at 07:07 PM
its a little late, but here my approach:
m_startRotation = transform.rotation.eulerAngles.y;
m_minRot = m_startRotation - 90.0f;
m_maxRot = m_startRotation + 90.0f;
m_isTranslationNessecary = false;
// check if rotation happens between 0/360
if(m_minRot <= 0.0f)
m_minRot += 360.0f;
if(m_maxRot >= 360.0f)
m_maxRot -= 360.0f;
if(m_minRot > m_maxRot)
m_isTranslationNessecary = true;
and this function checks if the obj can be rotated:
private float ClampAngle(float angle, float min, float max, bool isTranslationNecessary)
{
if(!isTranslationNecessary)
{
if(angle < min )
return min;
if(angle > max)
return max;
}
else
{
if(angle > max && angle < min)
{
if(min - angle > angle - max)
return max;
else
return min;
}
}
return angle;
}
Answer by VictorPinto90 · Jun 13, 2015 at 04:13 PM
Here is my solution. I have a camera thar rotates around a target in X and Y axis. I'm only limiting the angle in X-Axis
public void OnDrag (PointerEventData eventData){
float deltaX = Mathf.Clamp(eventData.delta.x,-maxPointerDelta,maxPointerDelta)*Time.smoothDeltaTime*speed;
float deltaY = Mathf.Clamp(eventData.delta.y,-maxPointerDelta,maxPointerDelta)*Time.smoothDeltaTime*speed;
Vector3 relativePos = cameraTransform.position - handler.position;
Vector3 v1 = new Vector3(relativePos.x,0,relativePos.z);
float angleX = Vector3.Angle(v1,relativePos);
float futureangle = angleX + -deltaY;
if(futureangle<maxAngle && futureangle>minAngle){
cameraTransform.RotateAround(target.position,cameraTransform.right,-deltaY);
}
cameraTransform.RotateAround(targer.position,Vector3.up,deltaX);
}
Answer by rblkwa · Nov 22, 2016 at 02:38 PM
public void SetRotation(float amount) {
float clampedAngle = Mathf.Clamp(CheckAngle(transform.eulerAngles.x - amount), -15, 15);
transform.eulerAngles = new Vector3(clampedAngle, transform.eulerAngles.y, transform.eulerAngles.z);
}
public float CheckAngle(float value)
{
float angle = value - 180;
if (angle > 0)
return angle - 180;
return angle + 180;
}
Your answer
Follow this Question
Related Questions
Limit local Rotations on one axis 3 Answers
Rotating Object on its Local World Axis 4 Answers
Limit Object Angle 2 Answers
How to use Euler angles to rotate an object ? 5 Answers
Problems caused by non-unique Euler angle solutions 1 Answer