Recherche de chemin de caractère

*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 le long d'un chemin logique pour atteindre une destination, en évitant les obstacles et (facultativement) des matériaux dangereux ou des régions définies.

Visualisation de la navigation

Pour aider à la recherche de chemin et au débogage, Studio peut rendre un maillage de navigation et modifier les étiquettes.Pour les activer, activez maillage de navigation et modifieurs de chemin à partir du widget options de visualisation dans le coin supérieur droit du fenêtre de jeu3D.

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.

Avec maille de navigation activée, les zones colorées montrent où un personnage pourrait marcher ou nager, tandis que les zones non colorées sont bloquées.Les petites flèches indiquent les zones que le personnage tentera d'atteindre en sautant, en supposant que vous avez défini à lorsque vous créez le chemin .

Navigation mesh showing in Studio

Avec modifieurs de recherche de chemin activés, les étiquettes de texte indiquent des matériaux et des régions spécifiques qui sont pris en compte lors de l'utilisation de modifieurs de recherche de chemin.

Navigation labels showing on navigation mesh

Limites connues

Les fonctionnalités de recherche de chemin ont des limitations spécifiques pour garantir un traitement efficace et des performances optimales.

Limite de placement vertical

Les calculs de recherche de chemin ne prennent en compte que les parties situées dans certaines limites verticales :

  • Limite inférieure — Les parties avec une coordonnée inférieure Y à -65,536 sont ignorées.
  • Limite supérieure — Les parties avec une coordonnée supérieure Y atteignant 65 536 studs sont ignorées.
  • Espacement vertical — La distance verticale du point inférieur de la partie la plus basse Y à la coordonnée du sommet de la partie la plus élevée Y ne doit pas dépasser 65 536 studs ; sinon, le système de recherche de chemin ignorera ces parties pendant la recherche de chemin.

Limitation de la distance de recherche

La distance de ligne de vue directe pour la recherche de chemin depuis le point de départ jusqu'au point d'arrivée ne doit pas dépasser 3 000 studs.Dépasser cette distance entraînera un statut NoPath >.

Créer des chemins

La recherche de chemin est initiée via PathfindingService et sa fonction CreatePath().

Lecteur localScript

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath()

CreatePath() accepte une table facultative de paramètres qui précise comment le personnage (agent) se déplace le long du chemin.

CléAvertissementTypePar défaut
AgentRadiusRayon de l'agent, en studs. Utile pour déterminer la séparation minimale des obstacles.entier2
AgentHeightHauteur de l'agent, en studs. L'espace vide plus petit que cette valeur, comme l'espace sous les escaliers, sera marqué comme non traversable.entier5
AgentCanJumpDétermine si le saut pendant la recherche de chemin est autorisé.booléentrue
AgentCanClimbDétermine si l'escalade TrussParts pendant la recherche de chemin est autorisée.booléenfalse
WaypointSpacingEspacement entre les points de chemin intermédiaires dans le chemin. Si vous le définissez à math.huge, il n'y aura pas de points de chemin intermédiaires.numéro4
CostsTable de matériaux ou définie PathfindingModifiers et leur coût pour la traversée.Utile pour faire en sorte que l'agent préfère certains matériaux/régions à d'autres.Voir modifieurs pour les détails.tableaunil
Lecteur localScript

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentRadius = 3,
AgentHeight = 6,
AgentCanJump = false,
Costs = {
Water = 20
}
})

Notez que l'agent peut escalader pendant la recherche de chemin en supposant que vous avez défini à lorsque vous créez le chemin et que rien ne bloque l'agent du chemin d'escalade.Un chemin escaladable a l'étiquette Montée et le coût pour un chemin escaladable est 1 par défaut.

Path going up a climbable TrussPart ladder
LocalScript - Routede montée en truss

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentCanClimb = true,
Costs = {
Climb = 2 -- Coût du chemin d'escalade ; la valeur par défaut est 1
}
})

Avancer le long des chemins

Cette section utilise le script de recherche de chemin suivant pour le personnage du joueur. Pour tester en lisant :

  1. Copiez le code dans un LocalScript à l'intérieur de StarterCharacterScripts .
  2. Définissez la variable TEST_DESTINATION à une destination Vector3 dans votre monde 3D que le personnage joueur peut atteindre.
  3. Suivez les sections suivantes pour en savoir plus sur la計算路径 et le mouvement des caractères.
LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Calculer le chemin
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenir les points de cheminement
waypoints = path:GetWaypoints()
-- Détecter si le chemin devient bloqué
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Vérifiez si l'obstacle est plus bas sur le chemin
if blockedWaypointIndex >= nextWaypointIndex then
-- Arrêter la détection du blocage du chemin jusqu'à ce que le chemin soit récalculé
blockedConnection:Disconnect()
-- Fonction d'appel pour récalculer le nouveau chemin
followPath(destination)
end
end)
-- Détecter lorsque le déplacement vers le prochain point de passage est terminé
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Augmenter l'index des points de passage et se déplacer au prochain point de passage
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Déplacez-vous initialement au deuxième point de passage (le premier point de passage est le commencerdu chemin ; sautez-le)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
followPath(TEST_DESTINATION)

Calculer le 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.

LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Calculer le chemin
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
end
Path start/end marked on series of islands and bridges

Obtenir des points de chemin

Une fois le Path calculé, il contiendra une série de points de passage qui tracera le chemin du début à terminer.Ces points peuvent être réunis avec la fonction Path:GetWaypoints().

LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Calculer le chemin
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenir les points de cheminement
waypoints = path:GetWaypoints()
end
end
Waypoints indicated across computed path
Points de chemin indiqués sur le chemin calculé

Déplacement du chemin

Chaque point de passage se compose à la fois d'une position () et d'une action () ().Pour déplacer un personnage contenant un Humanoid , comme un personnage typique de Roblox, le moyen le plus facile est d'appeler Humanoid:MoveTo() de point de chemin à point de chemin, en utilisant l'événement MoveToFinished pour détecter quand le personnage atteint chaque point de chemin.

LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Calculer le chemin
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenir les points de cheminement
waypoints = path:GetWaypoints()
-- Détecter si le chemin devient bloqué
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Vérifiez si l'obstacle est plus bas sur le chemin
if blockedWaypointIndex >= nextWaypointIndex then
-- Arrêter la détection du blocage du chemin jusqu'à ce que le chemin soit récalculé
blockedConnection:Disconnect()
-- Fonction d'appel pour récalculer le nouveau chemin
followPath(destination)
end
end)
-- Détecter lorsque le déplacement vers le prochain point de passage est terminé
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Augmenter l'index des points de passage et se déplacer au prochain point de passage
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Déplacez-vous initialement au deuxième point de passage (le premier point de passage est le commencerdu chemin ; sautez-le)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end

Gérer les chemins bloqués

De nombreux mondes Roblox sont dynamiques ; des parties peuvent se déplacer ou tomber, et des étages 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 récomputer le chemin autour de tout ce qui l'a bloqué.

LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Calculer le chemin
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenir les points de cheminement
waypoints = path:GetWaypoints()
-- Détecter si le chemin devient bloqué
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Vérifiez si l'obstacle est plus bas sur le chemin
if blockedWaypointIndex >= nextWaypointIndex then
-- Arrêter la détection du blocage du chemin jusqu'à ce que le chemin soit récalculé
blockedConnection:Disconnect()
-- Fonction d'appel pour récalculer le nouveau chemin
followPath(destination)
end
end)
end
end

Modifieurs de recherche de chemin

Par défaut, retourne le chemin le plus court entre le point de départ et la destination, à l'exception qu'il essaie d'éviter les sauts.Cela semble artificiel dans certaines situations — par instance, un chemin peut traverser l'eau plutôt qu'au-dessus d'un pont proche simplement parce que le chemin à travers l'eau est plus court géométriquement.

Two paths indicated with the shorter path not necessarily more logical

Pour optimiser encore plus la recherche de chemin, vous pouvez implémenter modifieurs de recherche de chemin pour calculer des chemins plus intelligents à travers différents matériaux , autour de régions définies ou à travers des obstacles .

Définir les coûts matériels

Lorsque vous travaillez avec Terrain et BasePart matériaux, 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 dans la table Costs doivent être des noms de chaîne représentant des noms Enum.Material par exemple Water pour Enum.Material.Water.

LocalScript - Recherche de caractères

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath({
Costs = {
Water = 20,
Mud = 5,
Neon = math.huge
}
})

Travailler avec des régions

Dans certains cas, la préférence du matériau n'est pas suffisante.Par exemple, vous voudrez peut-être que les personnages évitent une région définie , indépendamment des matériaux au sol.Cela peut être réalisé en ajoutant un objet PathfindingModifier à une partie.

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

    Anchored part defining a region to apply a pathfinding modifier to
  2. Insérez une instance PathfindingModifier sur la partie, localisez sa propriété Label et attribuez un nom significatif comme DangerZone .

    PathfindingModifier instance with Label property set to DangerZone
  3. Incluez une table Costs dans CreatePath() contenant une clé correspondante et une valeur numérique associée.Un modifieur peut être défini comme non traversable en définissant sa valeur à math.huge .

    LocalScript - Recherche de caractères

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local Workspace = game:GetService("Workspace")
    local path = PathfindingService:CreatePath({
    Costs = {
    DangerZone = math.huge
    }
    })

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 blocs physiques spécifiques, versus l'échec de la simulation à l'avance.

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

    Anchored part defining a region to apply a pathfinding modifier to
  2. Insérez une instance PathfindingModifier sur la partie et activez sa propriété PassThrough.

    PathfindingModifier instance with PassThrough property enabled

    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 «entendait» le personnage derrière la porte.

    Zombie NPC path passing through the previously blocking door

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 point de passage.Cela peut être réalisé via l'objet PathfindingLink.

En utilisant l'exemple de l'île ci-dessus, vous pouvez faire en sorte que l'agent utilise un bateau au lieu de traverser tous les ponts.

PathfindingLink showing how an agent can use a boat instead of walking across all of the bridges

Pour créer un PathfindingLink à l'aide de cet exemple :

  1. Pour aider à la visualisation et au débogage, activez les liens de recherche de chemin à partir du widget Options de visualisation dans le coin supérieur droit de la fenêtre de jeu3D.

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

    Attachments created for pathfinding link's start and end
  3. Créez un objet PathfindingLink dans l'espace de travail, puis attribuez ses propriétés Attachement0 et Attachement1 aux annexes de départ et de fin respectivement.

    Attachment0/Attachment1 properties of a PathfindingLink PathfindingLink visualized in the 3D world
  4. Attribuez un nom significatif comme UseBoat à sa propriété Label.Ce nom est utilisé comme un drapeau dans le script de recherche de chemin pour déclencher une action personnalisée lorsque l'agent atteint le point de lien de départ.

    Label property specified for 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 à Water .

    LocalScript - Recherche de caractères

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local Workspace = game:GetService("Workspace")
    local path = PathfindingService:CreatePath({
    Costs = {
    Water = 20,
    UseBoat = 1
    }
    })
  6. Dans l'événement qui se déclenche lorsqu'un point de passage est atteint, ajoutez un contrôle personnalisé pour le nom du modifieur Label et prenez une action différente de Humanoid:MoveTo() — dans ce cas, appelez une fonction pour installer l'agent dans le bateau, déplacer le bateau sur l'eau et poursuivre le chemin de l'agent lors de l'arrivée sur l'île de destination.

    LocalScript - Recherche de caractères

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local Workspace = game:GetService("Workspace")
    local path = PathfindingService:CreatePath({
    Costs = {
    Water = 20,
    UseBoat = 1
    }
    })
    local player = Players.LocalPlayer
    local character = player.Character
    local humanoid = character:WaitForChild("Humanoid")
    local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)
    local waypoints
    local nextWaypointIndex
    local reachedConnection
    local blockedConnection
    local function followPath(destination)
    -- Calculer le chemin
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, destination)
    end)
    if success and path.Status == Enum.PathStatus.Success then
    -- Obtenir les points de cheminement
    waypoints = path:GetWaypoints()
    -- Détecter si le chemin devient bloqué
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Vérifiez si l'obstacle est plus bas sur le chemin
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Arrêter la détection du blocage du chemin jusqu'à ce que le chemin soit récalculé
    blockedConnection:Disconnect()
    -- Fonction d'appel pour récalculer le nouveau chemin
    followPath(destination)
    end
    end)
    -- Détecter lorsque le déplacement vers le prochain point de passage est terminé
    if not reachedConnection then
    reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
    if reached and nextWaypointIndex < #waypoints then
    -- Augmenter l'index des points de passage et se déplacer au prochain point de passage
    nextWaypointIndex += 1
    -- Utilisez le bateau si la balise de point de passage est "UseBoat"; sinon, déplacez-vous au prochain point de passage
    if waypoints[nextWaypointIndex].Label == "UseBoat" then
    useBoat()
    else
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    else
    reachedConnection:Disconnect()
    blockedConnection:Disconnect()
    end
    end)
    end
    -- Déplacez-vous initialement au deuxième point de passage (le premier point de passage est le commencerdu chemin ; sautez-le)
    nextWaypointIndex = 2
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    else
    warn("Path not computed!", errorMessage)
    end
    end
    function useBoat()
    local boat = Workspace.BoatModel
    humanoid.Seated:Connect(function()
    -- Commencer à déplacer le bateau si l'agent est assis
    if humanoid.Sit then
    task.wait(1)
    boat.CylindricalConstraint.Velocity = 5
    end
    -- Détecter la position de contrainte par rapport à l'île
    local boatPositionConnection
    boatPositionConnection = RunService.PostSimulation:Connect(function()
    -- Arrêter le bateau lorsque à côté de l'île
    if boat.CylindricalConstraint.CurrentPosition >= 94 then
    boatPositionConnection:Disconnect()
    boat.CylindricalConstraint.Velocity = 0
    task.wait(1)
    -- Déloger l'agent et poursuivre la destination
    humanoid.Sit = false
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    end)
    end)
    end
    followPath(TEST_DESTINATION)

Compatibilité streaming

Le streaming d'instance en expérience est une fonctionnalité puissante qui charge et décharge dynamiquement du contenu 3D lorsque le personnage d'un joueur se déplace dans le monde.En explorant l'espace 3D, de nouveaux sous-ensembles de la chaîne d'espace se rendent sur leur appareil et certains des sous-ensembles existants pourraient s'écouler.

Considérez les meilleures pratiques suivantes pour l'utilisation de PathfindingService dans les expériences activées en 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 traverse une forêt, un arbre pourrait apparaître quelque part devant eux 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 de gestion des chemins bloqués et de récalculer le chemin lorsque nécessaire.

  • Une approche commune dans la recherche de chemin consiste à utiliser les coordonnées des objets existants pour la計算 , telle que définir une destination de chemin à la position d'un modèle Coffre au trésor existant dans le monde.Cette approche est entièrement compatible avec le côté serveur Scripts puisque le serveur a une vue complète du monde en tout temps, mais LocalScripts et ModuleScripts ceux qui s'exécutent sur le client peuvent échouer si ils tentent de calculer un chemin vers un objet qui n'est pas diffusé en entrée.

    Pour résoudre ce problème, envisagez de définir la destination à la position d'un BasePart dans un modèlisationpersistant permanent.Les modèles persistants se chargent peu après que le joueur ait rejoint 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 points de chemin après que l'événement se déclenche.