Relative rotation offset?

This script rotates a rigidbody’s transform.up to look at the mouse position on click, but the problem is I want it to only rotate relative to the current rotation when dragging the mouse, instead of rotating the transform.up to aim directly at it. How can I do this?

using UnityEngine;
using System.Collections;

public class RotateTowards : MonoBehaviour 
{
	public float smoothing = 0.3f;
	
	private Camera mainCamera;
	private Vector3 targetDir;
	
	void Awake () 
	{
		targetDir = Vector3.zero;
		mainCamera = Camera.main;
	}
	
	void Update () 
	{
		Ray ray = mainCamera.ScreenPointToRay (Input.mousePosition);
		Vector3 mousePosition = ray.origin + (ray.direction * 10);
		targetDir = (mousePosition - transform.position);		
	}
	
	void FixedUpdate () 
	{
		if (Input.GetMouseButton(0))
		{
			if (targetDir == Vector3.zero) 
			{
				rigidbody.angularVelocity = Vector3.zero;
			}
			else
			{
				float angularSpeed = AngleAroundAxis (transform.up, targetDir, Vector3.forward);
				rigidbody.angularVelocity = (Vector3.forward * angularSpeed * smoothing);
			}
		}
	}
	
	static float AngleAroundAxis (Vector3 dirA, Vector3 dirB, Vector3 axis) 
	{
		dirA = dirA - Vector3.Project (dirA, axis);
		dirB = dirB - Vector3.Project (dirB, axis);
		float angle = Vector3.Angle (dirA, dirB);
		return angle * (Vector3.Dot (axis, Vector3.Cross (dirA, dirB)) < 0 ? -1 : 1);
	}

}

I think this is what you want. At the top of the file:

private Vector3 downPos;

Add to FixedUpdate():

if (Input.GetMouseButtonDown(0)) {
    downPos = Input.mousePosition;
}

Then modify line 26:

  if (Input.GetMouseButton(0) && Vector3.Distance(downPos, Input.mousePosition) > threshold) {

‘threshold’ is some small value like 3. And if you are targeting multiple platforms, then you may want to calculate ‘threshold’ based on Screen.dpi.

The idea here is that the mouse has to be some small distance from where it was first clicked before it starts tracking. There is one hole in this logic that I don’t think matters in practice. If the user drags away form ‘downPos’ and then drags back to ‘downPos’, it will stop trying to rotate the object while the position is very near ‘downPos’. If this becomes a problem, it is easy to fix.

Here’s how I solved my problem thanks to Robertbu’s example code.

I could only get it to work correctly by placing most of the code in Update() and just using FixedUpdate() to apply the velocity changes. This seems to work perfectly, but please let me know if you think there’s a better way.

using UnityEngine;
using System.Collections;

public class RotateTowards : MonoBehaviour
{
	public float smoothing = 0.3f;
	
	private Camera mainCamera;
	private Vector3 targetDir;
	private Quaternion offset;
	private float angularSpeed;

	void Awake ()
	{
		mainCamera = Camera.main;
	}

	void Update ()
	{
		if (Input.GetMouseButtonDown(0)) 
		{
			rigidbody.angularVelocity = Vector3.zero;
			targetDir = MouseDirection();
			offset = Quaternion.FromToRotation (transform.up, targetDir);
		}

		if (Input.GetMouseButton(0))
		{
			targetDir = MouseDirection();
			angularSpeed = AngleAroundAxis (offset * transform.up, targetDir, Vector3.forward);
		}
	}

	void FixedUpdate ()
	{
		if (Input.GetMouseButton(0))
			rigidbody.angularVelocity = (Vector3.forward * angularSpeed * smoothing);
	}
	
	static float AngleAroundAxis (Vector3 dirA, Vector3 dirB, Vector3 axis)
	{
		dirA = dirA - Vector3.Project (dirA, axis);
		dirB = dirB - Vector3.Project (dirB, axis);
		float angle = Vector3.Angle (dirA, dirB);
		return angle * (Vector3.Dot (axis, Vector3.Cross (dirA, dirB)) < 0 ? -1 : 1);
	}

	Vector3 MouseDirection ()
	{
		Vector3 mousePos = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, 10.0f);
		Vector3 mouseWorldPos = mainCamera.ScreenToWorldPoint (mousePos);
		Vector3 dir = mouseWorldPos - transform.position;
		return dir;
	}
}