Reputation: 437
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
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
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:
toRotation
everytime it should move (xDeg
can be computed along side)fromRotation
and instead call Lerp like this:
Lerp(transform.rotation, toRotation,Time.deltaTime * lerpSpeed)
Upvotes: 0