Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved 3rd Person Look at Mouse Script

Discussion in 'Scripting' started by BenevolenceGames, Mar 19, 2021.

  1. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    Ok, so I need a hand again. I've scoured YouTube videos, I've looked through the forums, and I can't find any help. I have found plenty of 3rd Person Character Controller and 3rd Person Camera Controller scripts, but what I'm looking for specifically is a script that will cause the rotation of my character to follow the mouse point on the Y axis. Similar to the 2D LookAtMouse scripts, however when I enter those scripts into my game the capsule primitive just lays on it's side and flaps like a flounder or it only tracks SOME of the mouse movement and inconsistently at that.

    So this is what I have attached to my Main Camera --
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class LookAtMouse : MonoBehaviour
    6. {
    7.     public GameObject crosshair;
    8.     public GameObject player;
    9.     private Vector3 target;
    10.  
    11.     void Start()
    12.     {
    13.        
    14.     }
    15.  
    16.     void Update()
    17.     {
    18.         target = transform.GetComponent<Camera>().ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z));
    19.  
    20.         Vector3 diff = target - player.transform.position;
    21.         float rotationY = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
    22.         player.transform.rotation = Quaternion.Euler(0.0f, rotationY, 0.0f);
    23.     }
    24.  
    25. }
    26.  
    --which results in a very inconsistent movement that doesn't at all resemble the rotation tracking the mouse position.

    And just a basic input script for x, z movement.

    The game is in the style of Solomons Keep, using 3D character models and a semi top down fixed camera angle. The objective is to have the wizard(player) face the mouse at all times and have independent world space movement so that spells may be cast in all directions independent of movement.
     
    joe171 likes this.
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,102
    +1 for bringing fish into the discussion... well done!

    If this is fundamentally a 2D-oriented 3D game, I suspect your flounder issue is because you are using your rotationY argument as the Y rotator, NOT as the Z rotator.

    Assuming your camera is "default" for 2D, then the camera is looking primarily +Z, so that is also the axis around which you want to rotate.

    Have you tried this for line 22:

    Code (csharp):
    1. player.transform.rotation = Quaternion.Euler(0.0f, , 0.0f, rotationY);
    If that's not right, it should be either 90 or -90 off.
     
    BenevolenceGames likes this.
  3. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    So, initially when I wrote the script, that instructor was using it for a 2D TRUE Top down game, so it was initially set up as--
    Code (CSharp):
    1.         float rotationZ = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
    2.         player.transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
    3.  
    4.  
    but that caused the floundering. The capsule immediately loads on it's side and spinning like a hotdog (this causes the "faceplate" child object [ a cube primitive] to flop around on the ground, hence the floundering ;) {that's 3 fish and a food for those following along}

    Also, unrelated, I have floating movement and I'm not sure where that's coming from...

    At any rate, that's where I'm stuck.

    And this game is running in perspective not orthographic, so it has a camera transform of (P:x0,y8.5,z-5.5 R:x80,y0,z0 S:x1,y1,z1)
    I'm not sure if that matters.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,102
    Okay, so basically nearly straight-down look onto the X/Z ground plane...gotcha.

    In that case, line 18, which produces the target from the mouse position, would seem to be the first wrong step in the chain. Here's why:

    That third argument to ScreenToWorldPoint() is actually the distance from the Camera position forward into the scene.

    That third argument is NOT your player position.z but rather whatever GameObject this script is on, so that might be one problem.

    But even beyond that, this fixed distance means that as you move the mouse around, with a perspective camera, your target is going to move on a sphere shell ahead of the camera.

    Try putting a cube and moving it around to target and you'll see.

    The question is, where do you want to map 2D mouse movement onto, the ground? If so, use a raycast at the ground, or else raycast to a plane. Remember there are three different ways to raycast:

    https://forum.unity.com/threads/kee...within-the-view-frustrum.936209/#post-6117173
     
    BenevolenceGames likes this.
  5. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    TS001.PNG

    So you see the Capsule serves as the player, I want that capsule to rotate to face wherever the mouse is at all times...
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,102
    Okay, we're close. You actually DO want the rotationY, but the input (the target) you want is wrong from the calculations on line 18, for the reasons in my post directly above this. You want to raycast at the ground, probably to a mathematical Plane object.
     
    BenevolenceGames likes this.
  7. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    Crap. I haven't even began to learn the raycast functions. I was thinking to begin with that's probably what I would need. I saw a video earlier about particle effects and the creation thereof, and he coded a simple turret to look at mouse using Raycast, I haven't been able to find it since though.

    --Edit--
    I just saw that you posted a link to a very informative reading on Raycasting. Thank you!
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,102
    So you actually want the mouse "target" thingy to be on the ground regardless where the mouse is on screen, right? That's super easy. Check it:

    Code (csharp):
    1.        // replace Vector3.zero with something else if your ground isn't AT Y == 0
    2.         Plane p =  new Plane( Vector3.up, Vector3.zero);
    3.  
    4. /// ray into the scene where the mouse is
    5.         var ray = transform.GetComponent<Camera>().ScreenPointToRay(Input.mousePosition);
    6.  
    7. // how far did it hit?
    8.         float enter = 0;
    9.  
    10. // cast
    11.         p.Raycast(ray, out enter);
    12.  
    13. // get!
    14.         Vector3 target = ray.GetPoint(enter);
    That should suffice as a replacement for line 18 above.

    And best of all, you should put a no-collider cube in the scene and set its position to target each frame so you KNOW that's where the mouse is, just for testing.
     
    BenevolenceGames likes this.
  9. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    So I'm placing all of these into the Update? Wouldn't that create a bajillion planes? Secondary question, could I assign the parameters associated with my current "Ground" plane GameObject with that "p" var? so that the plane it is checking against is my actual ground plane?
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,102
    This "Plane" is just a mathematical struct in memory.

    It has no relationship to PrimitiveType.Plane, which is something Unity can construct and see in scene.

    IF you want to hit the ground, then you probably want the first type of Raycast here:

    https://forum.unity.com/threads/kee...within-the-view-frustrum.936209/#post-6117173

    With layers you can select for only the ground. There's example code in the API as it is slightly different:

    - you use layers and layermasks to ONLY hit the ground
    - you use a RaycastHit object to get back from Raycast what you hit
    - inside RaycastHit would be a .point that you would use as your target.
     
    BenevolenceGames likes this.
  11. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    Actually, I used the script you recommended and it works!

    With a slight catch.

    TS002.PNG

    So I added the cube, removed its rigidbody, and assigned it to target within the LookAtMouse script. I added your script in place of line 18, utilizing the raycast method as opposed to a CameraToScreenPoint method.(I think I said that right?) Now, the capsule looks exactly 90 degress to the positive of the screen point target. I tried to implement a fix --

    ...
    public float viewOffset = -90.0f

    void Update()
    {
    ...
    float rotationY = MAthf.Atan2(diff.y, diff.x) * MathfRad2Deg - viewOffset;


    and it correctly rotates to the target. Unless the target is behind the capsule. How can I get it to correct the rotation by the offset, and still track movement behind the GameObject?

    --Edit--
    By "behind" I don't mean the space on the ground plane blocked by the character capsule, rather the space closest to the camera.
    TS003.PNG
     
  12. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133

    So I looked at a few other sources and found a way to draw the ray from point to point. So I drew a ray from the character to the "target" variable, and this is what I got-- Screenshot (2).png
    -- I seem to only have about 180 degrees of rotation, and the ray does not line up to the "target". If you zoom in, you'll see a small white dot on the game and scene view, that is the "Pointer" cube primitive that has been assigned to the "crosshair" slot on the LookAtMouse class.
     
  13. BenevolenceGames

    BenevolenceGames

    Joined:
    Feb 17, 2021
    Posts:
    133
    **UPDATE -- SOLVED**

    So with a little more research, and the help of Kurt-Dekker, I figured out a code that works. Here it is.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerController : MonoBehaviour
    6. {
    7.     // The variables that power the character/game interaction
    8.     public float speed = 5.0f;
    9.     private Rigidbody myRigidbody;
    10.  
    11.     private Vector3 moveInput;
    12.     private Vector3 moveVelocity;
    13.  
    14.     public bool isCasting;
    15.     public bool isPaused;
    16.  
    17.     private Camera mainCamera;
    18.  
    19.     // Start is called before the first frame update
    20.     void Start()
    21.     {
    22.         // Delcares the variable as this GameObject's Rigidbody component
    23.         myRigidbody = GetComponent<Rigidbody>();
    24.         // Declares the camera in scene as the variable
    25.         mainCamera = FindObjectOfType<Camera>();
    26.     }
    27.  
    28.     // Update is called once per frame
    29.     void Update()
    30.     {
    31.         moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
    32.         moveVelocity = moveInput * speed;
    33.  
    34.         Ray cameraRay = mainCamera.ScreenPointToRay(Input.mousePosition);
    35.         Plane ground = new Plane(Vector3.up, Vector3.zero);
    36.         float rayLength;
    37.  
    38.         if(ground.Raycast(cameraRay, out rayLength))
    39.         {
    40.             Vector3 pointToLook = cameraRay.GetPoint(rayLength);
    41.             Debug.DrawLine(cameraRay.origin, pointToLook, Color.blue);
    42.             transform.LookAt(new Vector3(pointToLook.x, transform.position.y, pointToLook.z));
    43.         }
    44.  
    45.         if (Input.GetKey(KeyCode.Mouse0) && !isPaused)
    46.         {
    47.             isCasting = true;
    48.         }
    49.         else
    50.         {
    51.             isCasting = false;
    52.         }
    53.     }
    54.  
    55.     void FixedUpdate()
    56.     {
    57.         myRigidbody.velocity = moveVelocity;
    58.     }
    59. }
     
    Zackrage, Kilroy4082 and Kurt-Dekker like this.
  14. Kilroy4082

    Kilroy4082

    Joined:
    May 26, 2020
    Posts:
    2
    Omg thank you so much you saved my day
     
    BenevolenceGames likes this.
  15. unity_X6YlIreR1MPAsw

    unity_X6YlIreR1MPAsw

    Joined:
    Nov 6, 2022
    Posts:
    1
    You are a hero!!!!!
     
    BenevolenceGames likes this.
  16. pointperks

    pointperks

    Joined:
    Sep 26, 2023
    Posts:
    1
    God Bless you man, this has been driving me nuts the past two days. I learned 2d rotation to follow the mouse fairly easy, but that doesn't apply to 3d. May life give you boundless riches for your efforts. Thank you.
     
    BenevolenceGames likes this.
  17. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,055
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CursorLook : MonoBehaviour
    4. {
    5.     void Update()
    6.     {
    7.         Vector3 direction=Input.mousePosition-new Vector3(Screen.width/2,Screen.height/2,0);
    8.         transform.LookAt(transform.position+new Vector3(direction.x,0,direction.y));
    9.     }
    10. }
     
  18. BaxterJvR

    BaxterJvR

    Joined:
    May 5, 2024
    Posts:
    1
    I'm struggling with my character wanting to rotate horizontally to where the player must look, it either snaps back to the center or doesn't move at all, here is my code:

    using UnityEngine;

    public class MouseLook : MonoBehaviour
    {
    public float mouseSensitivity = 100f; // Sensitivity of mouse movement

    float xRotation = 0f; // Current rotation around the x-axis

    public Transform playerBody; // Reference to the player's body

    void Start()
    {
    Cursor.lockState = CursorLockMode.Locked; // Lock cursor to center of screen
    }

    void Update()
    {
    // Get mouse input for rotation
    float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
    float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

    // Rotate the player horizontally (around the y-axis)
    playerBody.Rotate(Vector3.up * mouseX);

    // Calculate vertical (up/down) rotation
    xRotation -= mouseY;
    xRotation = Mathf.Clamp(xRotation, -90f, 90f); // Limit vertical rotation to prevent flipping

    // Rotate the camera vertically (around the x-axis)
    transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
    }
    }