Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Limiting rotation with Mathf.Clamp

Discussion in 'Scripting' started by Baskyn, Feb 21, 2013.

  1. Baskyn

    Baskyn

    Joined:
    Feb 17, 2013
    Posts:
    67
    Ok, so what I'm doing is making a script that I'm adding to the tutorial car to keep it from flipping over whether you are on the ground or in the air. I'm trying to get Mario Kart type physics, which I'm sure everyone here knows it. I tried constraining the x and z axis but the car can't drive on slopes if you do that.
    I'm having problems with this. Only half of it works. I have to clamp both the positive and negative of Z and X. The problem is it's constraining the negative constantly when it shouldn't be. If there is a way to just LIMIT the rotation rather than telling it what rotation to be, that would be awesome. I haven't figured out how to do that yet. Any help here would be awesome! Thanks! :)

    Here's my code:

    Code (csharp):
    1.    
    2.     private var angleX : int = 0;
    3.     private var angleZ : int = 0;
    4.     private var anglenX : int = 0;
    5.     private var anglenZ : int = 0;
    6.     function Update ()
    7.     {
    8.         //limits the x rotation so if you rotate too far, your rotation is reset
    9.         if (transform.eulerAngles.x > 90){
    10.        
    11.         angleX = Mathf.Clamp(angleX, 1, 90);
    12.         transform.eulerAngles.x = angleX;
    13.         }
    14.        
    15.         if (transform.eulerAngles.x < -90){
    16.        
    17.         anglenX = Mathf.Clamp(angleX, 1, -90);
    18.         transform.eulerAngles.x = anglenX;
    19.         }
    20.        
    21.         //limits the z rotation so if you rotate too far, your rotation is reset
    22.         if (transform.eulerAngles.z > 90){
    23.         angleZ = Mathf.Clamp(angleZ, 1, 90);
    24.         transform.eulerAngles.z = angleZ;
    25.         }
    26.        
    27.         if (transform.eulerAngles.z < -90){
    28.         anglenZ = Mathf.Clamp(anglenZ, 1, -90);
    29.         transform.eulerAngles.z = anglenZ;
    30.         }
    31.     }
    32.         }
    33.     }
    34.  
     
    Last edited: Feb 21, 2013
  2. chelnok

    chelnok

    Joined:
    Jul 2, 2012
    Posts:
    680
    When you are clamping negative value;
    anglenX = Mathf.Clamp(angleX, 1, -90);

    i think it should be min, max:
    anglenX = Mathf.Clamp(angleX, -90, 1);
     
  3. Baskyn

    Baskyn

    Joined:
    Feb 17, 2013
    Posts:
    67
    I tried that but it didn't work.

    Could you explain what the value, min and max mean for a clamp? I don't fully understand them yet.
     
  4. chelnok

    chelnok

    Joined:
    Jul 2, 2012
    Posts:
    680
    static function Clamp (value : float, min : float, max : float) : float

    where
    value is the number you need to clamp
    min is the minium you want your number to be
    max is the maximum you want your number to be

    if your min and max are 10 and 20, the function returns 10 if your value (num you need to clamp) is below 10. If its 11, return value is 11, 'cos its between 10 and 20. If your value is more that 20, return value is 20.
     
  5. chelnok

    chelnok

    Joined:
    Jul 2, 2012
    Posts:
    680
    oh, btw didnt check what you actually was trying to do :) in your case its more like this:

    Code (csharp):
    1.         if (transform.eulerAngles.x > 90){
    2.         transform.eulerAngles.x = 90;
    3.         }
    4.  
    5.  
    6.         if (transform.eulerAngles.x < -90){
    7.           transform.eulerAngles.x = -90;
    8.         }
     
  6. EliteMossy

    EliteMossy

    Joined:
    Dec 2, 2012
    Posts:
    513
    Kinda weird to see it done like this, i would just do

    Code (csharp):
    1.  
    2. angleZ = Mathf.Clamp(angleZ, -90, 90);
    3. angleX = Mathf.Clamp(angleX, -90, 90);
    4. transform.eulerAngles.z = angleZ;
    5. transform.eulerAngles.x = angleX;
    6.  
    No need for if statements :)

    even better you could do

    Code (csharp):
    1.  
    2. transform.eulerAngles.z = Mathf.Clamp(angleZ, -90, 90);
    3. transform.eulerAngles.x = Mathf.Clamp(angleX, -90, 90);
    4.  
     
  7. Baskyn

    Baskyn

    Joined:
    Feb 17, 2013
    Posts:
    67
    Hmm. I tried using your code and it just makes the car flip out and fall through the level.

    Is there a better way to do this? I'm trying to keep the car horizontal or have the bottom facing the track when you're in the air and make it impossible to flip. I'm not sure the car physics I have right now are right though, seeing as they're meant to be realistic.

    EDIT: This was a reply to chelnok. I'll try out your ways EliteMossy and get back to you :)

    Ok, well that doesn't work either. It's acting like I put constraints on the x and z axis. It just stays horizontal and won't rotate at all when going up slopes.
     
    Last edited: Feb 21, 2013
  8. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,196
    Bumpz!
    Unity really needs a proper and native way of clamping angles!
     
    loldude482 likes this.
  9. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,196
    Ok, I think I got it:
    Code (CSharp):
    1.     static float ClampAngle(float angle, float min, float max)
    2.     {
    3.         if (min < 0 && max > 0 && (angle > max || angle < min))
    4.         {
    5.             angle -= 360;
    6.             if (angle > max || angle < min)
    7.             {
    8.                 if (Mathf.Abs(Mathf.DeltaAngle(angle, min)) < Mathf.Abs(Mathf.DeltaAngle(angle, max))) return min;
    9.                 else return max;
    10.             }
    11.         }
    12.         else if(min > 0 && (angle > max || angle < min))
    13.         {
    14.             angle += 360;
    15.             if (angle > max || angle < min)
    16.             {
    17.                 if (Mathf.Abs(Mathf.DeltaAngle(angle, min)) < Mathf.Abs(Mathf.DeltaAngle(angle, max))) return min;
    18.                 else return max;
    19.             }
    20.         }
    21.  
    22.         if (angle < min) return min;
    23.         else if (angle > max) return max;
    24.         else return angle;
    25.     }
    This is what it does (at least it is supposed to do)
     
    Nad_B, 13E12K, skinwalker and 12 others like this.
  10. WILEz1975

    WILEz1975

    Joined:
    Mar 23, 2013
    Posts:
    368
    Very very thanks Rodolfo.
     
    Rodolfo-Rubens likes this.
  11. soltex1

    soltex1

    Joined:
    Apr 2, 2015
    Posts:
    14
    Perfect! Thanks! :)
     
    Rodolfo-Rubens likes this.
  12. RealSoftGames

    RealSoftGames

    Joined:
    Jun 8, 2014
    Posts:
    220
    thanks for this, it was doing my head in when clamp was not working with angles.
     
    Rodolfo-Rubens likes this.
  13. Zythus

    Zythus

    Joined:
    Jun 28, 2014
    Posts:
    30
    Hey, glad you posted here, since I have been trying to do this as well, and even though it feels like I am close, I've spent hours going through peoples' posts in these threads - none of them helped enough. That's how I am ;/. What I have is this
    Code (CSharp):
    1.                 if (Input.GetTouch(0).phase == TouchPhase.Moved)
    2.              {
    3.                  touchPointX = Input.GetTouch(0).deltaPosition.x;
    4.                  touchPointY = Input.GetTouch(0).deltaPosition.y;
    5.                  camRotationX = Camera.main.transform.rotation.x;
    6.                  camRotationX = Mathf.Clamp(camRotationX, 1, 45); // tried to use that in an if statement below
    7. if(camRotationX > 1 && camRotationX < 45) //or sth like that                        
    8.  
    9. transform.RotateAround(player.transform.position, (new Vector3(-touchPointY, touchPointX, 0.0f)), 30 * Time.deltaTime);
    10.  
    11.                  Camera.main.transform.LookAt(transform.position + transform.forward * 10f);
    12.              }
    So what I was trying to do, is to read the touch position, and move camera around the gameobject's position accordingly - 360 degrees around it, but only 45 degrees vertically, so the camera will always look at the gameobject. It looks like it moves around just okay, but I want to limit X axis (it is the axis which changes when trying to swipe vertically), so the camera doesn't go into ground when swiping down, or over the gameobject when swiping up.
    I want to keep the value between 1 and 45. I used clamp, but the camera always got stuck after achieving e.g. the lowest value, and it couldn't be moved anymore. Any tips?
    Thanks in advance
     
  14. schreibmachine

    schreibmachine

    Joined:
    Oct 24, 2017
    Posts:
    1
    YOU SIR, are a GENIUS!
    This should really be implemented into Unity. I am beyond happy I found this, after ~9h straight programming (without any success) searching for something like this. It's so great thank you so very much!
     
  15. UniqueMAX

    UniqueMAX

    Joined:
    Feb 4, 2015
    Posts:
    1
    I think I found an even easier solution after I tried to debug the rotation value. Turns out it didn't show the negative values, but instead it went to 360 and lower for the previous circle.

    So I've used something like this:

    Code (CSharp):
    1.  float RotRange = SecMark.transform.localRotation.eulerAngles.x;
    2.         if (RotRange > 300f && RotRange <= 360 + MinCam)
    3.             RotRange = MinCam;
    4.         if (RotRange >= MaxCam && RotRange <= 300f)
    5.             RotRange = MaxCam;
    The down side of this method is that if you wish to place a min value -60 and lower it'd work wrong so you'd have to change the value 300f to 290 or lower. Just pick a number somewhere in a range you totally won't need.
     
  16. TaskiranXKTK

    TaskiranXKTK

    Joined:
    Jan 27, 2017
    Posts:
    2
    This still needs to be implemented! Thank you!
     
    MikeMnD and Rodolfo-Rubens like this.
  17. rbosse

    rbosse

    Joined:
    May 21, 2015
    Posts:
    18
    Omg, I love you!
     
    MikeMnD and Rodolfo-Rubens like this.
  18. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    505
    I'm using this to clamp my first person camera X and Y rotation that follows the mouse. It works well unless I put 2 negative floats like -1 and -30 or even 0 and -30, because 0 changes the value to 360, while (-5) turns it into 355 so the clamping doesn't work in those cases.
     
  19. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    Dude thanks so much, my method was causing it to snap to the min value and was being a pain in the ass. I hate Quaternions lol
     
  20. 13E12K

    13E12K

    Joined:
    May 4, 2014
    Posts:
    20
    This thread and the solution is a gem! 6 years and unity hasn't implemented a basic and elegant method like this to clamp the angles...
     
    DevMerlin likes this.
  21. kankeus

    kankeus

    Joined:
    Dec 3, 2013
    Posts:
    12
    I think this is the most elegant way to clamp any angle between 0-360 degrees


    Code (CSharp):
    1.  
    2. public static float ClampAngle360(float angle)
    3.  {
    4.     angle = angle % 360;
    5.     if (angle < 0.0f)
    6.         angle += 360.0f;
    7.     return angle;
    8. }
    9.  

    Then you can clamp "min" angles between 180-360 degrees and "max" angles between 0-180 degrees even if your actual angle is negative.

    e.g. (just made up random values):
    Code (CSharp):
    1.  
    2. float min_angle = 270;
    3. float max_angle = 90;
    4.  
    5. float angle = -45;
    6. angle = ClampAngle360(float angle);
    7.  
    8. if (angle > 180 && angle < min_angle) angle = min_angle;
    9. if (angle <= 180 && angle > max_angle) angle = max_angle;
    10.