Recherche de chemin

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

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.

Un gros plan de la vue 3D avec le bouton Options de visualisation indiqué dans le coin supérieur droit.

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.

Maillage de navigation affiché dans Studio

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 :

  1. IMPORTANT
    Dans l'Explorateur, sélectionnez le conteneur StarterPlayer. Ensuite, dans la fenêtre Propriétés, définissez DevComputerMovementMode et DevTouchMovementMode sur Scriptable.

  2. 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.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    Water = 20
    }
    })
    local character = script.Parent
    local humanoid = character:WaitForChild("Humanoid")
    local waypoints
    local nextWaypointIndex
    local blockedConnection
    local currentWaypointReachedConnection
    local currentWaypointPlaneNormal = Vector3.zero
    local currentWaypointPlaneDistance = 0
    local pathfinderWorking = false
    local function disconnectCurrentWaypointReachedConnection()
    if not currentWaypointReachedConnection then return end
    currentWaypointReachedConnection:Disconnect()
    currentWaypointReachedConnection = nil
    end
    local function isCurrentWaypointReached()
    if humanoid.FloorMaterial == Enum.Material.Air then
    return false
    end
    local reached = false
    if currentWaypointPlaneNormal ~= Vector3.zero then
    -- Calculer la distance entre le humanoïde et le plan de destination
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Calculer la composante de la vitesse du humanoïde qui est dirigée vers le plan
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Calculer le seuil du plan de destination en fonction de la vitesse du humanoïde
    local threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)
    -- Considérer le point de passage atteint s'il est inférieur au seuil devant le plan
    reached = dist < threshold
    else
    reached = true
    end
    if reached then
    currentWaypointPlaneNormal = Vector3.zero
    currentWaypointPlaneDistance = 0
    moveToNextWaypoint()
    end
    end
    local function calculateNextWaypointApproach()
    nextWaypointIndex += 1
    if nextWaypointIndex > #waypoints then
    return false
    end
    local currentWaypoint = waypoints[nextWaypointIndex - 1]
    local nextWaypoint = waypoints[nextWaypointIndex]
    -- Construire le plan de destination à partir du prochain point de passage vers l'actuel
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Définir la normale perpendiculaire au plan Y quand on ne grimpe pas
    if nextWaypoint.Label ~= "Climb" then
    currentWaypointPlaneNormal = Vector3.new(currentWaypointPlaneNormal.X, 0, currentWaypointPlaneNormal.Z)
    end
    if currentWaypointPlaneNormal.Magnitude > 0.000001 then
    currentWaypointPlaneNormal = currentWaypointPlaneNormal.Unit
    currentWaypointPlaneDistance = currentWaypointPlaneNormal:Dot(nextWaypoint.Position)
    end
    return true
    end
    local function resetWaypointData()
    humanoid:Move(Vector3.zero)
    currentWaypointPlaneNormal = Vector3.zero
    currentWaypointPlaneDistance = 0
    disconnectCurrentWaypointReachedConnection()
    pathfinderWorking = false
    end
    local function waitForGround()
    while humanoid.FloorMaterial == Enum.Material.Air do
    task.wait(GROUND_WAIT)
    end
    end
    function moveToNextWaypoint()
    if calculateNextWaypointApproach() then
    disconnectCurrentWaypointReachedConnection()
    currentWaypointReachedConnection = RunService.Heartbeat:Connect(isCurrentWaypointReached)
    local nextWaypointPosition = waypoints[nextWaypointIndex].Position
    local nextWaypointAction = waypoints[nextWaypointIndex].Action
    humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)
    if waypoints[nextWaypointIndex + 1] and waypoints[nextWaypointIndex + 1].Label == "UseBoat" then
    nextWaypointIndex += 1
    -- Appelez votre propre fonction personnalisée pour faire utiliser le bateau à l'agent
    elseif nextWaypointAction == Enum.PathWaypointAction.Jump then
    humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    while humanoid.FloorMaterial ~= Enum.Material.Air do
    task.wait(GROUND_WAIT)
    end
    humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)
    end
    else
    resetWaypointData()
    end
    end
    local function findStartingPoint(waypoints)
    nextWaypointIndex = 1
    while nextWaypointIndex + 1 <= #waypoints do
    local dist = waypoints[nextWaypointIndex + 1].Position - humanoid.RootPart.Position
    dist = Vector3.new(dist.X, 0, dist.Z)
    if dist.magnitude >= 2 then
    return
    end
    nextWaypointIndex += 1
    end
    end
    local function followPath()
    -- Calculer le chemin
    pathfinderWorking = true
    waitForGround()
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, DESTINATION)
    end)
    if not success or path.Status ~= Enum.PathStatus.Success then
    warn("Chemin non calculé !", errorMessage)
    return
    end
    -- Récupérer les points de passage du chemin
    waypoints = 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 chemin
    if 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 chemin
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Modifiez la variable DESTINATION (

    LIGNE 5
    ) pour une destination Vector3 dans le monde 3D que le personnage du joueur peut atteindre.

  4. 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() (

LIGNES 9–14
). Cette méthode accepte une table de paramètres optionnels qui affinent la manière dont le personnage (agent) se déplace le long du chemin.

CléDescriptionTypePar défaut
AgentRadiusRayon de l'agent, en studs. Utile pour déterminer la séparation minimale des obstacles.integer2
AgentHeightHauteur de l'agent, en studs. Un espace vide plus petit que cette valeur, comme l'espace sous les escaliers, sera marqué comme non-traversable.integer5
AgentCanJumpDétermine si le saut est autorisé lors de la recherche de chemin.booleantrue
AgentCanClimbDé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.booleanfalse
WaypointSpacingEspacement 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.number4
CostsTable 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.tablenil

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 (

LIGNES 133–139
).

Début/fin du chemin marqué à travers deux ponts

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() (

LIGNE 142
). Le tableau retourné est organisé dans l'ordre des points de passage du début à la fin du chemin.

Points de passage indiqués à travers le chemin calculé
Points de passage indiqués à travers le chemin calculé

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 (

LIGNES 32–56
) pour détecter quand le personnage atteint chaque point de passage.

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 (

LIGNES 145–154
).

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.

Deux chemins indiqués avec le chemin le plus court ne nécessitant pas nécessairement plus de logique

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.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, quel que soit le matériau sous leurs pieds. Cela peut être réalisé en ajoutant un objet PathfindingModifier à une partie.

  1. Créez une partie Anchored autour de la région et définissez sa propriété CanCollide sur false.

    Partie ancrée définissant une région pour appliquer un modificateur de recherche de chemin.
  2. Insérez une instance PathfindingModifier sur la partie, localisez sa propriété Label, et attribuez-lui un nom significatif comme DangerZone.

    Instance PathfindingModifier avec la propriété Label définie sur DangerZone.
  3. 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.01
    local VELOCITY_MULTIPLIER = 0.0625
    local 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.

  1. Créez une partie Anchored autour de l'objet et définissez sa propriété CanCollide sur false.

    Partie ancrée définissant une région pour appliquer un modificateur de recherche de chemin.
  2. Insérez une instance PathfindingModifier sur la partie et activez sa propriété PassThrough.

    Instance PathfindingModifier avec la propriété PassThrough activée.

    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.

    Chemin du PNJ zombie passant à travers la porte précédemment bloquante.

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.

PathfindingLink montrant comment un agent peut utiliser un bateau.

Pour créer un PathfindingLink en utilisant cet exemple :

  1. OPTIONNEL
    Activez 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.

  2. Créez deux Attachments, un sur le siège du bateau et un près du point d'atterrissage du bateau.

    Attachements créés pour le début et la fin du lien de recherche de chemin.
  3. 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.

    Propriétés Attachment0/Attachment1 d'un PathfindingLink. Visualisation du PathfindingLink dans le monde 3D.
  4. 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.

    Propriété Label spécifiée pour PathfindingLink.
  5. 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.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    UseBoat = 2, Water = 20
    }
    })
  6. 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.

©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.