Problem clamping rotation

I’m using the below code to clamp the rotation of my turret. It works fine on the right side, but when I get to the maximum clamp on the left side it resets to the right side instead of just stopping.

        //rotate chain gun to cursor with clamp
        Vector3 pos = Camera.main.WorldToScreenPoint(chaingun.position);
        Vector3 dir = Input.mousePosition - pos;
        float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        angle = Mathf.Clamp(angle, -150, 0);
        chaingun.rotation = Quaternion.AngleAxis(angle, Vector3.forward);

Example

Imgur: The magic of the Internet

How can I adjust my code to prevent this?

Vector3 dir = Input.mousePosition-new Vector3(Screen.width/2,Screen.height/2,0); // mouse direction relative to the middle of the screen
chaingun.right=Vector3.Slerp(chaingun.right,dir,5*Time.deltaTime);	// Smoothly rotate towards the direction.
chaingun.eulerAngles=new Vector3(0,0,Mathf.Clamp(-Mathf.DeltaAngle(chaingun.eulerAngles.z,0),-150,0)); // Clamp.

//

Unfortunately this doesn’t fix it.

Your best bet is to have local-only float variables to represent traverse and elevation, then change and clamp those floats, and finally drive .localRotation of the parts on their correct Transform axis.

Notes on clamping rotations and NOT using .eulerAngles because of gimbal lock:

How to instantly see gimbal lock for yourself:

All about Euler angles and rotations, by StarManta:

https://starmanta.gitbooks.io/unitytipsredux/content/second-question.html

Quaternion dumper if you absolutely insist on peeking inside the ugly parts:

IF you are talking about cameras…

Camera stuff is pretty tricky… I hear all the Kool Kids are using Cinemachine from the Unity Package Manager.

There’s even a dedicated Camera / Cinemachine area: see left panel.

If you insist on making your own camera controller, do not fiddle with camera rotation.

The simplest way to do it is to think in terms of two Vector3 points in space:

  1. where the camera is LOCATED
  2. what the camera is LOOKING at
private Vector3 WhereMyCameraIsLocated;
private Vector3 WhatMyCameraIsLookingAt;

void LateUpdate()
{
  cam.transform.position = WhereMyCameraIsLocated;
  cam.transform.LookAt( WhatMyCameraIsLookingAt);
}

Then you just need to update the above two points based on your GameObjects, no need to fiddle with rotations. As long as you move those positions smoothly, the camera will be nice and smooth as well, both positionally and rotationally.

Unless you’re asking specifically about 2D physics please don’t use the 2D-Physics tag. This is scripting and Transform code which isn’t specific to 2D-physics.

I’ll remove that tag for you and add “Scripting”.

Thanks.

I’m not sure if I didn’t explain properly but I’m not trying to rotate the camera. I don’t seem to be experiencing gimbal lock.

I’m just trying to rotate an object to face the mouse cursor with clamps.

I apologize for being lazy to fire up Unity on a Sunday, after I’ve been already working today because of a critical milestone.

If you’re still here tomorrow, I will attempt to recreate your scenario and show you what you can improve in your code and how to untangle these kinds of problems in general.

1 Like
Vector3 dir = Input.mousePosition-new Vector3(Screen.width/2,Screen.height/2,0); // mouse direction relative to the middle of the screen
float angle=Vector3.SignedAngle(chaingun.parent.right,dir,chaingun.parent.forward);
chaingun.localRotation=Quaternion.Lerp(chaingun.localRotation,Quaternion.Euler(0,0,angle),5*Time.deltaTime);
chaingun.localEulerAngles=new Vector3(0,0,Mathf.Clamp(-Mathf.DeltaAngle(chaingun.localEulerAngles.z,0),-150,0)); // Clamp
1 Like

This works! Strangely, if I change the speed on line 3 from 5 to 50 it breaks again. Not sure why, is it a bug?

Anyway, thank you!

That parameter is basically the rotation speed and so if you set it too high then your original problem will return where the gun quickly snaps from an angle of -150 to 0 as the cursor goes around the back of the chopper.

Your original code would probably have been okay if you had smoothly rotated the gun from its current direction to the target direction instead of just setting the rotation directly. Although your code would’ve rotated back to angle zero a little earlier than mine.

If you want the rotation to be a little snappier then instead of lerp you can use this:

chaingun.localRotation=Quaternion.RotateTowards(chaingun.localRotation,Quaternion.Euler(0,0,angle),500*Time.deltaTime);

Personally I like Lerp and Slerp because of the easing that the functions provide. Although some people don’t like using those functions in the way I use them because they think it’s not the way God intended for them to be used. But most of us don’t care…

Sorry, God. …but not sorry.

Of course it’s deeper than that and has very little to do with God. Problem with lerp is that it’s frame-independent and so any ad-hoc animation made with it will vary in duration (among other problems). However, there is a way to tame it, here’s for example exponential decay

static public float expDecay(float min, float max, float decay, float deltaTime)
  => max + (min - max) * exp(-decay * deltaTime);

Where rate of decay should be something between 1 and 25.

Mathematically this is equivalent to
f(t)=max+(min-max)\cdot e^{-\lambda t}

This is preferred in place of a classic frame-by-frame lerp, i.e.

void Update() {
  var pos = lerp(pos, target, 0.7);
}

This is pleasant-looking and convenient (esp when target moves), but can be very problematic for several overlapping reasons.

Much more info here (I am thankful this is a topic that is actually properly addressed in recent years)
Also a useful article from 2016.

Edit:
@zulo3d ‘frame-independent’ was a typo, thanks

1 Like

You probably meant frame rate dependent but yes, you’re right. But Lerp used with Time.deltaTime it’s good enough for most use cases. Although I wouldn’t recommend it for having a character jump across gaps but then I also wouldn’t recommend a character controller for the same reason as that is also not truly frame rate independent.

Thanks for the links!.

1 Like