Results 1 to 5 of 5
  1. #1
    Join Date
    Jun 2012
    Posts
    10

    Rotate a 2D wheel toward the mouse (Unity)

    Not sure if this should go in this forum or Unity, but thought I would try here.

    So I've been playing with this for several weeks and I've tried several iterations of code and some have gotten me close, but not exactly where I want to be.

    Basically, I am trying to rotate a wheel, in 2D space, in a circle toward the direction of where the mouse is currently located at. I think the place where I am getting stuck is how to make the rotation actually occur correctly. As it is currently, when the mouse is pressed on the object I record the location of the click. Then as I drag the mouse around I record the location of the mouse.

    I've tried getting the angle and setting Euler, getting the angle and use RotateAround, using LookRotation, and FromToRotation. The closest I've come is with FromToRotation which I'll post below. LookRotation works correctly for the rotation, but it flips the object so it is parallel to the z-axis and I need it to stay perpendicular to the z-axis.

    I have been trying to get this to work for several weeks and I know this isn't impossible, I just can't figure out how to get it to work. Any help would be greatly appreciated!!!!!!! See below for examples.

    Here is what I am trying to accomplish in a simple diagram:

    Click image for larger version. 

Name:	rotate.png 
Views:	790 
Size:	10.9 KB 
ID:	73202

    Here is the FromToRotation code, that works pretty well, except that I have to drag in a really large out circle in order to get it to rotate correctly.
    Code:
    using UnityEngine;
    
     
    
    public class SpinWithMouse : MonoBehaviour
    
    {
    
        public Transform target;
    
        public float speed = 1f;
    
     
    
        Transform mTrans;
    
        Vector3 currentLoc;
    
        public float angle = 0f;
    
     
    
        void Start ()
    
        {
    
            //Set the transform for the script to the transform of the object the script is attached to
    
           mTrans = transform;
    
        }
    
     
    
        void OnPress()
    
        {
    
           //Set the x,y,z that is currently clicked or touched
    
           currentLoc = UICamera.currentTouch.pos;
    
        }
    
     
    
        void OnDrag (Vector2 delta)
    
        {
    
            //Set the x,y,z of where the mouse is currently at
    
           Vector3 newLoc = UICamera.currentTouch.pos;
    
     
    
            //Get the difference between the start angle and the stop angle
    
           //Quaternion targetRotation = Quaternion.LookRotation(newLoc - currentLoc);
    
     
    
            //Set the rotation of the object based on where the click occured and where the mouse is dragged i
    
            mTrans.localRotation =  Quaternion.FromToRotation(currentLoc,newLoc); //Quaternion.Euler(0f, 0f, -0.5f * delta.x * speed) * mTrans.localRotation;
    
     
    
           Debug.Log("Position " + UICamera.currentTouch.pos);
    
           Debug.Log("Wheel location " + mTrans.localRotation);
    
        }
    
    }
    Then this is LookRotation Code which gets the rotation perfectly, but it transforms the object so it's no longer laying flat and thus defeats what I am trying to accomplish.

    Code:
    using UnityEngine;
    
     
    
    public class SpinWithMouse : MonoBehaviour
    
    {
    
        public Transform target;
    
        public float speed = 1f;
    
     
    
        Transform mTrans;
    
        Vector3 currentLoc;
    
        Vector3 startTouch;
    
        public float angle = 0f;
    
     
    
        void Start ()
    
        {
    
            //Set the transform for the script to the transform of the object the script is attached to
    
            mTrans = transform;
    
        }
    
     
    
        void OnPress()
    
        {
    
            //Set the x,ythat is currently clicked or touched
    
            currentLoc = UICamera.currentTouch.pos;
    
            
    
            //Set the distance of the touch from the object
    
            startTouch = currentLoc - mTrans.localPosition;
    
        }
    
     
    
        void OnDrag (Vector2 delta)
    
        {
    
            //Set the x,y of where the mouse is currently at
    
            Vector3 newLoc = UICamera.currentTouch.pos;
    
            
    
            Vector3 currentTouch = newLoc - mTrans.localPosition;
    
     
    
            //Get the difference between the start angle and the stop angle
    
            //Quaternion targetRotation = Quaternion.LookRotation(newLoc - currentLoc);
    
     
    
            //Set the rotation of the object based on where the click occured and where the mouse is dragged i
    
            Quaternion newRotation = Quaternion.LookRotation(newLoc - currentLoc); //Quaternion.FromToRotation(startTouch,currentTouch); //Quaternion.Euler(0f, 0f, -0.5f * delta.x * speed) * mTrans.localRotation;
    
            
    
            mTrans.localRotation = newRotation;
    
     
    
            Debug.Log("Position " + UICamera.currentTouch.pos);
    
            Debug.Log("Wheel location " + mTrans.localRotation);
    
        }
    
    }
    This is my code trying to use angles, but it just rotates out of control with no seeming ryhm or reason.
    Code:
    using UnityEngine;
    
     
    
    public class SpinWithMouse : MonoBehaviour
    
    {
    
        public Transform target;
    
        public float speed = 1f;
    
     
    
        Transform mTrans;
    
        Vector3 currentLoc;
    
        Vector3 startTouch;
    
        public float angle = 0f;
    
     
    
        void Start ()
    
        {
    
            //Set the transform for the script to the transform of the object the script is attached to
    
            mTrans = transform;
    
        }
    
     
    
        void OnPress()
    
        {
    
            //Set the x,y that is first clicked or touched
    
            currentLoc = UICamera.currentTouch.pos;
    
            
    
            //Set the distance of the first touch from the object
    
            startTouch = currentLoc - mTrans.localPosition;
    
        }
    
     
    
        void OnDrag (Vector2 delta)
    
        {
    
            //Set the x,y of where the mouse or touch is currently at
    
            Vector3 newLoc = UICamera.currentTouch.pos;
    
            
    
            //Set the distance of the current touch from the object
    
            Vector3 currentTouch = newLoc - mTrans.localPosition;
    
     
    
            //Get the Angle between the starting location and the current location
    
            float angle = Vector3.Angle(newLoc, currentLoc);
    
            
    
            //Set the rotation of the object based on where the click occured and where the mouse is dragged i
    
            //Quaternion newRotation = Quaternion.LookRotation(newLoc - currentLoc); //Quaternion.FromToRotation(startTouch,currentTouch); //Quaternion.Euler(0f, 0f, -0.5f * delta.x * speed) * mTrans.localRotation;
    
            
    
            mTrans.RotateAround(transform.forward, angle);
    
     
    
            Debug.Log("Position " + UICamera.currentTouch.pos);
    
            Debug.Log("Wheel location " + mTrans.localRotation);
    
        }
    
    }
    If it helps here are how each of the sections of code actually run:
    Code1
    Code2
    Code3

  2. #2
    Join Date
    Aug 2007
    Location
    Montreal, Canada
    Posts
    618
    Here you go brother. Take a flat cylinder positioned at the origin that is perpendicular to the z-azis, call it GameWheel.
    The script below is called "RotateScript", attach it to the game object GameWheel.

    Position your camera somewhere on the negative z-axis (for example (0,0,-10)) to fit the cylinder in the screen.
    Set the camera to look in the direction of the positive z-axis, (which just means all the cameras rotation values are 0).



    Code:
    using UnityEngine;
    
    public class RotateScript : MonoBehaviour 
    {
    	public const float SPEED_DAMPER = 0.01f; 
    	
    	Vector2 previousMousePosition = Vector2.zero;
    	Vector2 currentMousePosition = Vector2.zero;
    	bool previousFrameMouseDown = false;
    	
    	
    	private void Update() 
    	{
    		Vector2 mousePos = new Vector2(Input.mousePosition.x - Screen.width/2, 
    			                           Input.mousePosition.y - Screen.height/2);
    		
    		if (Input.GetMouseButtonDown(0) && !previousFrameMouseDown)
    		{
    			previousMousePosition = mousePos;
    			currentMousePosition = mousePos;
    			previousFrameMouseDown = true;
    		}
    		else if (Input.GetMouseButton(0) && previousFrameMouseDown)
    		{
    			previousMousePosition = currentMousePosition;
    			currentMousePosition = mousePos;
    		}
    		else if (!Input.GetMouseButton(0))
    		{
    			previousFrameMouseDown = false;	
    		}
    			
    		if (previousMousePosition != -currentMousePosition && previousFrameMouseDown)
    		{
    			float rotationAmount = ReturnSignedAngleBetweenVectors(previousMousePosition, currentMousePosition);
    			transform.RotateAroundLocal(Vector3.forward, rotationAmount * SPEED_DAMPER);
    		}
    	}
    	
    	
    	private float ReturnSignedAngleBetweenVectors(Vector2 vectorA, Vector2 vectorB)
    	{
    		Vector3 vector3A = new Vector3(vectorA.x, vectorA.y, 0f);
    		Vector3 vector3B = new Vector3(vectorB.x, vectorB.y, 0f);
    		
    		if (vector3A == vector3B)
    			return 0f;
    		
    		// refVector is a 90cw rotation of vector3A
    		Vector3 refVector = Vector3.Cross(vector3A, Vector3.forward);
    		float dotProduct = Vector3.Dot(refVector, vector3B);
    		
    		if (dotProduct > 0)
    			return -Vector3.Angle(vector3A, vector3B);
    		else if (dotProduct < 0)
    			return Vector3.Angle(vector3A, vector3B);
    		else
    			throw new System.InvalidOperationException("the vectors are opposite, vectorA = -vectorB");
    	}
    	
    	
    	
    }
    Last edited by stealthcoder; 10-18-2012 at 02:31 AM.

  3. #3
    Join Date
    Jun 2012
    Posts
    10
    This is almost exactly what I am looking for except for one problem. The wheel can't stay centered in the screen. If I move the wheel to the -y then it stops this from working and my wheels will be in different x,y locations.

    Any thoughts?

  4. #4
    Join Date
    Aug 2007
    Location
    Montreal, Canada
    Posts
    618
    I was overlooking that there are two coordinate systems here, one the world space, and the other the screen space.
    Mouse position information is returned in screen space where as the position of our object is in world space.

    This code should work now for the cylinder at an arbitrary position in the x-y plane.

    Code:
    using UnityEngine;
    
    public class RotateScript : MonoBehaviour 
    {
    	Vector2 previousMousePosition = Vector2.zero;
    	Vector2 currentMousePosition = Vector2.zero;
    	bool previousFrameMouseDown = false;
    	
    	private void Update() 
    	{
    		Vector2 mousePos = new Vector2(Input.mousePosition.x, 
    			                           Input.mousePosition.y);
    		
    		if (Input.GetMouseButtonDown(0) && !previousFrameMouseDown)
    		{
    			previousMousePosition = mousePos;
    			currentMousePosition = mousePos;
    			previousFrameMouseDown = true;
    		}
    		else if (Input.GetMouseButton(0) && previousFrameMouseDown)
    		{
    			previousMousePosition = currentMousePosition;
    			currentMousePosition = mousePos;
    		}
    		else if (!Input.GetMouseButton(0))
    		{
    			previousFrameMouseDown = false;	
    		}
    			
    		Vector3 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
    		Vector2 screenPositionXY = new Vector2(screenPosition.x, screenPosition.y);
    		Vector2 previousPositionVector = previousMousePosition - screenPositionXY;
    		Vector2 currentPositionVector = currentMousePosition - screenPositionXY;
    		
    		if (previousPositionVector != -currentPositionVector && previousFrameMouseDown)
    		{
    			float rotationAmount = ReturnSignedAngleBetweenVectors(previousPositionVector,
    																   currentPositionVector);
    																
    			transform.RotateAroundLocal(Vector3.forward, rotationAmount * Time.deltaTime);
    		}
    	}
    	
    	
    	private float ReturnSignedAngleBetweenVectors(Vector2 vectorA, Vector2 vectorB)
    	{
    		Vector3 vector3A = new Vector3(vectorA.x, vectorA.y, 0f);
    		Vector3 vector3B = new Vector3(vectorB.x, vectorB.y, 0f);
    		
    		if (vector3A == vector3B)
    			return 0f;
    		
    		// refVector is a 90cw rotation of vector3A
    		Vector3 refVector = Vector3.Cross(vector3A, Vector3.forward);
    		float dotProduct = Vector3.Dot(refVector, vector3B);
    		
    		if (dotProduct > 0)
    			return -Vector3.Angle(vector3A, vector3B);
    		else if (dotProduct < 0)
    			return Vector3.Angle(vector3A, vector3B);
    		else
    			throw new System.InvalidOperationException("the vectors are opposite");
    	}
    	
    }

  5. #5
    Join Date
    Aug 2007
    Location
    Montreal, Canada
    Posts
    618
    Will you have multiple wheels?

    If so you will need some type of way of knowing which wheel will be rotated by your mouse drags.
    For example a distance check, outside of a certain radius from the wheel a mouse drag will not affect the wheel.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •