Rotation at constant speed using LookRotation

Hello
I am attempting to rotate a “turret-like” object towards the mouse location
and i want to have control at the speed of the rotation and also the responsiveness

im using Lerp as it seems to be the kind of movement i want, a constant, linear rotation
it works fine, except that it seems to slow down its rotation to a crawl as it nears the target (point to my mouse position)

i would like to have it accelerate and decelerate at a linear pace before achieving its maximum rotation speed
hope it makes sense
here is my script

using UnityEngine;
using System.Collections;

public class TurretController : MonoBehaviour
{

    private float rotationSpeed = 0.5f;

    // Use this for initialization
    void Start ()
    {

    }
   
    // Update is called once per frame
    void Update ()
    {
        // Rotate to face mouse position
        var mousePosition = Camera.main.ScreenToWorldPoint (Input.mousePosition);
        Quaternion rotation = Quaternion.LookRotation (transform.position - mousePosition, Vector3.forward);

        transform.rotation = Quaternion.Lerp(transform.rotation, rotation ,Time.deltaTime * rotationSpeed); 
        transform.eulerAngles = new Vector3 (0, 0, transform.eulerAngles.z);
    }
}

The reason why your linear interpolation, which should as the name very well suggests be linear, slows down at the end instead, is because you are not using it correctly. The first two parameters should be the same through the entire interpolation, whereas you are giving it a different value every frame. The last parameter should be a value between 0 and 1, which defines how far along the interpolation you currently are - i.e. 0 is the starting position, 0.5 is half way, and 1 is at the final position.

That being said, for your purposes, lerp is not the way to go. Lerp is useful if you have a fixed interpolation for a fixed amount of time, i.e. if you clicked somewhere and the turret rotated towards the clicked position. However, you desire continuous rotation. You should use Quaternion.RotateTowards() instead, as follows:

transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);

This will continuously rotate the turret at the same speed, until it reaches the target angle. When you move the cursor, the turret will rotate towards it again. This won’t have your turret speed up and slow down yet, though. For that, you need to control your rotational speed. We can use a similar function to RotateTowards to achieve this effect with very little effort: Mathf.MoveTowards().

public class Turret : MonoBehaviour
{
    public float MaxAngularVelocity = 50;
    public float AngularAcceleration = 15;
    [Tooltip("The minimum angle below which the turret will slow down.")]
    public float MinAngleForMaxSpeed = 15;

    private float _rotationalSpeed;

    // Update is called once per frame
    void Update()
    {
        // Get the mouse world position in front of the turret (you'll need to change this to your needs, i.e. using a raycast if need be, I don't know what your setup looks like).
        Vector3 mouseWorld = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, Vector3.Distance(transform.position, Camera.main.transform.position) + 1));
        // Flatten out the position relative to the turret.
        mouseWorld.y = transform.position.y;
        // Calculate the target rotation.
        Quaternion targetRotation = Quaternion.LookRotation(mouseWorld - transform.position);
        // Rotate the turret towards the target.
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, Time.deltaTime * _rotationalSpeed);

        // Calculate the remaining amount of degrees toward the target angle.
        float angleToTargetRotation = Quaternion.Angle(transform.rotation, targetRotation);
       
        // Calculate the max angular velocity we can have, based on how close we are to the target angle.
        float targetMaxSpeed = Mathf.Lerp(0, MaxAngularVelocity, Mathf.Clamp01((angleToTargetRotation - MinAngleForMaxSpeed) / MinAngleForMaxSpeed));
        // Decelerate.
        if (targetMaxSpeed < _rotationalSpeed)
        {
            _rotationalSpeed = targetMaxSpeed;
        }
        // Or accelerate.
        else
        {
            _rotationalSpeed = Mathf.MoveTowards(_rotationalSpeed, targetMaxSpeed, Time.deltaTime * AngularAcceleration);
        }
    }
}
1 Like

Hi Thomas

I didnt mention that my project is set up as a 2D top-down perspective, where the z-axis is my depth.
i hope thats why your code is confusing me :), i tried to wrap my head around it, but i figured i should first make sure we are on the same page here.
also, i’m a beginner, so its very likely that this is the real issue, but i try to make sure i somewhat understand things before i move on.
thanks