Wojtek Wencel
Wojtek Wencel

Reputation: 2117

Constrain object rotation to behave like joystick

I'm trying to constrain an object's rotation, so that it behaves like a joystick (meaning it can only rotate up to some maximum angle from center).

I tried to constrain rotation on each individual axis, but they behaved really weirdly when rotating (the angle values didn't just grow linearly). The input I'm using to supply this rotation is the rotation of physical controller. How can I do this?

This is how it works now and how I want it to work: rotation Images are 2D but it applies to all axes.

Upvotes: 0

Views: 303

Answers (1)

Serlite
Serlite

Reputation: 12258

It sounds to me like there are two parts to the problem:

  1. Determine whether supplied rotation exceeds maximum allowable rotation of object
  2. If the supplied rotation is too large, reducing that rotation so it fits within the rotational constraints, then applying it

In my code examples, I'll be assuming that the initial rotation of the virtual joystick is zero across the board (Quaternion.identity), and that your supplied rotation is called newRotation.

For the first part, Quaternion.Angle() comes to mind. This gives the angle between two given rotations, and can be used like so:

if (Quaternion.Angle(Quaternion.identity, newRotation) < 30){
    // Angle from initial to new rotation is under 30 degrees
}

For the second part, you'll need some way of reducing the supplied rotation so it is within the allowable angle from the initial rotation. For that, Quaternion.Slerp() is useful. This allows you to interpolate between two rotations, returning a Quaternion that is a combination of the two supplied. For example, this gives back half of newRotation:

Quaternion.Slerp(Quaternion.identity, newRotation, 0.5f);

Putting these two methods together, you can write a clamping method which ensures that the supplied rotation never exceeds a certain angle from the initial rotation. Here's an example usage:

// Maximum angle joystick can tilt at
public float tiltAngle;
// If this isn't your initial rotation, set it in Awake() or Start()
Quaternion initialRotation = Quaternion.identity;

void Update(){
    Quaternion newRotation = MethodToGetSuppliedRotation();
    Quaternion clampedRotation = ClampRotation(initialRotation, newRotation, tiltAngle);
    transform.localRotation = clampedRotation;
}

// Clamps "b" such that it never exceeds "maxAngle" degrees from "a"
Quaternion ClampRotation(Quaternion a, Quaternion b, float maxAngle){
    float newAngle = Quaternion.Angle(a, b);
    if (newAngle <= maxAngle){
        // Rotation within allowable constraint
        return b;
    }
    else{
        // This is the proportion of the new rotation that is within the constraint
        float angleRatio = maxAngle / newAngle;
        return Quaternion.Slerp(a, b, angleRatio);
    }
}

Hope this helps! Let me know if you have any questions.

Upvotes: 1

Related Questions