using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] public class AnimationController : MonoBehaviour { private Animator animator; private NavMeshAgent agent; [SerializeField] private Transform targetDestination; [Header("Animation Settings:")] [SerializeField] private bool rootMotion = true; [SerializeField] private float movingTurnSpeed = 360f; [SerializeField] private float stationaryTurnSpeed = 120f; [Header("Idle Rotation Settings:")] [SerializeField] private float rotationTriggerThreshold = 45f; [SerializeField] private float turnSmoothingSpeed = 0.1f; private float turnAngle; private Vector2 velocity; private Vector2 smoothDeltaPosition; private void Awake() { animator = GetComponent<Animator>(); agent = GetComponent<NavMeshAgent>(); animator.applyRootMotion = rootMotion; agent.updatePosition = false; agent.updateRotation = false; } private void OnAnimatorMove() { Vector3 rootPosition = animator.rootPosition; rootPosition.y = agent.nextPosition.y; transform.position = rootPosition; agent.nextPosition = rootPosition; } void Update() { //agent.destination = targetDestination.position; SynchronizeAnimatorAndAgent(); } private void SynchronizeAnimatorAndAgent() { Vector3 worldDeltaPosition = agent.nextPosition - transform.position; worldDeltaPosition.y = 0; // Calculate velocity float dx = Vector3.Dot(transform.right, worldDeltaPosition); float dy = Vector3.Dot(transform.forward, worldDeltaPosition); Vector2 deltaPosition = new Vector2(dx, dy); float smooth = Mathf.Min(1, Time.deltaTime / turnSmoothingSpeed); smoothDeltaPosition = Vector2.Lerp(smoothDeltaPosition, deltaPosition, smooth); velocity = smoothDeltaPosition / Time.deltaTime; // Handle stopping if (agent.remainingDistance <= agent.stoppingDistance) { velocity = Vector2.Lerp(Vector2.zero, velocity, agent.remainingDistance / agent.stoppingDistance); } bool shouldMove = velocity.magnitude > 0.2f && agent.remainingDistance > 0.1f; if(!shouldMove) velocity = Vector2.zero; animator.SetBool("move", shouldMove); velocity = velocity.normalized; animator.SetFloat("velocity", velocity.magnitude); // Handle position sync if (worldDeltaPosition.magnitude > agent.radius / 2f) { transform.position = Vector3.Lerp(animator.rootPosition, agent.nextPosition, smooth); } // ---------- HANDLE ROTATION ----------- Vector3 toTarget = agent.steeringTarget - transform.position; toTarget.y = 0; float angle = 0f; if (toTarget.sqrMagnitude > 0.01f) { angle = Vector3.SignedAngle(transform.forward, toTarget.normalized, Vector3.up); } // Smooth turn angle (used for animation blend) turnAngle = Mathf.Lerp(turnAngle, angle, smooth); float normalizedTurnAngle = turnAngle / rotationTriggerThreshold; normalizedTurnAngle = Mathf.Clamp(normalizedTurnAngle, -1f, 1f); animator.SetFloat("turnAngle", turnAngle); // ACTUALLY ROTATE THE AGENT (for nav mesh and visual sync) if (shouldMove) { float rotationSpeed = Mathf.Lerp(stationaryTurnSpeed, movingTurnSpeed, velocity.magnitude); Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up); transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); } } }