Making the player face the direction of the cursor

Hello,
I’m trying the make the player (an object in 3D space) face the mouse cursor, responding only in the XZ-Plane.
However, the object consistently rotates at a very high speed and doesn’t follow the cursor.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Camera cam;

    void Update()
    {
        // Transforms the object's (player) position to a 2D-screen position
        Vector2 playerPos = cam.WorldToViewportPoint(transform.position);
        // Gets the position of the mouse
        Vector2 mousePos = Input.mousePosition;
        // Creates a vector going from the position of the player to the position of the mouse (in 2D space), and normalizes it
        Vector2 vector = (mousePos - playerPos).normalized;
        // Gets the direction which the player is facing, converts it to 2D
        Vector2 playerFacing = cam.WorldToViewportPoint(transform.forward);

        playerPos.Normalize();
        // Using scalar multiplication in order to find the angle between 'playerFacing' and 'vector'
        float angle = Mathf.Acos((vector.x * playerFacing.x * + vector.y * playerFacing.y))*180/Mathf.PI;

        // Rotation the player 'angle' degress
        transform.Rotate(0, angle, 0);
    }

Why wouldn’t it work?

I’ve also tried another method, creating a vector going through the camera and the mouse position (converted to a world point), then finding the point of intersection between the straight line going through these points and the XZ-Plane and then telling the object to rotate to that point. Thought it would work since the point of intersection is ‘hidden’ by the cursor, thus making it seem like the object is facing the cursor itself.
The result is better than the code written above, yet not good enough.

Thank you!

Transform.Rotate adds a rotation to the current rotation of the transform. Have you looked into transform.LookAt()?

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
transform.LookAt(ray.origin);

Edit: reading again I see “responding only in the XZ-Plane” - you could just use something like this to get rotation in Z (untested)…

float rz = Mathf.Rad2Deg * ((Mathf.Atan2(at.x - from.x, at.y - from.y)));
transform.rotation = Quaternion.Euler(0, 0, rz);

First of all, thank you both!
I’ve decided to use the transform.LookAt() method and implemented it in the following code, and it’s actually working!

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Camera cam;

    void Update()
    {
        // Converting the mouse position to a point in 3D-space
        Vector3 point = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 1));
        // Using some math to calculate the point of intersection between the line going through the camera and the mouse position with the XZ-Plane
        float t = cam.transform.position.y / (cam.transform.position.y - point.y);
        Vector3 finalPoint = new Vector3(t * (point.x - cam.transform.position.x) + cam.transform.position.x, 1, t * (point.z - cam.transform.position.z) + cam.transform.position.z);
        //Rotating the object to that point
        transform.LookAt(finalPoint, Vector3.up);
    }
6 Likes

Just wanted to give a shout out that the above code worked exactly for what I was looking to do as well. Thanks!

I’m doing mine a bit differently potentially can be helpful to some:

public static Vector3 GetAimPos()
{
Physics.Raycast(
Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit);
return hit.point;
}

caster.MyTransform.LookAt(GetAimPos());