Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by Exarch · Mar 15, 2012 at 10:00 AM · rotationeuleranglesclampturret

Clamping a wrapping rotation.

I am trying to clamp the rotation of turret's barrel in an interesting way. The negative elevation of the barrel is 10 degrees, the positive elevation of the barrel is 350 degrees. 0 degrees is straight ahead. All rotations are being done through localEulerAngles as the turret rides on a body which pitches and rolls to follow terrain.

A visual example: Envision pacman with his mouth open. The 0 degree line is drawn out from the center of his mouth. The top of his mouth is 350 degrees. The minimum is 10 degrees. I want to allow a transform to only rotate through the 20 degree range which denotes the open area.

I have attempted to manipulate Mathf.Clamp and various if statements without success, the problem lies with the fact that my minimum rotation is numerically higher than the maximum as a result of the wrapping past zero.

Is there a way to write an if statement of the form "int > var > int" or anything I can do to not allow a range of 11 to 349.

If any more information is needed, or if I've omitted anything critical, please let me know.

Comment
Add comment · Show 3
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image syclamoth · Mar 15, 2012 at 10:03 AM 0
Share

This is the problem with relying on eulerAngles. It's one of the reasons Quaternions are so often used in game development. Is there any real reason why you can't reimplement it using Quaternions?

avatar image syclamoth · Mar 15, 2012 at 10:06 AM 1
Share

(oh, and why is angle an int?)

Otherwise, you can use something like this:

 if(angle < 180)
 {
     angle = $$anonymous$$athf.Clamp(angle, 0, 10);
 }
 if(angle > 180)
 {
     angle = $$anonymous$$athf.Clamp(angle, 350, 360);
 }

Then, when you increment the angle make sure you are using things like $$anonymous$$athf.LerpAngle (which manages things looping at 360).

avatar image Exarch · Mar 15, 2012 at 10:53 AM 1
Share

I tried Quaternions initially, however I did not have success when I attempted to clamp rotation to one axis only. Nor did I have success attempting to make the turret look at a target point while the platform it was on was being driven all over the terrain. Further, I had a lot of trouble working out a way to set rotation speed to maximum degrees/second. Finally I had a great deal of difficulty making Quaternions work in local space. Ultimately I probably could have gotten it to work, but managed to come up with a localEulerAngles solution much quicker.

The angle is an int because I mistyped "float" :) $$anonymous$$athf.$$anonymous$$oveTowardsAngle uses floats and returns the rotation difference as a float.

// $$anonymous$$ake a rotation from the difference between current and target, then move at aimspeedvert degrees/second elerot = $$anonymous$$athf.$$anonymous$$oveTowardsAngle(currentele.x,vertaim.x,aimspeedvert * Time.deltaTime);

Your example works well but I would have to insert it where the script initially takes the aim angles from the camera, which would be a mess in the scripts current form and take a great deal of re-implimentation. I'll keep it in $$anonymous$$d should I rewrite for any reason though. (It won't work on the barrel directly, because once the first if statement is true, the second could never be)

It would be somewhat interesting to get working,

In the end, I managed to "pull my head out", draw a diagram and work from there, resulting in this:

// Negative Clamp if(elerot > 10){ if(elerot < 90){ elerot = 10;} } // Positive Clamp if(elerot < 350){ if(elerot > 240){ elerot = 350;} }

Which clamps the barrel's elevation effectively.

UnityAnswers seems to work just like real life, work on something for a few evenings, ask the question and figure it out yourself 10 $$anonymous$$utes later...

6 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by fherbst · Mar 15, 2012 at 10:05 AM

Map the higher values down to negative rotations and clamp them, then map back.

Edit: now with source code.

using UnityEngine; using System.Collections;

public class ClampAngleTest : MonoBehaviour {

public float angleIn; public float angleOut; public float minAngle; public float maxAngle;

void Update () { angleOut = ClampAngle(angleIn, minAngle, maxAngle); }

float ClampAngle(float angle, float from, float to) { if(angle > 180) angle = 360 - angle; angle = Mathf.Clamp(angle, from, to); if(angle < 0) angle = 360 + angle;

return angle; }

You can feed that angle directly back to eulerAngles. Try it out by changing angleIn in the Inspector while in play mode.

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Exarch · Mar 15, 2012 at 10:24 AM 0
Share

Right after I clicked submit, I managed to get something working, however I like the idea of mapping angles, I'll have to see if I can find it in the scripting reference.

avatar image Exarch · Mar 15, 2012 at 11:18 AM 0
Share

I can't seem to find the reference page on remapping values. If you would, can you steer me in the right direction?

avatar image fherbst · Mar 15, 2012 at 11:57 AM 0
Share
 using UnityEngine;
 using System.Collections;
 
 public class ClampAngleTest : $$anonymous$$onoBehaviour {
         
 public float angleIn;
 public float angleOut;
 public float $$anonymous$$Angle;
 public float maxAngle;
 
 void Update () {
   angleOut = ClampAngle(angleIn, $$anonymous$$Angle, maxAngle);
 }
     
 float ClampAngle(float angle, float from, float to) {
   if(angle > 180) angle = 360 - angle;
   angle = $$anonymous$$athf.Clamp(angle, from, to);
   if(angle < 0) angle = 360 + angle;
         
   return angle;
 }
avatar image slippdouglas · Dec 16, 2013 at 10:39 PM 0
Share

does not work

avatar image
0

Answer by Owen-Reynolds · Mar 15, 2012 at 05:24 PM

As you noted, EulerAngles.x is unreliable (and the docs caution against using it.) So, just use your own angle, that you say will be -10 to 10. Unity is just fine setting rotations using negative angles:

 float gunDegs = 0; // will be -10 to 10 degrees, of x rotation

 // user changes it, and just in case... :
 gunDegs = Mathf.Clamp(gunDegs, -10, 10);

 gun.localRotation = Quaternion.Euler(gunDegs, 0, 0);
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by TheBeardPhantom · Dec 11, 2013 at 05:29 PM

Technically, to handle any angle thrown at it, you'd want to use this:

 public static float ClampAngle(this float angle) {
     if(angle < 0f)
         return angle + (360f * (int) ((angle / 360f) + 1));
     else if(angle > 360f)
         return angle - (360f * (int) (angle / 360f));
     else
         return angle;
 }

This returns the following:

 Input:        Output:
 754            34
 -1000        80
 50            50
Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image KillMobil · Feb 08, 2014 at 05:59 PM 0
Share

Works For Above 360 but not for below!

avatar image
0

Answer by ryvianstyron · Oct 22, 2015 at 08:13 AM

Hi @Exarch ,

I wrote the following code to rotate a catapult via mouse (left and right) - so I have 2 positive values and it's a wrapping rotation like yours.

 // Get x axis from mouse
 float MouseTurn = Input.GetAxis("Mouse X");
 // rotate your object in the y axis (left and right)
 transform.Rotate(0, MouseTurn * MouseTurnSpeed * Time.deltaTime, 0);
 
 // Set a tracker to check if the angle is in between the values you want
 bool InBetween = false;
 Vector3 currentRotation = transform.localRotation.eulerAngles; Debug.Log("CURRENT ROTATION: " + currentRotation.ToString());
                         
 // Because this is wrapping rotation, I have 2 positive values
 if(currentRotation.y > 320.0f &&currentRotation.y <= 360.0f || currentRotation.y >= 0 && currentRotation.y <= 40.0f)
 {
       InBetween = true;
 }
 else
 {
      InBetween = false;
 }
  // if its outside the range you want, force it back to the closest value within the range and set the rotation via transform.localRotation
 if(!InBetween)
 {
      // Closer to 40
      if(Mathf.Abs(currentRotation.y - 320.0f) > 
              Mathf.Abs(currentRotation.y - 40.0f)) 
      {
              currentRotation.y = 40.0f;
              transform.localRotation = Quaternion.Euler(currentRotation);
              InBetween = true;
      }
      // Closer to 320
      else if(Mathf.Abs(currentRotation.y - 40.0f) >      Mathf.Abs(currentRotation.y - 320.0f))
     {
              currentRotation.y = 320.0f;
              transform.localRotation = Quaternion.Euler(currentRotation);
              InBetween = true;
     }
 }

I hope this helps someone!

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by Tanky · Sep 13, 2017 at 08:54 PM

Try this extension method (C#). It properly handles negative inputs as well as the boundaries on multiples of 360:

     public static float ClampedAngle (this float angleInDegrees)
     {
         if (angleInDegrees >= 360f) {
             return angleInDegrees - (360f * (int)(angleInDegrees / 360f));
         } else if (angleInDegrees >= 0f) {
             return angleInDegrees;
         } else {
             float f = angleInDegrees / -360f;
             int i = (int)f;
             if (f != i)
                 ++i;
             return angleInDegrees + (360f * i);
         }
     }
 
Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Sep 13, 2017 at 10:26 PM 0
Share

A method like this basically already exists. Just use $$anonymous$$athf.Repeat with a length of "360".

Also your method should not be called "clamp" as your method does not clamp anything but just "wrap" the angle. Clamping is pretty much the opposite from wrapping.

Also you don't really read the question. He want to restrict the angle to be in a "20°" range from "-10" to "+10". However since "-10" wraps to "350" that was the actual problem. So he want to allow the value in the range 350 - 360 and 0 - 10

As others have already said the best approach is to remap the value to the range -180 to 180, clamp it (-10 to 10) and map it back.

 // expects angle in the range 0 to 360
 // expects $$anonymous$$ and max in the range -180 to 180
 // returns the clamped angle in the range 0 to 360
 float Clamp(float angle, float $$anonymous$$, float max)
 {
     if (angle > 180f) // remap 0 - 360 --> -180 - 180
         angle -= 360f;
     angle = $$anonymous$$athf.Clamp(angle, $$anonymous$$, max);
     if (angle < 0f) // map back to 0 - 360
         angle += 360f;
     return angle;
 }

avatar image Bunny83 · Sep 13, 2017 at 10:28 PM 0
Share

ps: $$anonymous$$athf.Repeat just does this:

 public static float Repeat(float t, float length)
 {
     return t - $$anonymous$$athf.Floor(t / length) * length;
 }
avatar image MarekM2 · Nov 14, 2018 at 07:05 PM 0
Share

@Bunny83 I have a similar problem, but my object can also rotate backwards, so essentially the $$anonymous$$ and max stay the same, but after an object has been kind of mirrored to point in the opposite direction. Any advice?

  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

14 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Help clamping a Rotation 1 Answer

How to clamp Lookat? 2 Answers

Bizzare problem with limiting camera veritcal rotation 2 Answers

Limit gameobject rotation to -480 and 480 degrees? 2 Answers

Turret rotation clamp. 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges