How to Add Limits to Rotating Wheel?

I have a wheel that the player can click on with their mouse, and drag in a circular motion to rotate it on its Z axis. With the help of 2 different scripts I’ve found online and combined in my game, I also have it so that the continuous Z rotation is stored, rather than simply 0 - 360, then wrapping around and restarting.

This is all working well, except for the limits. I’m trying to limit the wheel’s Z rotation between -2000 and 25. What I have works for the actual rotation of the wheel, but once the wheel reaches and stops at one of the limits, I’m still able to keep rotating the value with the mouse. Because of this, if I keep moving my mouse around the wheel even after it has stopped, the continuous rotation keeps going, so if I had hit, say, the -2000 limit, but had kept rotating, I would have to ‘undo’ all the extra rotation by rotating back in the opposite direction until I passed -2000 again, before the wheel would actually rotate again.

How can I stop all the rotations at the limit, so even if I keep dragging my mouse around the wheel after it has reached the limits, it doesn’t keep adding on rotation?

I hope I was able to explain this well. I’m also wondering how I would go about lerping the rotation, so the wheel doesn’t immediately rotate with the exact movement of the mouse, but has a little bit of a drag to it? The behavior of the wheels in “Amnesia: Justine” are what I’m aiming for.

This is what I have so far:

using UnityEngine;
using System.Collections;

public class SpinWheel : MonoBehaviour {

	public float yAngle;

	private Camera myCam;
	private Vector3 screenPos;
	private float angleOffset;
	public bool inZone = false;
	public bool looking = false;

	float newRotationValue;
	float oldRotationValue;

	void Start () {
		myCam=Camera.main;
		yAngle = transform.eulerAngles.y;
	}

	void Update () {

		if (inZone == true && looking == true)
		{
			//This fires only on the frame the button is clicked
			if (Input.GetMouseButtonDown(0))
			{
				screenPos = myCam.WorldToScreenPoint (transform.position);
				Vector3 v3 = Input.mousePosition - screenPos;
				angleOffset = (Mathf.Atan2(transform.right.y, transform.right.x) - Mathf.Atan2(v3.y, v3.x))  * Mathf.Rad2Deg;

				GetComponent<AudioSource>().Play();
			}

			//This fires while the button is pressed down
			if (Input.GetMouseButton(0))
			{
				Vector3 v3 = Input.mousePosition - screenPos;
				float angle = Mathf.Atan2(v3.y, v3.x) * Mathf.Rad2Deg;

				newRotationValue = angle+angleOffset;
				while (newRotationValue < oldRotationValue - 180f) newRotationValue += 360f;
				while (newRotationValue > oldRotationValue + 180f) newRotationValue -= 360f;
				oldRotationValue = newRotationValue;

				if (oldRotationValue > -2000 && oldRotationValue < 25)
				{
					transform.eulerAngles = new Vector3(0,yAngle,angle+angleOffset);
				}
			}
		}
	}
}

I normally use JS, so I’m not the best with C#.

I also could’ve sworn I had it so the wheel wasn’t snapping into a certain rotation when the mouse button was clicked, but now it is. I’m wondering if this has to do with the fact that I have the cursor locked, until a second script unlocks it when the player clicks on the wheel, and locks it again when they release it.

I think you need just a few changes, here’s my solution:

First define a float value that going to store the velocity of the lerp rotation.

public float rotationSpeed = 5f;

Then change the code of where you detect the mouse is being pressed down to this.

//This fires while the button is pressed down
    if (Input.GetMouseButton(0))
    {
        Vector3 v3 = Input.mousePosition - screenPos;
        float angle = Mathf.Atan2(v3.y, v3.x) * Mathf.Rad2Deg;

        newRotationValue = angle+angleOffset;
        while (newRotationValue < oldRotationValue - 180f) newRotationValue += 360f;
        while (newRotationValue > oldRotationValue + 180f) newRotationValue -= 360f;
        //oldRotationValue = newRotationValue; //This is making oldRotation update even when the new rotation is not being applied.

        if (newRotationValue > -2000 && newRotationValue < 25) //Checks if newRotation is valid
        {
            //Goes from the currentAngle to the target Angle with velocity = rotationSpeed
            //Using Time.delta to make it independable from frame rate
            transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, new Vector3(0,yAngle,angle+angleOffset), rotationSpeed * Time.deltaTime);

            //After updating make the oldRotation the newRotation so in the next frame represents the last valid rotation made.
            oldRotationValue = newRotationValue;
        }
    }

I haven’t tested it because I can’t reproduce the setup, but I think this will work.

the simplest answer would be to Animating it.

If I’m understanding what you’re saying, @TheGoliath, you do the following and get the following behavior:

  1. Click on the wheel with the mouse.
  2. Drag the mouse either 25 degrees one way, or constantly around to -2000 degrees the other way.
  3. The wheel hits the limit and stops physically rotating.
  4. You’re still able to move the mouse around and the rotation value you’re storing still increases.

Is that correct?

If that is the case, then I recommend changing only a small snippet of your code. I believe I’m reading your code right when I say that you

“check if newRotationValue is trying to wrap around the 0-360 range and correct this. Then see if the rotation is between -2000 and 25. If it is, apply the rotation to the wheel.”

In that case, I recommend moving the code around a bit, but I can’t visualize how this would affect your math except to hopefully fix your current issue.

             newRotationValue = angle+angleOffset;

             if (oldRotationValue > -2000 && oldRotationValue < 25)
             {
                // I changed these from while to an if-else if, but I don't know your math,
                // so this could need to be changed back. Just looked to me that the while
                // statement would keep on going until it goes on, but that could be the
                // wrap-prevention...

                if (newRotationValue < (oldRotationValue - 180f))
                { newRotationValue += 360f; }
                else if (newRotationValue > (oldRotationValue + 180f))
                { newRotationValue -= 360f; }

                 oldRotationValue = newRotationValue;
                 transform.eulerAngles = new Vector3(0,yAngle,angle+angleOffset);
             }

I has a project about magicwheel on git, all of you can try it, and if you like, develop it with me :smiley:
git clone Bitbucket