I have a situation where I want to rotate a NavMeshAgent as it navigates around my scene. I found a script that seems to do what I want, here is the main function/example I was interested in.
While this does work to rotate my object with a NavMeshAgnet, it rotates the object too fast, it’s not natural looking for what I’m doing. I tried adjusting the speed by modifying Time.deltaTime * .1f. I tried .1, .01, .0001 etc. None of the numbers I tried seem to have any affect. I tried to read the Slerp documentation, but there is not much information on this function.
Was hoping someone might shed some light on how I can slow this rotation down a bit. I’m wondering if the NavMeshAgent itself has some rotation speed built in that’s overriding how fast the Slerp function specifying.
OR, more likely, I just don’t know what I’m doing.
Basically it says that you move ‘t’ percent from a to b. So Passing in 0.5 gives you a value halfway from a to b.
In the case of Quaternions, this is a rotation half between a and b. (it’s also slerp, because the formula is spherical rather than linear… because rots are spherical inherently… but the overall concept is the same).
What you’re telling it to do when you pass in dt * 0.1, is to move 10% of 1/60th of the way from a to b (assuming 60fps).
Thing is as you do this every update tick, the distance from a to b is smaller. So this percentage of that distance is changing. 10% of 100 is 10, where as 10% of 10 is 1. So basically, over time a slerp will start out rushing towards the target, and then asymptotically approaching slowing towards the target.
You might want to try something like ‘RotateTowards’:
It gives you more control over the rate of speed as you rotate from a to b. Your code would be more like:
public float speed = 1f; //speed is in degrees per second
private void RotateTowards(Transform target)
{
Vector3 direction = (target.position - transform.position).normalized;
Quaternion lookRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * speed);
}
You’re using a sort of “alternative” usage of Slerp. The intended usage of Slerp (and all Lerp functions, which function basically the same) is that you send it the start and end, and then move the third parameter from 0 to 1 based on your time (or whatever). What you’re doing is to send it the current and the end, and your third parameter is a constant(ish) low number. While I don’t think that that usage is “wrong”, since it gives the movement a particular and possibly desired look and feel - snaps into movement and then eases into the final position, very good for camera movement - it’s definitely not the intended usage of Slerp, and it’s definitely harder to control than other techniques.
Quaternion actually has a RotateTowards function that might be more along the lines of what you’re looking for - it moves by a set number of degrees towards the desired rotation - more of a constant rotation speed.
I’m going to follow you around and tell you it’s wrong every time you say that
You are going to encounter problems when doing this, because of never reaching the final position and floating point inaccuracies, you can get wiggle and wobble for example.
The same effect can be achieved with RotateTowards, and including the remaining angle delta in the calculation of its maxDegreesDelta parameter.
Essentially, this results in a PD controller (similar to a PID controller but without an integral factor). Take a look at for example this link to see how it works.
Doing it correctly you get a much better result and much more flexibility.
Hi lordofduct.
I took your suggestion, changed my code to use Quaternion.RotateTowards…
I’m still getting the exact same situation. I’ve tried changing the speed using many different values such as 10, 1, .1, .01, .001. The rotation is always at the same speed.
For reference, here is my full script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
/// <summary>
/// To be used on a ship to simulate sailing through an ocean to a set of predefined locations.
/// </summary>
public class ShipNavigation : MonoBehaviour
{
// How fast to rotate the ship once it's gotten to a destination
public float _rotationSpeed = 1f;
// Contains a collection of NavPoint prefabs.. (points in the path to go)
public GameObject[] _navPoints;
// NavMeshAgent on the game object
private NavMeshAgent _navAgent;
// Index into the _navPoints array to pull the next NavPoint
private int _nextPoint = 0;
/// <summary>
/// Setup the navigation
/// </summary>
void Start( )
{
_navAgent = GetComponent<NavMeshAgent>( );
_navAgent.autoBraking = false;
GoToNextPoint( );
}
/// <summary>
/// Navigates the ship to the next destination stored in the _navPoints array
/// </summary>
void GoToNextPoint( )
{
if( _navPoints.Length == 0 )
return;
_navAgent.updateRotation = true;
_navAgent.destination = _navPoints[ _nextPoint ].transform.position;
_navAgent.updateRotation = true;
_nextPoint = NextNavPoint( _nextPoint );
// Rotate the transform to point at the next destination
RotateTowards( _navPoints[ _nextPoint ].transform );
}
/// <summary>
/// Check to see if we are at destination, if so, force new navigation location.
/// </summary>
void Update( )
{
if(!_navAgent.pathPending && _navAgent.remainingDistance < 0.5f )
GoToNextPoint( );
}
/// <summary>
/// Get the next index in the array. Circle back to the beginning if needed
/// </summary>
/// <param name="curNavPoint">Current index into the _navPoints array</param>
/// <returns></returns>
private int NextNavPoint( int curNavPoint )
{
if( curNavPoint == _navPoints.Length - 1)
curNavPoint = 0;
else
curNavPoint++;
return curNavPoint;
}
/// <summary>
/// Rotate the object the NavMeshAgent is attached to.
/// </summary>
/// <param name="target">X,Y,Z location to rotate to</param>
private void RotateTowards( Transform target )
{
Vector3 direction = ( target.position - transform.position ).normalized;
Quaternion lookRotation = Quaternion.LookRotation( direction );
transform.rotation = Quaternion.RotateTowards( transform.rotation, lookRotation, Time.deltaTime * _rotationSpeed );
}
/// <summary>
/// Debugging, utility function to just display the NavPoint transform positions.
/// </summary>
private void DisplayPointPosition( )
{
Transform t;
foreach( GameObject o in _navPoints )
{
t = o.transform;
Debug.Log( o.name + " --- X = " + t.position.x + ", Y = " + t.position.y + ", Z = " + t.position.z);
}
}
}
How are you changing the values? If you’re changing the values in the script on line 13, that won’t affect it after you first added the variable. Because _rotationSpeed is a public member, as soon as it’s compiled (and/or added to an object) for the first time, the Unity editor grabs the default value from the script, and then ignores it - from then on it will use its own stored value. After you add the variable, the way to change it is to edit it in the Inspector.
Right, I was not worried about changing the value at runtime in the inspector. I would stop and stop the scene each time I changed the value.
Ok so based on what you just said. I took out the initialization from the script. Now, the only place it gets set is from the value in the inspector.
Anyway, with it setup like this, i’m still seeing the rotation speed remain constant no matter what value I set in the inspector after I stop and restart the scene
I’ve done it in both places. The =1f; was just to give the inspector a default value. And adding this variable was a last minute change anyway… Previously I had just experimented with different values by hard coding a value directly into the function call.
The fun part of all this is even after I get THIS figured out… I have another problem.
I have a completely different script that handles simulated ship movement. As in, the ship bobs up and down in the water, the ship rocks back and forth as well as front to back.
I HAD the bobbing script attached to this, but it was preventing my rotation from happening so I disabled it. I’m thinking that I will need to stop the bobbing, rocking scripts when I get to a destination rotate, then turn the bobbing and rocking back on.