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 rendre un maillage de navigation et des modificateurs d'étiquettes. 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é, des 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 différentes 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 à la fois DevComputerMovementMode et DevTouchMovementMode sur Scriptable.


Copiez le code suivant dans un LocalScript au sein de StarterCharacterScripts, ou obtenez 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 du humanoïde au 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 à partir du plan de destination basé sur la vitesse du humanoïdelocal threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)-- Considérer le waypoint atteint si moins que le 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 waypoint vers le courantcurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position-- Définir la normale perpendiculaire au plan Y lorsqu'il 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-- Obtenir les waypoints 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 le 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.Poursuivez les sections suivantes pour en savoir plus sur le calcul de chemin et le mouvement du personnage.
Création de chemin
La recherche de chemin est initiée via 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. L'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 pendant la recherche de chemin est autorisé. | boolean | true |
| AgentCanClimb | Détermine si l'escalade des TrussParts pendant la recherche de chemin est autorisée. Un chemin escaladable a une Label nommée Climb et le coût pour un chemin escaladable est 1 par défaut. | boolean | false |
| WaypointSpacing | Espacement entre les waypoints intermédiaires dans le chemin. S'il est défini sur math.huge, il n'y aura pas de waypoints intermédiaires. | number | 4 |
| Costs | Table des matériaux ou des PathfindingModifiers définis et leur coût de traversée. Utile pour faire préférer à l'agent 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 le Path est calculé, il contiendra une série de waypoints 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 se compose à la fois d'une Position (Vector3) et d'une Action (PathWaypointAction). Pour déplacer un personnage contenant un Humanoid, comme un personnage Roblox typique, la meilleure façon est d'appeler Humanoid:Move() d'un waypoint à l'autre et d'utiliser le rappel isCurrentWaypointReached() du script (
Chemins bloqués
De nombreux mondes Roblox sont dynamiques ; des parties peuvent se déplacer 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 l'a bloqué (
Modificateurs de recherche de chemin
Par défaut, Path:ComputeAsync() retourne le chemin le plus court entre le point de départ et la destination, à l'exception du fait qu'il essaie d'éviter les sauts. Cela semble peu naturel dans certaines situations ; par exemple, un chemin peut passer à travers de l'eau marécageuse plutôt que de l'éviter simplement parce que le chemin à travers 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 des obstacles.
Coûts des matériaux
Lors de l'utilisation 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 sur math.huge.
Les clés dans la table Costs doivent être des noms de chaîne représentant les noms de Enum.Material, par exemple Water pour Enum.Material.Water ou CrackedLava pour Enum.Material.CrackedLava.
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.01
local VELOCITY_MULTIPLIER = 0.0625
local 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, peu importe les matériaux 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é DangerZone correspondante 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 sur 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 rechercher 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, plutôt que l'échec 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 NPC zombie au personnage du joueur, le chemin s'étend au-delà de la porte et vous pouvez inciter le zombie à le traverser. Même si le zombie est incapable d'ouvrir la porte, il réagit comme s'il "entend" le personnage derrière la porte.

Liens de recherche de chemin
Parfois, il est 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 waypoint. Cela peut être réalisé via l'objet PathfindingLink.
En utilisant l'exemple ci-dessus, vous pouvez faire en sorte que l'agent utilise un bateau.

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 de 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 attribuez 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 atteint 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 la 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 du modificateur Label peut être utilisée pour effectuer une action différente de Humanoid:Move() ; dans ce cas, vous pourriez appeler une fonction pour asseoir l'agent dans le bateau, déplacer le bateau à travers l'eau, défaire l'agent au point d'atterrissage du bateau, puis continuer le chemin de l'agent vers sa destination finale.
Compatibilité avec le streaming
Le streaming d'instance en jeu est une fonctionnalité puissante qui charge et décharge dynamiquement du contenu 3D à mesure que le personnage d'un joueur se déplace dans le monde. Alors qu'ils explorent l'espace 3D, de nouveaux sous-ensembles de l'espace sont diffusés vers leur appareil et certains des sous-ensembles existants peuvent être diffusés.
Considérez les meilleures pratiques suivantes pour utiliser PathfindingService dans des jeux activés pour le streaming :
Le streaming peut bloquer ou débloquer un chemin donné à mesure qu'un personnage se déplace le long de celui-ci. Par exemple, alors qu'un personnage court à travers une forêt, un arbre peut être diffusé quelque part devant eux et obstruer le chemin. Pour faire fonctionner la recherche de chemin de manière transparente avec le streaming, il est fortement recommandé d'utiliser la technique gestion des 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 sur la position d'un modèle TreasureChest existant dans le monde. Cette approche est entièrement compatible avec les Scripts côté serveur puisque le serveur a une vue complète du monde à tout moment, mais les LocalScripts et ModuleScripts qui s'exécutent sur le client peuvent échouer s'ils tentent de calculer un chemin vers un objet qui n'est pas diffusé.
Pour résoudre ce problème, envisagez 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 diffusés, donc 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 waypoints 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 garantir un traitement efficace et des performances optimales. De plus, les calculs de recherche de chemin peuvent échouer pour diverses raisons comme indiqué ci-dessous.