hjk321
hjk321

Reputation: 437

How to make a Game Object point towards the mouse in Unity? (C#)

I am creating a game involving a turret and it needs to "point" (that is, rotate) to the mouse. It's in 3-D environment, but at a bird's eye view. So for my purposes we are in a 2-D environment.

Here is my code:

using UnityEngine;
using System.Collections;

public class Turret : MonoBehaviour {

// Use this for initialization
void Start () {

}

int speed;  float friction;  float lerpSpeed ; private float xDeg ; 
private float yDeg; private Quaternion fromRotation; private Quaternion toRotation;

void Update () { 
    xDeg -= Input.GetAxis ("Mouse X"); yDeg += Input.GetAxis ("Mouse Y"); 
    fromRotation = transform.rotation; 
    toRotation = Quaternion.Euler(yDeg,xDeg,0); 
    transform.rotation = Quaternion.Lerp(fromRotation,toRotation,Time.deltaTime * lerpSpeed); 
}
}

If you could tell me what I'm doing wrong or give me the correct code that would be great! Please note that I am using a C# script.

Upvotes: 1

Views: 8400

Answers (2)

Can Baycay
Can Baycay

Reputation: 875

Input and rotation calculations are not right.

xDeg -= Input.GetAxis ("Mouse X"); yDeg += Input.GetAxis ("Mouse Y"); 
toRotation = Quaternion.Euler(yDeg,xDeg,0); 

You are making a top down game. So I assume that you are trying to aim at where mouse points on a 2D plane, which is ground. You should get your input not based on mouse axes but taking account where your mouse cursor is.

That being said, you can use this method to achieve your goal:

public class CharacterInput : MonoBehaviour
{
    public Transform CharacterTransform;

    void Update()
    {
        var groundPlane = new Plane(Vector3.up, -CharacterTransform.position.y);
        var mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        float hitDistance;

        if (groundPlane.Raycast(mouseRay, out hitDistance))
        {
            var lookAtPosition = mouseRay.GetPoint(hitDistance);
            CharacterTransform.LookAt(lookAtPosition, Vector3.up);
        }
    }
}

And to rotate it smoothly:

public class CharacterInput : MonoBehaviour
{
    public Transform CharacterTransform;
    public float RotationSmoothingCoef = 0.1f;

    void Update()
    {
        var groundPlane = new Plane(Vector3.up, -CharacterTransform.position.y);
        var mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        float hitDistance;

        if (groundPlane.Raycast(mouseRay, out hitDistance))
        {
            var lookAtPosition = mouseRay.GetPoint(hitDistance);
            var targetRotation = Quaternion.LookRotation(lookAtPosition - CharacterTransform.position, Vector3.up);
            var rotation = Quaternion.Lerp(CharacterTransform.rotation, targetRotation, RotationSmoothingCoef);
            CharacterTransform.rotation = rotation;
        }
    }
}

Better calculate smoothing in FixedUpdate to make it independent of frames per second. So it rotates at the same speed on every computer configuration:

public class CharacterInput : MonoBehaviour
{
    public Transform CharacterTransform;
    public float RotationSmoothingCoef = 0.01f;

    private Quaternion targetRotation;

    void Update()
    {
        var groundPlane = new Plane(Vector3.up, -CharacterTransform.position.y);
        var mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        float hitDistance;

        if (groundPlane.Raycast(mouseRay, out hitDistance))
        {
            var lookAtPosition = mouseRay.GetPoint(hitDistance);
            targetRotation = Quaternion.LookRotation(lookAtPosition - CharacterTransform.position, Vector3.up);
        }
    }

    void FixedUpdate()
    {
        var rotation = Quaternion.Lerp(CharacterTransform.rotation, targetRotation, RotationSmoothingCoef);
        CharacterTransform.rotation = rotation;
    }
}

Upvotes: 1

ZivS
ZivS

Reputation: 2124

I think this is a common mistake for a Unity beginner (as I had it wrong the first time as well).

As you probably know by now, the Update() method is called every new frame. So, every new frame (in your code) you calculate where the mouse is, how to rotate and call Lerp.

What you probably miss is how Lerp works and that is interpolating the motion by making one step every frame , i.e every time Lerp is called it rotates (in your case) by some interval. Your interval is Time.deltaTime * lerpSpeed which changes every frame since Time.deltaTime is the time between 2 consecutive frames.

So to make Lerp work properly (== smooth interpolation) you must call it with the same start and end position and complete the interpolation between them (call Lerp from 0 to 1 with as many intervals as you wish).

What I suggest you do is move this code:

xDeg -= Input.GetAxis ("Mouse X"); yDeg += Input.GetAxis ("Mouse Y"); 
fromRotation = transform.rotation; 
toRotation = Quaternion.Euler(yDeg,xDeg,0); 

to a different place (one which Update has access to these variables) and:

  1. Set toRotation everytime it should move (xDeg can be computed along side)
  2. Remove fromRotation and instead call Lerp like this: Lerp(transform.rotation, toRotation,Time.deltaTime * lerpSpeed)

Upvotes: 0

Related Questions