// GDnD wiki example // Demonstrates: the "hybrid" route: NavMesh.CalculatePath for the route, your own code for the movement. // No NavMeshAgent at all: the path's corners go to WaypointPathFollower, and a CharacterController // moves the body. Swap CalculatePath for your own grid A* and nothing else changes. // Related pages: [[unity-navmesh]], [[pathfinding-algorithms]], [[steering-behaviours]] // Requires: Unity 6, a baked NavMesh (AI Navigation package, NavMesh Surface component). using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(CharacterController))] public class CalculatePathMover : MonoBehaviour { [SerializeField] private Transform goal; [SerializeField] private float maxSpeed = 4f; [SerializeField] private float acceleration = 12f; [SerializeField] private float repathDistance = 1f; // re-plan when the goal moves this far [SerializeField] private bool smoothPath = true; private readonly WaypointPathFollower follower = new WaypointPathFollower(); private NavMeshPath path; // created in Awake, reused every re-plan private CharacterController body; private Vector3 plannedGoal = new Vector3(float.MaxValue, 0f, 0f); private Vector3 velocity; private void Awake() { body = GetComponent(); path = new NavMeshPath(); follower.MaxSpeed = maxSpeed; } private void Update() { if (goal != null && (goal.position - plannedGoal).sqrMagnitude > repathDistance * repathDistance) Replan(goal.position); Vector3 desired = follower.DesiredVelocity(transform.position); velocity = Vector3.MoveTowards(velocity, desired, acceleration * Time.deltaTime); body.Move((velocity + Physics.gravity) * Time.deltaTime); // crude gravity keeps it grounded if (velocity.sqrMagnitude > 0.01f) transform.rotation = Quaternion.LookRotation(velocity); } private void Replan(Vector3 target) { plannedGoal = target; // Synchronous: fine for one character re-planning occasionally; budget it for crowds. if (NavMesh.CalculatePath(transform.position, target, NavMesh.AllAreas, path) && path.status != NavMeshPathStatus.PathInvalid) { follower.SetPath(path.corners, smoothPath ? (a, b) => !NavMesh.Raycast(a, b, out _, NavMesh.AllAreas) : null); } } private void OnDrawGizmosSelected() { if (path == null) return; Gizmos.color = Color.cyan; for (int i = 1; i < path.corners.Length; i++) Gizmos.DrawLine(path.corners[i - 1], path.corners[i]); } }