Search code examples
c#unity-game-enginegame-physics

When I make an object face a mouse cursor in Unity it eventually offsets


I made a script that makes the player point towards the mouse cursor, but recently I discovered a bug. When I move the mouse cursor too much (An example being when I spin the mouse around the object in circles, causing the object to move around.), the object ends up pointing a bit off of where the mouse should be. As in, the cursor would signal the object to look at it, and the object ends up looking the slightest bit off, making it feel quite odd after maneuvering quickly. How can I make it so the object always faces the cursor, with no offsets, even when I move the cursor as much as possible.

            private void LateUpdate()
{
    Vector3 lookAtPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Vector3 mousePoint = new Vector3(lookAtPoint.x, lookAtPoint.y, 0);

    float angle = getAngle(transform.position, mousePoint);
    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, angle), 9f * Time.deltaTime);

    float getAngle(Vector3 currentLocation, Vector3 mouseLocation)
    {
        float x = mouseLocation.x - currentLocation.x;
        float y = mouseLocation.y - currentLocation.y;

        return angle = Mathf.Rad2Deg * Mathf.Atan2(y, x);

    }
}

Solution

  • Looks like it's down to the way that you are using Quaternion.Lerp(). In particular, the last parameter - it is meant to be a value between 0f and 1f which you supply, it does not auto-increment for you.

    So to fix this issue, what you want to do is save off a float somewhere. When you move the mouse (mouse position has changed since last frame) then start that value at 0f again. Then increment it at some value every frame until it is equal to or greater than 1f.

    Doing this will not only fix your bug. It will, depending on how fast your increment, give you a smooth rotation effect. Below is an example.

    internal class SomeClass : MonoBehaviour
    {
      private float lerpAmount = 0f;
      private Vector3 cachedMousePosition = Vector3.zero;
      
      private void LateUpdate()
      {
        var mousePosition
          = Camera.main.ScreenToWorldPoint(Input.mousePosition)
          * new Vector3(1, 1, 0);
        
        bool recalculateRotation = false;
        if (this.cachedMousePosition != mousePosition)
        {
          this.lerpAmount = 0f;
          recalculateRotation = true;
        }
    
        if (this.lerpAmount < 1f)
        {
          this.lerpAmount = Mathf.Min(this.lerpAmount + Time.deltaTime, 1f);
          recalculateRotation = true;
        }
    
        if (recalculateRotation)
        {
          float angle = getAngle(transform.position, mousePoint);
          transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, angle), this.lerpAmount);
        }
    
        float getAngle(Vector3 currentLocation, Vector3 mouseLocation)
        {
          float x = mouseLocation.x - currentLocation.x;
          float y = mouseLocation.y - currentLocation.y;
    
          return angle = Mathf.Rad2Deg * Mathf.Atan2(y, x);
    
        }
      }