Pathfinding é o processo de mover um personagem ao longo de um caminho lógico para chegar a um destino, evitando obstáculos e (opcionalmente) materiais perigosos ou regiões definidas.
Visualização de Navegação
Para ajudar com a otimização do caminho e diagnóstico, o Studio pode renderizar uma malha de navegação e modificador de nav r et r 2> t 5> nav 8> mesh8> e 9> modificador de nav 1> modificador de nav 4> mesh4> do widget 7>Opções de Visualização7> no canto superior direito da janela de visualização 3D.
Com Rede de navegação ativado, áreas coloridas mostram onde um personagem pode andar ou nadar, enquanto áreas não coloridas estão bloqueadas. As pequenas setas indicam áreas que um personagem tentará alcançar ao pular, assumindo que você tenha definido AgentCanJump para true quando criar a 2> 5>Rede de Path5> .
Com modificadores de Pathfinding ativados, rótulos de texto indicam materiais e regiões específicas que são levados em consideração ao usar modificadores de 1>Pathfinding1> .
Limitações Conhecidas
Os recursos de Pathfinding especificam limitações para garantir um processamento eficiente e performanceotimimal.
Limite de Colocação Vertical
Cálculos de caminhos consideram apenas peças dentro de certos limites verticais:
- Limite Inferior — Peças com coordenadas de baixo Y têm seus contornos ignorados.
- Limite superior - Peças com coordenadas de topo Y excedendo 65.536 studs são ignoradas.
- Espaço Vertical — A distância vertical da parte mais baixa até a parte superior Y coordenada deve exceder 65.536 studs; caso contrário, o sistema de caminhada de path irá ignorar essas partes durante a cálculo de path.
Limitação de Distância de Pesquisa
A distância direta de visão para a localização de caminhos a partir do início até o ponto de destino não deve exceder 3.000 metros. Exceder esta distância resultará em um status de NoPath.
Criando Caminhos
A pesquisa de caminho é iniciada através de PathfindingService e de sua função CreatePath().
Script Local
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath()
CreatePath() aceita uma tabela opcional de parâmetros que ajusta como o personagem (agente) se move ao longo do caminho.
Clave | Descripción | Tipo | Por defecto |
---|---|---|---|
AgentRadius | Raíz del agente, en studs. Útil para determinar la separación mínima de los obstáculos. | entero | 2 |
AgentHeight | Altura del agente, en studs. Espacio vacío más pequeño que este valor, como el espacio debajo de las escaleras, se marcará como no transitable. | entero | 5 |
AgentCanJump | Determina si se permite saltar durante el cálculo del camino. | booleano | true |
AgentCanClimb | Determina si se permite escalar TrussParts durante la búsqueda de caminos. | booleano | false |
WaypointSpacing | Espaciamiento entre los puntos de intermedio en el camino. Si se establece en math.huge , no habrá puntos de intermedio. | número | 4 |
Costs | Tabla de materiales o definidos PathfindingModifiers y su costo por transitar. Útil para hacer que el agente prefiera ciertos materiales/regiones sobre otros. Consulte modificadores para obtener detalles. | mesa | nil |
Script Local
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentRadius = 3,AgentHeight = 6,AgentCanJump = false,Costs = {Water = 20}})
Nota que o agente pode escalar TrussParts durante a pesquisa de caminho assumindo que você tenha definido AgentCanClimb para true quando criar o caminho 1> e nada bloqueia o agente da subida da ponte. Um cam
LocalScript - Caminho de Escalada Local
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Climb = 2 -- Custo do caminho de escalada; padrão é 1}})
Movendo ao longo de caminhos
Esta seção usa o seguinte script de otimização de caminho para o personagem do jogador. Para testar enquanto lê:
- Copie o código em um LocalScript dentro de StarterCharacterScripts .
- Edite a linha 11 para um destino Vector3 que o personagem do jogador pode alcançar.
- Prossiga pelas seções a seguir para aprender sobre a cálculo de caminhos e o movimento de personagens.
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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)
-- Computar o caminho
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenha os pontos de caminho
waypoints = path:GetWaypoints()
-- Detecte se o caminho ficar bloqueado
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Verifique se o obstáculo está mais abaixo do caminho
if blockedWaypointIndex >= nextWaypointIndex then
-- Pare de detectar bloqueio de caminho até que o caminho seja recontado
blockedConnection:Disconnect()
-- Chame a função para re-calcular o novo caminho
followPath(destination)
end
end)
-- Detecte quando o movimento para o próximo ponto de destino estiver completo
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Aumente o índice de ponto de interrogação e mova-se para o próximo ponto de interrogação
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Inicialmente, vá para o segundo ponto de interesse (o primeiro ponto de interesse é o iniciardo caminho; pule-o)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
followPath(TEST_DESTINATION)
Calculando o Caminho
Depois de criar um caminho válido com CreatePath(), ele deve ser calculado ao chamar Path:ComputeAsync() com um 1> Datatype.Vector31> para ambos o ponto de partida e o destino.
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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)
-- Computar o caminho
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
end
Obtendo Waypoints
Uma vez que o Path é calculado, ele contém uma série de pontos de interrupção que rastreiam o caminho do início ao terminar/parar/sair. Esses pontos podem ser coletados com a função Path:GetWaypoints().
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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)
-- Computar o caminho
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenha os pontos de caminho
waypoints = path:GetWaypoints()
end
end
Movimento de Caminho
Cada ponto de interesse consiste em ambos um posição (Vector3 ) e um ação (1> Class.PathWaypointAction|Pass
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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)
-- Computar o caminho
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenha os pontos de caminho
waypoints = path:GetWaypoints()
-- Detecte se o caminho ficar bloqueado
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Verifique se o obstáculo está mais abaixo do caminho
if blockedWaypointIndex >= nextWaypointIndex then
-- Pare de detectar bloqueio de caminho até que o caminho seja recontado
blockedConnection:Disconnect()
-- Chame a função para re-calcular o novo caminho
followPath(destination)
end
end)
-- Detecte quando o movimento para o próximo ponto de destino estiver completo
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Aumente o índice de ponto de interrogação e mova-se para o próximo ponto de interrogação
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Inicialmente, vá para o segundo ponto de interesse (o primeiro ponto de interesse é o iniciardo caminho; pule-o)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
Manuseio de Caminhos Bloqueados
Muitos mundos do Roblox são dinâmicos; peças podem se mover ou cair e pisos podem colapsar. Isso pode bloquear um caminho calculado e impedir o personagem de alcançar seu destino. Para lidar com isso, você pode conectar o evento Path.Blocked e recompilar o caminho ao redor de qualquer coisa bloqueada.
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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)
-- Computar o caminho
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Obtenha os pontos de caminho
waypoints = path:GetWaypoints()
-- Detecte se o caminho ficar bloqueado
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Verifique se o obstáculo está mais abaixo do caminho
if blockedWaypointIndex >= nextWaypointIndex then
-- Pare de detectar bloqueio de caminho até que o caminho seja recontado
blockedConnection:Disconnect()
-- Chame a função para re-calcular o novo caminho
followPath(destination)
end
end)
end
end
Modificadores de Caminhos
Por padrão, Path:ComputeAsync() retorna o caminho mais curto entre o ponto de partida e o destino, com a exceção de que ele tenta evitar pulos. Isso parece fora do natural em algumas situações, por instância, um caminho pode ir através da água, em vez de sobre uma ponte próxima, simplesmente porque o caminho através da água é geometricamente mais curto.
Para otimizar ainda mais a pesquisa de caminhos, você pode implementar modificadores de caminhos para calcular caminhos mais inteligentes em vários materiais, em torno de regiões definidas ou através de 1>obstáculos1>.
Configurando Custos de Materiais
Ao trabalhar com Terrain e BasePart materiais, você pode incluir uma tabela de Costs dentro de 1> Class.PathfindingService:CreatePath()|CreatePath()1> para tornar alguns materiais mais
As chaves na tabela Costs devem ser nomes de string representando nomes Enum.Material, por exemplo, Water para 1> Nem.Material.Water1>.
LocalScript - Localizando Caminhos de Personagens
local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {Water = 20,Mud = 5,Neon = math.huge}})
Trabalhando com Regiões
Em alguns casos, preferência de material não é suficiente. Por exemplo, você pode querer que os personagens evitem uma região definida , independentemente dos materiais sob os pés. Isso pode ser feito adicionando um objeto PathfindingModifier a uma peça.
Crie uma peça Anchored ao redor da região perigosa e configure sua propriedade CanCollide para falso .
Insira uma instância de PathfindingModifier no parça, localize sua propriedade Label e atribua um nome significativo, como DangerZone .
Inclua uma tabela Costs dentro de CreatePath() contendo uma chave correspondente e um valor numérico associado. Um modificador pode ser definido como não trável definindo seu valor para math.huge.
LocalScript - Localizando Caminhos de Personagenslocal PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {DangerZone = math.huge}})
Ignorando Obstáculos
Em alguns casos, é útil encontrar caminhos através de obstáculos sólidos como se eles não existissem. Isso permite que você compreenda um caminho através de blocos físicos específicos, em vez da falha de cálculo.
Crie uma peça Anchored ao redor do objeto e configure sua propriedade CanCollide para falso .
Insira uma instância PathfindingModifier na peça e ative sua propriedade PassThrough.
Agora, quando um caminho é calculado a partir do NPC zumbi para o personagem do jogador, o caminho se estende além da porta e você pode solicitar que o zumbi atravesse. Mesmo que o zumbi não consiga abrir a porta, ele reage como se estivesse ouvindo o personagem atrás da porta.
Encontrando caminhos
Às vezes, é necessário encontrar um caminho através de um espaço que não pode ser normalmente atravessado, como através de um abismo, e executar uma ação personalizada para alcançar o próximo ponto de destino. Isso pode ser feito através do ObjetoPathfindingLink.
Usando o exemplo da ilha acima, você pode fazer com que o agente use um barco em vez de caminhar por todos os pontes.
Para criar um PathfindingLink usando este exemplo:
Para ajudar com a visualização e depuração, ative Pathfinding links nas configurações de widget Opções de Visualização no canto superior direito da janela de visualização 3D.
Crie dois Attachments , um na parte de baixo do assento e um perto do ponto de pouso do barco.
Crie um objeto PathfindingLink no espaço de trabalho, então atribua suas propriedades Ata0 e Ata1 às propriedades de anexos de partida e de saída respectivamente.
Atribua um nome significativo como UseBoat à sua propriedade Label. Este nome é usado como uma bandeira no script de caminho para iniciar uma ação personalizada quando o agente atinge o ponto de partida.
Inclua uma tabela Costs dentro de CreatePath() contendo tanto uma chave Water quanto uma chave personalizada correspondendo ao nome de propriedade 1> Class.PathfindingLink.Label|Label1>. Atribua a chave personalizada um valor inferior a 4> Water 4> .
LocalScript - Localizando Caminhos de Personagenslocal PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {Water = 20,UseBoat = 1}})No evento que ocorre quando um waypoint é alcançado, adicione um cheque personalizado para o nome do modificador Label e tome uma ação diferente do que Humanoid:MoveTo() — neste caso, chamando uma função para sentar o agente no barco, mover o barco através da água e continuar o caminho do agente ao chegar na ilha de destino.
LocalScript - Localizando Caminhos de Personagenslocal PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {Water = 20,UseBoat = 1}})local player = Players.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)local waypointslocal nextWaypointIndexlocal reachedConnectionlocal blockedConnectionlocal function followPath(destination)-- Computar o caminholocal success, errorMessage = pcall(function()path:ComputeAsync(character.PrimaryPart.Position, destination)end)if success and path.Status == Enum.PathStatus.Success then-- Obtenha os pontos de caminhowaypoints = path:GetWaypoints()-- Detecte se o caminho ficar bloqueadoblockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)-- Verifique se o obstáculo está mais abaixo do caminhoif blockedWaypointIndex >= nextWaypointIndex then-- Pare de detectar bloqueio de caminho até que o caminho seja recontadoblockedConnection:Disconnect()-- Chame a função para re-calcular o novo caminhofollowPath(destination)endend)-- Detecte quando o movimento para o próximo ponto de destino estiver completoif not reachedConnection thenreachedConnection = humanoid.MoveToFinished:Connect(function(reached)if reached and nextWaypointIndex < #waypoints then-- Aumente o índice de ponto de interrogação e mova-se para o próximo ponto de interrogaçãonextWaypointIndex += 1-- Use o barco se a etiqueta de ponto de interesse é "UseBarco"; caso contrário, mova-se para o próximo ponto de interesseif waypoints[nextWaypointIndex].Label == "UseBoat" thenuseBoat()elsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endelsereachedConnection:Disconnect()blockedConnection:Disconnect()endend)end-- Inicialmente, vá para o segundo ponto de interesse (o primeiro ponto de interesse é o iniciardo caminho; pule-o)nextWaypointIndex = 2humanoid:MoveTo(waypoints[nextWaypointIndex].Position)elsewarn("Path not computed!", errorMessage)endendfunction useBoat()local boat = workspace.BoatModelhumanoid.Seated:Connect(function()-- Iniciar o movimento do barco se o agente estiver sentadoif humanoid.Sit thentask.wait(1)boat.CylindricalConstraint.Velocity = 5end-- Detectar a posição do limite em relação à ilhalocal boatPositionConnectionboatPositionConnection = RunService.PostSimulation:Connect(function()-- Pare o barco ao lado da ilhaif boat.CylindricalConstraint.CurrentPosition >= 94 thenboatPositionConnection:Disconnect()boat.CylindricalConstraint.Velocity = 0task.wait(1)-- Desmarcar o agente e continuar para o destinohumanoid.Sit = falsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endend)end)endfollowPath(TEST_DESTINATION)
Compatibilidade de streaming
In-experience stream de instância de vídeo é uma característica poderosa que carrega e descarrega 3D conteúdo como um personagem do jogador se move ao redor do mundo. À medida que eles exploram o espaço 3D, novos subsets do espaço stream para o seu dispositivo e alguns dos subsets existentes podem stream out.
Considere as seguintes melhores práticas para usar PathfindingService em experiências habilitadas para streaming:
A transmissão pode bloquear ou desbloquear um caminho dado enquanto um personagem se move ao longo dele. Por exemplo, enquanto um personagem corre através de uma floresta, uma árvore pode stream em algum lugar à frente deles e obstruir o caminho. Para fazer o tratamento de caminhos bloqueados com transmissão, é altamente recomendado que você use a técnica Handling Blocked Paths e re-计算 o caminho quando necessário.
Um abordagem comum em otimização de caminhos é usar as coordenadas de objetos existentes para cálculo, como definir um destino de caminho para a posição de um modelo de TesouroChest existente no
Para abordar este problema, considere definir o destino para a posição de um BasePart dentro de um modelo persistente dentro de um modelo PersistentLoaded. Modelos persistentes carregam imediatamente após o jogador se juntar e nunca são streamed, para que um script do lado do cliente possa se conectar ao evento 2> Class.Workspace.PersistentLoaded|PersistentLoaded2>