Recherche de chemin est le processus de déplacement d'un personnage ou d'un objet (agent) le long d'un chemin logique autour des obstacles pour atteindre une destination, en évitant éventuellement des matériaux dangereux ou des régions définies.
Visualisation de la navigation
Pour aider à la mise en page et au débogage de la recherche de chemin, Studio peut afficher un maillage de navigation et des étiquettes de modificateur. Pour les activer, activez Maillage de navigation et Modificateurs de recherche de chemin depuis le widget Options de visualisation dans le coin supérieur droit de la vue 3D.

Avec Maillage de navigation activé, les zones colorées montrent où un personnage peut marcher ou nager. De petites flèches indiquent les zones qu'un personnage tentera d'atteindre en sautant.

Mise en œuvre
Bien que la recherche de chemin puisse être mise en œuvre de diverses manières via PathfindingService et ses méthodes associées telles que CreatePath(), cette section utilise le script de recherche de chemin suivant pour le personnage du joueur.
Pour tester tout en lisant :
- IMPORTANTDans l'Explorateur, sélectionnez le conteneur StarterPlayer. Ensuite, dans la fenêtre Propriétés, définissez DevComputerMovementMode et DevTouchMovementMode sur Scriptable.


Copiez le code suivant dans un LocalScript dans StarterCharacterScripts, ou téléchargez ce package et déposez-le dans StarterCharacterScripts.
PlayerPathFollow (LocalScript dans StarterCharacterScripts)local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local DESTINATION = Vector3.new(20, 0.5, 20)local GROUND_WAIT = 0.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Water = 20}})local character = script.Parentlocal humanoid = character:WaitForChild("Humanoid")local waypointslocal nextWaypointIndexlocal blockedConnectionlocal currentWaypointReachedConnectionlocal currentWaypointPlaneNormal = Vector3.zerolocal currentWaypointPlaneDistance = 0local pathfinderWorking = falselocal function disconnectCurrentWaypointReachedConnection()if not currentWaypointReachedConnection then return endcurrentWaypointReachedConnection:Disconnect()currentWaypointReachedConnection = nilendlocal function isCurrentWaypointReached()if humanoid.FloorMaterial == Enum.Material.Air thenreturn falseendlocal reached = falseif currentWaypointPlaneNormal ~= Vector3.zero then-- Calculer la distance entre le humanoïde et le plan de destinationlocal dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance-- Calculer la composante de la vitesse du humanoïde qui est dirigée vers le planlocal velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)-- Calculer le seuil du plan de destination en fonction de la vitesse du humanoïdelocal threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)-- Considérer le point de passage atteint s'il est inférieur au seuil devant le planreached = dist < thresholdelsereached = trueendif reached thencurrentWaypointPlaneNormal = Vector3.zerocurrentWaypointPlaneDistance = 0moveToNextWaypoint()endendlocal function calculateNextWaypointApproach()nextWaypointIndex += 1if nextWaypointIndex > #waypoints thenreturn falseendlocal currentWaypoint = waypoints[nextWaypointIndex - 1]local nextWaypoint = waypoints[nextWaypointIndex]-- Construire le plan de destination à partir du prochain point de passage vers l'actuelcurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position-- Définir la normale perpendiculaire au plan Y quand on ne grimpe pasif nextWaypoint.Label ~= "Climb" thencurrentWaypointPlaneNormal = Vector3.new(currentWaypointPlaneNormal.X, 0, currentWaypointPlaneNormal.Z)endif currentWaypointPlaneNormal.Magnitude > 0.000001 thencurrentWaypointPlaneNormal = currentWaypointPlaneNormal.UnitcurrentWaypointPlaneDistance = currentWaypointPlaneNormal:Dot(nextWaypoint.Position)endreturn trueendlocal function resetWaypointData()humanoid:Move(Vector3.zero)currentWaypointPlaneNormal = Vector3.zerocurrentWaypointPlaneDistance = 0disconnectCurrentWaypointReachedConnection()pathfinderWorking = falseendlocal function waitForGround()while humanoid.FloorMaterial == Enum.Material.Air dotask.wait(GROUND_WAIT)endendfunction moveToNextWaypoint()if calculateNextWaypointApproach() thendisconnectCurrentWaypointReachedConnection()currentWaypointReachedConnection = RunService.Heartbeat:Connect(isCurrentWaypointReached)local nextWaypointPosition = waypoints[nextWaypointIndex].Positionlocal nextWaypointAction = waypoints[nextWaypointIndex].Actionhumanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)if waypoints[nextWaypointIndex + 1] and waypoints[nextWaypointIndex + 1].Label == "UseBoat" thennextWaypointIndex += 1-- Appelez votre propre fonction personnalisée pour faire utiliser le bateau à l'agentelseif nextWaypointAction == Enum.PathWaypointAction.Jump thenhumanoid:ChangeState(Enum.HumanoidStateType.Jumping)while humanoid.FloorMaterial ~= Enum.Material.Air dotask.wait(GROUND_WAIT)endhumanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)endelseresetWaypointData()endendlocal function findStartingPoint(waypoints)nextWaypointIndex = 1while nextWaypointIndex + 1 <= #waypoints dolocal dist = waypoints[nextWaypointIndex + 1].Position - humanoid.RootPart.Positiondist = Vector3.new(dist.X, 0, dist.Z)if dist.magnitude >= 2 thenreturnendnextWaypointIndex += 1endendlocal function followPath()-- Calculer le cheminpathfinderWorking = truewaitForGround()local success, errorMessage = pcall(function()path:ComputeAsync(character.PrimaryPart.Position, DESTINATION)end)if not success or path.Status ~= Enum.PathStatus.Success thenwarn("Chemin non calculé !", errorMessage)returnend-- Récupérer les points de passage du cheminwaypoints = path:GetWaypoints()-- Détecter si le chemin devient bloquéblockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)-- Vérifier si l'obstacle est plus loin sur le cheminif blockedWaypointIndex >= nextWaypointIndex then-- Arrêter de détecter le blocage du chemin jusqu'à ce que le chemin soit recalculéblockedConnection:Disconnect()resetWaypointData()-- Appeler la fonction pour recalculer un nouveau cheminfollowPath()endend)findStartingPoint(waypoints)moveToNextWaypoint()endfollowPath()Modifiez la variable DESTINATION (
LIGNE 5) pour une destination Vector3 dans le monde 3D que le personnage du joueur peut atteindre.Procédez aux sections suivantes pour en savoir plus sur le calcul de chemin et le mouvement des personnages.
Création de chemin
La recherche de chemin est initiée par PathfindingService et sa méthode CreatePath() (
| Clé | Description | Type | Par défaut |
|---|---|---|---|
| AgentRadius | Rayon de l'agent, en studs. Utile pour déterminer la séparation minimale des obstacles. | integer | 2 |
| AgentHeight | Hauteur de l'agent, en studs. Un espace vide plus petit que cette valeur, comme l'espace sous les escaliers, sera marqué comme non-traversable. | integer | 5 |
| AgentCanJump | Détermine si le saut est autorisé lors de la recherche de chemin. | boolean | true |
| AgentCanClimb | Détermine si la montée de TrussParts est autorisée lors de la recherche de chemin. Un chemin grimpable a une étiquette Label nommée Climb et le coût d'un chemin grimpable est 1 par défaut. | boolean | false |
| WaypointSpacing | Espacement entre les points de passage intermédiaires dans le chemin. Si défini sur math.huge, il n'y aura pas de points de passage intermédiaires. | number | 4 |
| Costs | Table des matériaux ou des PathfindingModifiers définis et de leur coût de traversal. Utile pour faire en sorte que l'agent préfère certains matériaux/régions par rapport à d'autres. Voir modificateurs pour plus de détails. | table | nil |
Calcul de chemin
Après avoir créé un chemin valide avec CreatePath(), il doit être calculé en appelant Path:ComputeAsync() avec un Vector3 pour le point de départ et la destination (

Une fois que la Path est calculée, elle contiendra une série de points de passage qui tracent le chemin du début à la fin. Ces points peuvent être rassemblés avec la méthode Path:GetWaypoints() (

Mouvement sur le chemin
Chaque PathWaypoint consiste en un Position (Vector3) et une Action (PathWaypointAction). Pour déplacer un personnage contenant un Humanoid, comme un personnage Roblox typique, la meilleure manière est d'appeler Humanoid:Move() de point de passage en point de passage et d'utiliser le rappel isCurrentWaypointReached() du script (
Chemins bloqués
De nombreux mondes Roblox sont dynamiques ; des parties peuvent bouger ou tomber et des sols peuvent s'effondrer. Cela peut bloquer un chemin calculé et empêcher le personnage d'atteindre sa destination. Pour gérer cela, vous pouvez connecter l'événement Path.Blocked et recalculer le chemin autour de ce qui le bloque (
Modificateurs de recherche de chemin
Par défaut, Path:ComputeAsync() renvoie le chemin le plus court entre le point de départ et la destination, sauf qu'il essaie d'éviter les sauts. Cela semble artificiel dans certaines situations ; par exemple, un chemin peut traverser de l'eau marécageuse plutôt que de la contourner simplement parce que le chemin dans l'eau est géométriquement plus court.

Pour optimiser encore plus la recherche de chemin, vous pouvez mettre en œuvre des modificateurs de recherche de chemin pour calculer des chemins plus intelligents à travers divers matériaux, autour de régions définies, ou pour ignorer les obstacles.
Coûts des matériaux
Lorsque vous travaillez avec des matériaux Terrain et BasePart, vous pouvez inclure une table Costs dans CreatePath() pour rendre certains matériaux plus traversables que d'autres. Tous les matériaux ont un coût par défaut de 1 et tout matériau peut être défini comme non-traversable en définissant sa valeur à math.huge.
Les clés de la table Costs doivent être des noms de chaîne représentant les noms d'Enum.Material, par exemple Water pour Enum.Material.Water ou CrackedLava pour Enum.Material.CrackedLava.
PlayerPathFollow (LocalScript)local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local DESTINATION = Vector3.new(20, 0.5, 20)local GROUND_WAIT = 0.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Water = 20, CrackedLava = 100, Slate = 20}})
Configurer des régions
Dans certains cas, la préférence de matériau n'est pas suffisante. Par exemple, vous pourriez vouloir que les personnages évitent une région définie, quel que soit le matériau sous leurs pieds. Cela peut être réalisé en ajoutant un objet PathfindingModifier à une partie.
Créez une partie Anchored autour de la région et définissez sa propriété CanCollide sur false.

Insérez une instance PathfindingModifier sur la partie, localisez sa propriété Label, et attribuez-lui un nom significatif comme DangerZone.

Incluez une clé correspondante DangerZone et une valeur numérique associée dans la table Costs de CreatePath(). Un modificateur peut être défini comme non-traversable en définissant sa valeur à math.huge.
PlayerPathFollow (LocalScript)local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local DESTINATION = Vector3.new(20, 0.5, 20)local GROUND_WAIT = 0.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {DangerZone = math.huge, Water = 20, CrackedLava = 20, Slate = 20}})
Ignorer les obstacles
Dans certains cas, il est utile de trouver un chemin à travers des obstacles solides comme s'ils n'existaient pas. Cela vous permet de calculer un chemin à travers des bloqueurs physiques spécifiques, par opposition à l'échec immédiat du calcul.
Créez une partie Anchored autour de l'objet et définissez sa propriété CanCollide sur false.

Insérez une instance PathfindingModifier sur la partie et activez sa propriété PassThrough.

Maintenant, lorsqu'un chemin est calculé du PNJ zombie au personnage joueur, le chemin s'étend au-delà de la porte et vous pouvez inciter le zombie à le traverser. Même si le zombie n'est pas capable d'ouvrir la porte, il réagit comme s'il "entendait" le personnage derrière la porte.

Liens de recherche de chemin
Il arrive parfois qu'il soit nécessaire de trouver un chemin à travers un espace qui ne peut pas être normalement traversé, comme à travers un gouffre, et d'effectuer une action personnalisée pour atteindre le prochain point de passage. Cela peut être réalisé par l'objet PathfindingLink.
En utilisant l'exemple ci-dessus, vous pouvez faire utiliser un bateau à l'agent.

Pour créer un PathfindingLink en utilisant cet exemple :
- OPTIONNELActivez Liens de recherche de chemin depuis le widget Options de visualisation dans le coin supérieur droit de la vue 3D. Cela aide à la visualisation et au débogage lors de la mise en œuvre des liens de recherche de chemin.
Créez deux Attachments, un sur le siège du bateau et un près du point d'atterrissage du bateau.

Créez un objet PathfindingLink dans l'espace de travail, puis assignez ses propriétés Attachment0 et Attachment1 aux attachements de départ et de fin respectivement.


Attribuez un nom significatif comme UseBoat à sa propriété Label. Ce nom est utilisé comme un indicateur dans le script de recherche de chemin pour déclencher une action personnalisée lorsque l'agent atteindra le point de départ du lien.

Incluez une table Costs dans CreatePath() contenant à la fois une clé Water et une clé personnalisée correspondant au nom de propriété Label. Attribuez à la clé personnalisée une valeur inférieure à celle de Water.
PlayerPathFollow (LocalScript)local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local DESTINATION = Vector3.new(20, 0.5, 20)local GROUND_WAIT = 0.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {UseBoat = 2, Water = 20}})Dans la fonction moveToNextWaypoint() (
LIGNES 93–114), une vérification personnalisée pour le nom de modificateur Label peut être utilisée pour prendre une action différente de Humanoid:Move() ; dans ce cas, vous pourriez appeler une fonction pour placer l'agent dans le bateau, déplacer le bateau à travers l'eau, désembarquer l'agent au point d'atterrissage du bateau, puis poursuivre le chemin de l'agent jusqu'à sa destination finale.
Compatibilité en streaming
Le streaming d'instances en jeu est une fonctionnalité puissante qui charge et décharge dynamiquement du contenu 3D à mesure que le personnage du joueur se déplace dans le monde. Au fur et à mesure qu'il explore l'espace 3D, de nouveaux sous-ensembles de l'espace sont diffusés sur son appareil et certains des sous-ensembles existants peuvent être déchargés.
Considérez les meilleures pratiques suivantes pour utiliser PathfindingService dans les jeux compatibles avec le streaming :
Le streaming peut bloquer ou débloquer un chemin donné à mesure qu'un personnage se déplace dessus. Par exemple, alors qu'un personnage court à travers une forêt, un arbre pourrait apparaître quelque part devant lui et obstruer le chemin. Pour que la recherche de chemin fonctionne sans problème avec le streaming, il est fortement recommandé d'utiliser la technique gérer les chemins bloqués et de recalculer le chemin lorsque cela est nécessaire.
Une approche courante dans la recherche de chemin est d'utiliser les coordonnées des objets existants pour le calcul, comme définir une destination de chemin à la position d'un modèle de TreasureChest existant dans le monde. Cette approche est entièrement compatible avec les Scripts côté serveur puisqu'ils ont une vue complète du monde à tout moment, mais les LocalScripts et ModuleScripts qui s'exécutent côté client peuvent échouer s'ils tentent de calculer un chemin vers un objet qui n'est pas diffusé.
Pour résoudre ce problème, envisager de définir la destination sur la position d'une BasePart dans un modèle persistant. Les modèles persistants se chargent peu après l'entrée du joueur et ne sont jamais déchargés, de sorte qu'un script côté client peut se connecter à l'événement PersistentLoaded et accéder en toute sécurité au modèle pour créer des points de passage après que l'événement se soit déclenché.
Limitations et facteurs d'échec
Le moteur de recherche de chemin comprend des limitations spécifiques pour assurer un traitement efficace et des performances optimales. De plus, les calculs de recherche de chemin peuvent échouer pour diverses raisons, comme décrit ci-dessous.