Pathfinding de Personagens

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

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.

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

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

Navigation mesh showing in Studio

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

Navigation labels showing on navigation mesh

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.

ClaveDescripciónTipoPor defecto
AgentRadiusRaíz del agente, en studs. Útil para determinar la separación mínima de los obstáculos.entero2
AgentHeightAltura 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.entero5
AgentCanJumpDetermina si se permite saltar durante el cálculo del camino.booleanotrue
AgentCanClimbDetermina si se permite escalar TrussParts durante la búsqueda de caminos.booleanofalse
WaypointSpacingEspaciamiento entre los puntos de intermedio en el camino. Si se establece en math.huge , no habrá puntos de intermedio.número4
CostsTabla 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.mesanil
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

Path going up a climbable TrussPart ladder
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ê:

  1. Copie o código em um LocalScript dentro de StarterCharacterScripts .
  2. Edite a linha 11 para um destino Vector3 que o personagem do jogador pode alcançar.
  3. 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
Path start/end marked on series of islands and bridges

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
Waypoints indicated across computed path
Waypoints indicados em caminho calculado

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.

Two paths indicated with the shorter path not necessarily more logical

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.

  1. Crie uma peça Anchored ao redor da região perigosa e configure sua propriedade CanCollide para falso .

    Anchored part defining a region to apply a pathfinding modifier to
  2. Insira uma instância de PathfindingModifier no parça, localize sua propriedade Label e atribua um nome significativo, como DangerZone .

    PathfindingModifier instance with Label property set to DangerZone
  3. 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 Personagens

    local 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.

  1. Crie uma peça Anchored ao redor do objeto e configure sua propriedade CanCollide para falso .

    Anchored part defining a region to apply a pathfinding modifier to
  2. Insira uma instância PathfindingModifier na peça e ative sua propriedade PassThrough.

    PathfindingModifier instance with PassThrough property enabled

    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.

    Zombie NPC path passing through the previously blocking door

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.

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

Para criar um PathfindingLink usando este exemplo:

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

  2. Crie dois Attachments , um na parte de baixo do assento e um perto do ponto de pouso do barco.

    Attachments created for pathfinding link's start and end
  3. 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.

    Attachment0/Attachment1 properties of a PathfindingLink PathfindingLink visualized in the 3D world
  4. 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.

    Label property specified for PathfindingLink
  5. 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 Personagens

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local path = PathfindingService:CreatePath({
    Costs = {
    Water = 20,
    UseBoat = 1
    }
    })
  6. 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 Personagens

    local 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.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)
    -- 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
    -- Use o barco se a etiqueta de ponto de interesse é "UseBarco"; caso contrário, mova-se para o próximo ponto de interesse
    if waypoints[nextWaypointIndex].Label == "UseBoat" then
    useBoat()
    else
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    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
    function useBoat()
    local boat = workspace.BoatModel
    humanoid.Seated:Connect(function()
    -- Iniciar o movimento do barco se o agente estiver sentado
    if humanoid.Sit then
    task.wait(1)
    boat.CylindricalConstraint.Velocity = 5
    end
    -- Detectar a posição do limite em relação à ilha
    local boatPositionConnection
    boatPositionConnection = RunService.PostSimulation:Connect(function()
    -- Pare o barco ao lado da ilha
    if boat.CylindricalConstraint.CurrentPosition >= 94 then
    boatPositionConnection:Disconnect()
    boat.CylindricalConstraint.Velocity = 0
    task.wait(1)
    -- Desmarcar o agente e continuar para o destino
    humanoid.Sit = false
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    end)
    end)
    end
    followPath(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>