Limiting rotation of an object (4221)

Hi,

I'm making a little game where you control a bunker on the ground defending itself from bombers and paratroopers. The bunker has a cannon which the player can rotate to aim at her enemies. My problem is that there is no limit to the rotation, so right now the player can aim the cannon straight down into the ground. What I would like to do is to limit the possible rotation between the values of, say, 0 to 180 along the x-axis.

So how can I limit the rotation values for an object?

6 Answers

6

You can use the same code from MouseLook.cs. The following code will lock rotation between -90 and 90 degrees in the Z-axis.

Here is an example:

// gobal 
private float rotationZ = 0f;
private float sensitivityZ = 2f;

void lockedRotation()
{
   rotationZ += Input.GetAxis("Mouse X") * sensitivityZ;
   rotationZ = Mathf.Clamp (rotationZ, -90, 90);
			
    transform.localEulerAngles = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y, -rotationZ);
}

Joels answer provides the best Solution, using the axis to first affect the rotation is the best way to demonstrate the clamp, thanks.

Hi @joel_b , I had a related issue and came across this post while searching. Just wanted to say thanks for taking the time to write this, I've had an issue with Quaternions in the MouseLook script for a long time, but never altered they way I handled rotations until reading your transform.localEulerAngles = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y, -rotationZ); line. After that I was able to make my force-player-look code work just fine without weird interactions with the Quaternion. So thanks for spurring on my thinking!

Short, sweet, and effective! Thanks!

There is a slight improvement that can be made to this. Instead of separating rotationZ into it's own variable, and then repeating tranform.localEulerAngles._, you can use the fact that Vector_ are structs, and passed-by-value. Just manipulate the final localEulerAngles vector directly and then plug it back to the transform: var rotation = transform.localEulerAngles; rotation.z = Mathf.Clamp (rotation.z + Input.GetAxis("Mouse X") * sensitivityZ, -90, 90); transform.localEulerAngles = rotation;

You can limit the rotation of an object like this, for example if you wanted to limit the X-rotation of an object between 30 and 150 degrees:

float MinClamp = 30;
float MaxClamp = 150;

// Other movement needs to be calculated first, so
// we use LateUpdate() instead of Update().
void LateUpdate()
{
     transform.rotation.eulerAngles = new Vector3(
          Mathf.Clamp(transform.rotation.eulerAngles.x, MinClamp, MaxClamp),
          transform.rotation.eulerAngles.y,
          transform.rotation.eulerAngles.z
     );
}

You can obviously modify this to clamp any angle and any value you want, and it can be in its own script file, you don't have to include it in another script.

Limiting a single axis using eulerAngles tends to be quite problematic due to internal quaternion conversions. For example, this code will fail when you try to rotate X past 90. It won't work for values like min=-30, max=30 either. It's generally better to track rotations yourself, like the MouseLook script does. (Just to nitpick, transform.rotation.eulerAngles is redundant; you only need transform.eulerAngles.)

Ah, didn't know that. Is there some better way of limiting like this (using Update) than using the euler angles?

@SpikeX: Yes, use your own variable for tracking the rotation in combination with a ClampAngle function, and apply that to the transform, like MouseLook does. :)

@Eric5h5 I think you can get the negatives to work with a little bit of logic. Like: var localEuler = transform.localEulerAngles; if (localEuler.z > 180) localEuler.z = -360 + localEuler.z; localEuler.z = Mathf.Clamp(localEuler.z, min, max); transform.localEulerAngles = localEuler; As a (way) more experienced programmer, are there any pitfalls you can see with something like that?

You can use Quaternion.Slerp() to limit the rotation of an object, for instance:

transform.localRotation = Quaternion.Slerp(rot, Quaternion.Euler(Vector3.forward), limit);

This would blend between Quaternion rot and forward based on limit.

You can use quaternion function
Quaternion.RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)
to restrict rotation
like this:

Quaternion ClampRotationXByLookDirection(Quaternion curentRotation)
{
    Vector3 lookDirectionX = Vector3.ProjectOnPlane(lookDirection, Vector3.right).normalized;
    Quaternion lookRotation = Quaternion.LookRotation(lookDirectionX);
    Quaternion towardsRotation = Quaternion.RotateTowards(lookRotation, curentRotation, lookDeltaX);
    return towardsRotation;
}

Quaternion ClampRotationYByLookDirection(Quaternion curentRotation)
{
    Vector3 lookDirectionY = Vector3.ProjectOnPlane(lookDirection, Vector3.up).normalized;
    Quaternion lookRotation = Quaternion.LookRotation(lookDirectionY);
    Quaternion towardsRotation = Quaternion.RotateTowards(lookRotation, curentRotation, lookDeltaY);
    return towardsRotation;
}

This limits how far from vertical the Y-axis of an object’s Transform can get. You set yLimit to the cosine of the maximum angle (here, about 45 degrees).

using UnityEngine;

public class RollLimiter : MonoBehaviour
{
    public float yLimit = .707f;

    Quaternion prior;

    void Start()
    {
        prior = transform.rotation;
    }

    void Update()
    {
        if ((transform.rotation * Vector3.up).y < yLimit)
        {
            transform.rotation = prior;
        }

        prior = transform.rotation;
    }
}

Take a look at the MouseLook script in Standard Assets for a way of limiting rotation.

@Eric This would only apply if the cannon was in first-person mode.

@SpikeX: No, the principle is the same. You wouldn't actually use the MouseLook script, you'd use the angle-limiting bits.

Oh. Yeah, that would work, but it would require a bit of script modification, and I tend to not put too much faith into the people asking the questions. ;)

I'd rather assume that people usually need enough detail to answer the question in most cases, and can ask follow-up questions if they need more info, rather than trying to cover every possible situation upfront. MouseLook is pretty straightforward and everybody has it, so I didn't think there was a reason to paste the code here in this case.