Rotate Object with mouse in RTS style game

Hello everyone I wanted to try to make a RTS style game, but I have stuck at the problem for many days. I want to rotate the building using a mouse, eg : when you are in build mode you can place object at the current mouse position, when the player press SHIFT key and move the mouse horizontal then object should rotate according to it. You many be thinking its easy just check if SHIFT key is pressed or not and if it is then rotate it. Yes, you are correct I tried to store the object position when SHIFT is press and rotate accordingly at that position but when I let go of the SHIFT key the mouse position has change as you have rotate the object and it snaps the object position to the new position. Do you guys have any solution to this problem, like after you release SHIFT key mouse position snaps to the object position or some other alternative?

I also tried to create a virtual mouse but then I have to write a whole lot of code for that virtual mouse to interact with the UI elements, so I have left it for now.

For reference you can see Planet Coaster. There you can press down Z key and move mouse horizontal and object rotates and when you let go of the Z key mouse position doesn’t change.

Answer 1 “#LeaveCursorAlone

187516-ourh4tliuqw3gtfg9ahergoihaerg9iuhrg978uguohfopipew.gif
_

using UnityEngine;

public class LetsSpawnAndRotate : MonoBehaviour
{
	[SerializeField] GameObject _prefab = null, _prefabPreview = null;
    GameObject _instance = null;
    Vector3 _pos, _normal;
    Vector2 _click;
	void Update ()
	{
        var camera = Camera.main;
        var ray = camera.ScreenPointToRay( Input.mousePosition );

		if( Input.GetMouseButtonDown(0) )
        {
            if( Physics.Raycast(ray,out var hit) )
            {
                _pos = hit.point;
                _normal = hit.normal;
                _instance = GameObject.Instantiate(
                    _prefabPreview ,
                    _pos ,
                    Quaternion.AngleAxis( 90 , Quaternion.LookRotation(_normal)*Vector3.right ) * Quaternion.LookRotation(_normal)
                );
                _click = Input.mousePosition;
            }
        }

        if( _instance!=null )
        {
            Vector2 mouseDelta = (Vector2) Input.mousePosition - _click;
            if( Physics.Raycast(ray,out var hit) && (mouseDelta).sqrMagnitude>0 )
            {
                float yaw = -Mathf.Atan2( mouseDelta.y , mouseDelta.x );
                Quaternion rotBase = Quaternion.AngleAxis( 90 , Quaternion.LookRotation(_normal)*Vector3.right ) * Quaternion.LookRotation(_normal);
                Quaternion rotAux = Quaternion.AngleAxis( Mathf.Rad2Deg*yaw , _normal );
                _instance.transform.rotation = rotAux * rotBase;
            }
            if( Input.GetMouseButtonUp(0) )
            {
                GameObject.Instantiate( _prefab , _instance.transform.position , _instance.transform.rotation );
                Destroy( _instance );
                _instance = null;
            }
        }
	}
    void OnDrawGizmos ()
    {
        Gizmos.DrawRay( _pos , _normal );
    }
}

_

Answer 2 “Plane & simple”

link text

using UnityEngine;

public class LetsRotateAndSpawn : MonoBehaviour
{
	[SerializeField] GameObject _prefab = null, _prefabPreview = null;
	[SerializeField] Vector3 _planeOrigin = Vector3.zero , _planeNormal = Vector3.up;
	GameObject _instancePreview = null;
	Vector3 _cursorPos = Vector3.zero, _cursorDragPosStart = Vector3.zero;
	Quaternion _cursorRot = Quaternion.identity, _cursorDragRotStart = Quaternion.identity;
	Camera	_camera = null;
	void Start ()
	{
		_camera = Camera.main;
		_instancePreview = GameObject.Instantiate( _prefabPreview );
		_instancePreview.SetActive( false );
	}
	void Update ()
	{
		Plane plane = new Plane( inNormal:_planeNormal , inPoint:_planeOrigin );
		var ray = _camera.ScreenPointToRay( Input.mousePosition );
		if( plane.Raycast( ray , out var hitDist ) )
		{
			Vector3 hitPoint = ray.origin + ray.direction * hitDist;
			_cursorPos = hitPoint;

			// store rot pivot info
			if( Input.GetMouseButtonDown(1) )
			{
				_cursorDragPosStart = hitPoint;
				_cursorDragRotStart = _cursorRot;
			}
			
			// update rotation
			if( Input.GetMouseButton(1) )
			{
				Vector3 vec = _cursorPos - _cursorDragPosStart;
				float screenPxDist = Vector2.Distance( _camera.WorldToScreenPoint(_cursorDragPosStart) , _camera.WorldToScreenPoint(_cursorPos) );
				if( vec.sqrMagnitude>0 )
				_cursorRot = Quaternion.Slerp(
					_cursorDragRotStart ,
					Quaternion.LookRotation( vec , _planeNormal ) ,
					Mathf.SmoothStep( 0 , 1 , screenPxDist / ( Mathf.Min(Screen.width,Screen.height) * 0.05f ) )
				);
			}

			// update preview mesh
			if( !_instancePreview.activeSelf ) _instancePreview.SetActive( true );
			_instancePreview.transform.SetPositionAndRotation( hitPoint , _cursorRot );

			// spawn prefab
			if( Input.GetMouseButtonDown(0) )
				GameObject.Instantiate( _prefab , hitPoint , _cursorRot );
		}
		else _instancePreview.SetActive( false );
	}
	void OnDrawGizmos ()
	{
		if( _camera!=null && Input.GetMouseButton(1) )
		{
			Gizmos.color = Color.yellow;
			Vector3 offset = new Vector3{ z=_camera.nearClipPlane*2f };
			Gizmos.DrawLine(
				_camera.ScreenToWorldPoint( Vector3.Scale( _camera.WorldToScreenPoint(_cursorDragPosStart) , new Vector3{x=1,y=1} ) + offset ) ,
				_camera.ScreenToWorldPoint( Vector3.Scale( _camera.WorldToScreenPoint(_cursorPos) , new Vector3{x=1,y=1} ) + offset )
			);
		}
		Gizmos.matrix = Matrix4x4.TRS( _planeOrigin , Quaternion.AngleAxis(0,_planeNormal) , Vector3.one );
		Gizmos.DrawWireCube( Vector3.zero , new Vector3{ x=100 , z=100 } );
	}
}

Check this answer on how to set the cursor position to a specific location.
It’s Windows only though, you’d have to find a specific soludion for different OS