How to rotate the navmesh agent before start moving?

Hello everyone!

I want to rotate the front side of my navmesh agent to the path before starting a movement. I stuck on the calculation of angle between transform.forward and _navMeshAgent.steeringTarget.

This is my code:

using UnityEngine;
using UnityEngine.AI;
using System.Collections;

[DisallowMultipleComponent]
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(CapsuleCollider))]
public class AnimationController : MonoBehaviour
{
    private Animator _animator;
    private Rigidbody _rigidbody;
    private NavMeshAgent _agent;
    private static readonly int Forward = Animator.StringToHash("Forward");
    private static readonly int Turn = Animator.StringToHash("Turn");

    [SerializeField] private float _forwardDumpTime = 0.3f;
    [SerializeField] private float _turnDumpTime = 0.1f;
    [SerializeField] private float _movingTurnSpeed = 360f;
    [SerializeField] private float _stationaryTurnSpeed = 180f;

    private bool _isRotating;
    private List<Vector3> _path;

    private void Awake()
    {
        _animator = GetComponent<Animator>();
        _agent = GetComponent<NavMeshAgent>();
        _agent.updateRotation = false;
        _rigidbody = GetComponent<Rigidbody>();
        _rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY |
                                 RigidbodyConstraints.FreezeRotationZ;
    }

    private void Update()
    {
        if (!_isRotating)
        {
            UpdateAnimator(_agent.remainingDistance > _agent.stoppingDistance ? _agent.desiredVelocity : Vector3.zero);
        }
    }

    public void MoveTo(Vector3 target)
    {
        _agent.SetDestination(target);
        StartCoroutine(RotateToNextPosition());
    }

    private IEnumerator RotateToNextPosition()
    {
        _agent.isStopped = true;
        _isRotating = true;
        while(rotateAngle > 0.1f)
        {
            _animator.SetFloat(Turn, rotateAngle);
            yield return null;
        }
        _isRotating = false;
        _agent.isStopped = false;
    }

    private float rotateAngle => Vector3.Angle(transform.forward, _agent.steeringTarget) / 180f;

    private void UpdateAnimator(Vector3 velocity)
    {
        float turnAmount = 0f;
        float forwardAmount = 0f;

        if (velocity != Vector3.zero)
        {
            if (velocity.magnitude > 1f)
            {
                velocity.Normalize();
            }

            velocity = transform.InverseTransformDirection(velocity);

            turnAmount = Mathf.Atan2(velocity.x, velocity.z);
            forwardAmount = velocity.z;
            float turnSpeed = Mathf.Lerp(_stationaryTurnSpeed, _movingTurnSpeed, forwardAmount);

            transform.Rotate(0, turnAmount * turnSpeed * Time.deltaTime, 0f);
        }

        _animator.SetFloat(Forward, forwardAmount, _forwardDumpTime, Time.deltaTime);
        _animator.SetFloat(Turn, turnAmount, _turnDumpTime, Time.deltaTime);
    }

    private void OnDrawGizmos()
        {
            if (_path != null && _navMeshAgent)
            {
                Gizmos.color = Color.blue;
                for (var i = 0; i < _path.Count - 1; i++)
                {
                    Gizmos.DrawLine(_path[i], _path[i + 1]);
                }
                Gizmos.color = Color.green;
                Gizmos.DrawWireSphere(_navMeshAgent.steeringTarget, 0.2f);
            }
            Gizmos.color = Color.red;
            Gizmos.DrawLine(transform.position, transform.position + transform.forward);
        }
}

The calculated angle value is very strange and I don’t know why. On the picture below I have drawn a red line for transform.forward, a blue line for path and a green wired sphere for _navMeshAgent.steeringTarget. The angle between transform.forward and _navMeshAgent.steeringTarget is 23 degrees without adjusting the value (angle / 180 => value between 0 and 1 ).



What am I doing wrong? How can I properly calculate the angle and rotate the agent to the path?

Thank you in advance and sorry for my English :slight_smile:

FUUUU!!!

I don’t know why, but code below is working :smiley:

public void MoveTo(Vector3 target)
{
    _agent.SetDestination(target);
    StartCoroutine(RotateToNextPosition());
}

private IEnumerator RotateToNextPosition()
{
    _agent.isStopped = true;
    yield return null;
    print(rotateAngle);
    _agent.isStopped = false;
}

private float rotateAngle
{
    get
    {
        Vector3 direction = _agent.steeringTarget - transform.position;
        return Vector3.SignedAngle(transform.forward, direction, Vector3.up) / 180f;
    }
}

Only need to do is turn the agent to the desired angle through the animation.

I’ve got the solution, but it’s like an ugly hackaround. For correct calculation of the angle needs to skip at least 1 frame after stopping the agent.

private void Update()
{
    if (!_isRotating)
    {
        UpdateAnimator(_agent.remainingDistance > _agent.stoppingDistance ? _agent.desiredVelocity : Vector3.zero);
    }
}

public void MoveTo(Vector3 target)
{
    _agent.SetDestination(target);
    StartCoroutine(RotateToNextPosition());
}

private IEnumerator RotateToNextPosition()
{
    _agent.isStopped = true;
    _isRotating = true;
    _rigidbody.detectCollisions = false;
    yield return null;
    _animator.SetFloat(Turn, rotateAngle);
    while (Mathf.Abs(rotateAngle) > 0.1f)
    {
        yield return null;
    }
    _animator.SetFloat(Turn, 0f);
    _rigidbody.detectCollisions = true;
    _isRotating = false;
    _agent.isStopped = false;
}

private float rotateAngle
{
    get
    {
        Vector3 direction = _agent.steeringTarget - transform.position;
        return Vector3.SignedAngle(transform.forward, direction, transform.up) / 180f;
    }
}

If anyone knows how to make this code better, please let me know.

2 Likes