Búsqueda de caminos

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Búsqueda de caminos es el proceso de mover un personaje u objeto (agente) a lo largo de un camino lógico alrededor de obstáculos para alcanzar un destino, evitando opcionalmente materiales peligrosos o regiones definidas.

Visualización de navegación

Para ayudar con el diseño y la depuración de la búsqueda de caminos, Studio puede renderizar una malla de navegación y etiquetas de modificadores. Para habilitarlas, activa Malla de navegación y Modificadores de búsqueda de caminos desde el widget de Opciones de visualización en la esquina superior derecha de la vista 3D.

Una vista cercana de la vista 3D con el botón de Opciones de visualización indicado en la esquina superior derecha.

Con Malla de navegación habilitada, las áreas coloreadas muestran dónde un personaje podría caminar o nadar. Flechas pequeñas indican áreas que un personaje intentará alcanzar saltando.

Malla de navegación mostrando en Studio

Implementación

Aunque la búsqueda de caminos se puede implementar de varias maneras a través de PathfindingService y sus métodos asociados como CreatePath(), esta sección utiliza el siguiente script de búsqueda de caminos para el personaje del jugador.

Para probar mientras lees:

  1. IMPORTANTE
    En el Explorador, selecciona el contenedor StarterPlayer. Luego, en la ventana de Propiedades, establece tanto DevComputerMovementMode como DevTouchMovementMode en Scriptable.

  2. Copia el siguiente código en un LocalScript dentro de StarterCharacterScripts, o obtén este paquete y colócalo en StarterCharacterScripts.

    PlayerPathFollow (LocalScript en StarterCharacterScripts)
    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINO = Vector3.new(20, 0.5, 20)
    local ESPERA_SUELO = 0.01
    local MULTIPLICADOR_VELOCIDAD = 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
    -- Calcular la distancia desde el humanoide hasta el plano de destino
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Calcular el componente de la velocidad del humanoide que está hacia el plano
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Calcular el umbral desde el plano de destino basado en la velocidad del humanoide
    local threshold = math.max(1.0, MULTIPLICADOR_VELOCIDAD * velocity)
    -- Considerar el waypoint alcanzado si es menor que el umbral frente al plano
    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]
    -- Construir el plano de destino desde el siguiente waypoint hacia el actual
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Establecer la normal perpendicular al plano Y cuando no se está subiendo
    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(ESPERA_SUELO)
    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
    -- Llama a tu propia función personalizada para hacer que el agente use el barco
    elseif nextWaypointAction == Enum.PathWaypointAction.Jump then
    humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    while humanoid.FloorMaterial ~= Enum.Material.Air do
    task.wait(ESPERA_SUELO)
    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()
    -- Calcular el camino
    pathfinderWorking = true
    waitForGround()
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, DESTINO)
    end)
    if not success or path.Status ~= Enum.PathStatus.Success then
    warn("¡Camino no computado!", errorMessage)
    return
    end
    -- Obtener los waypoints del camino
    waypoints = path:GetWaypoints()
    -- Detectar si el camino se bloquea
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Verificar si el obstáculo está más adelante en el camino
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Dejar de detectar el bloqueo del camino hasta que se vuelva a calcular
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Llama a la función para volver a calcular un nuevo camino
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Edita la variable DESTINO (

    LÍNEA 5
    ) a un destino de Vector3 dentro del mundo 3D al que el personaje jugador pueda llegar.

  4. Procede a través de las siguientes secciones para aprender sobre la computación de caminos y el movimiento del personaje.

Creación de caminos

La búsqueda de caminos se inicia a través de PathfindingService y su método CreatePath() (

LÍNEAS 9–14
). Este método acepta una tabla opcional de parámetros que ajustan cómo se mueve el personaje (agente) a lo largo del camino.

ClaveDescripciónTipoPredeterminado
AgentRadiusRadio del agente, en studs. Útil para determinar la separación mínima de los obstáculos.entero2
AgentHeightAltura del agente, en studs. El 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 la búsqueda de caminos.booleanotrue
AgentCanClimbDetermina si se permite escalar TrussParts durante la búsqueda de caminos. Un camino escalable tiene una Label llamada Climb y el costo para un camino escalable es 1 por defecto.booleanofalse
WaypointSpacingEspaciado entre waypoints intermedios en el camino. Si se establece en math.huge, no habrá waypoints intermedios.número4
CostsTabla de materiales o PathfindingModifiers definidos y su costo de tránsito. Útil para hacer que el agente prefiera ciertos materiales/regiones sobre otros. Consulta modificadores para más detalles.tablanil

Computación de caminos

Después de haber creado un camino válido con CreatePath(), debe ser computado llamando a Path:ComputeAsync() con un Vector3 tanto para el punto de partida como para el destino (

LÍNEAS 133–139
).

Inicio/final del camino marcado a través de dos puentes

Una vez que se computa el Path, contendrá una serie de waypoints que trazan el camino de inicio a fin. Estos puntos se pueden reunir con el método Path:GetWaypoints() (

LÍNEA 142
). El array devuelto está ordenado en el orden de los waypoints desde el inicio del camino hasta el final del camino.

Waypoints indicados a lo largo del camino computado
Waypoints indicados a lo largo del camino computado

Movimiento por el camino

Cada PathWaypoint consiste en una Position (Vector3) y una Action (PathWaypointAction). Para mover un personaje que contenga un Humanoid, como un personaje típico de Roblox, la mejor manera es llamar a Humanoid:Move() de waypoint a waypoint y usar el callback isCurrentWaypointReached() del script (

LÍNEAS 32–56
) para detectar cuándo el personaje alcanza cada waypoint.

Caminos bloqueados

Muchos mundos de Roblox son dinámicos; las partes pueden moverse o caer y los pisos pueden colapsar. Esto puede bloquear un camino computado y evitar que el personaje alcance su destino. Para manejar esto, puedes conectar el evento Path.Blocked y volver a computar el camino alrededor de lo que lo bloqueó (

LÍNEAS 145–154
).

Modificadores de búsqueda de caminos

Por defecto, Path:ComputeAsync() devuelve el camino más corto entre el punto de partida y el destino, con la excepción de que intenta evitar saltos. Esto parece antinatural en algunas situaciones; por ejemplo, un camino puede pasar a través de agua pantanosa en lugar de rodearla simplemente porque el camino a través del agua es geométricamente más corto.

Dos caminos indicados con el camino más corto no necesariamente más lógico

Para optimizar aún más la búsqueda de caminos, puedes implementar modificadores de búsqueda de caminos para calcular caminos más inteligentes a través de varios materiales, alrededor de regiones definidas, o para ignorar obstáculos.

Costos de materiales

Al trabajar con materiales de Terrain y BasePart, puedes incluir una tabla de Costs dentro de CreatePath() para hacer que ciertos materiales sean más transitables que otros. Todos los materiales tienen un costo predeterminado de 1 y cualquier material puede definirse como no transitable estableciendo su valor en math.huge.

Las claves en la tabla Costs deben ser nombres de cadena que representan nombres de Enum.Material, por ejemplo Water para Enum.Material.Water o CrackedLava para Enum.Material.CrackedLava.

PlayerPathFollow (LocalScript)
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DESTINO = Vector3.new(20, 0.5, 20)
local ESPERA_SUELO = 0.01
local MULTIPLICADOR_VELOCIDAD = 0.0625
local path = PathfindingService:CreatePath({
AgentCanClimb = true,
Costs = {
Water = 20, CrackedLava = 100, Slate = 20
}
})

Configurar regiones

En algunos casos, la preferencia de materiales no es suficiente. Por ejemplo, es posible que desees que los personajes eviten una región definida, independientemente de los materiales bajo sus pies. Esto se puede lograr agregando un objeto PathfindingModifier a una parte.

  1. Crea una parte Anchored alrededor de la región y establece su propiedad CanCollide en false.

    Parte anclada definiendo una región para aplicar un modificador de búsqueda de caminos.
  2. Inserta una instancia de PathfindingModifier en la parte, localiza su propiedad Label, y asigna un nombre significativo como DangerZone.

    Instancia de PathfindingModifier con la propiedad Label establecida en DangerZone.
  3. Incluye una clave DangerZone coincidente y un valor numérico asociado dentro de la tabla Costs de CreatePath(). Un modificador puede definirse como no transitable estableciendo su valor en math.huge.

    PlayerPathFollow (LocalScript)
    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINO = Vector3.new(20, 0.5, 20)
    local ESPERA_SUELO = 0.01
    local MULTIPLICADOR_VELOCIDAD = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    DangerZone = math.huge, Water = 20, CrackedLava = 20, Slate = 20
    }
    })

Ignorar obstáculos

En algunos casos, es útil buscar caminos a través de obstáculos sólidos como si no existieran. Esto te permite calcular un camino a través de bloqueadores físicos específicos, en lugar de que la computación falle por completo.

  1. Crea una parte Anchored alrededor del objeto y establece su propiedad CanCollide en false.

    Parte anclada definiendo una región para aplicar un modificador de búsqueda de caminos.
  2. Inserta una instancia de PathfindingModifier en la parte y habilita su propiedad PassThrough.

    Instancia de PathfindingModifier con la propiedad PassThrough habilitada.

    Ahora, cuando se compute un camino desde el NPC zombie hasta el personaje jugador, el camino se extiende más allá de la puerta y puedes hacer que el zombie lo atraviese. Incluso si el zombie no puede abrir la puerta, reacciona como si "oyera" al personaje detrás de la puerta.

    Camino del NPC zombie pasando a través de la puerta que bloqueaba anteriormente.

Enlaces de búsqueda de caminos

A veces es necesario encontrar un camino a través de un espacio que no se puede atravesar normalmente, como a través de un abismo, y realizar una acción personalizada para alcanzar el siguiente waypoint. Esto se puede lograr a través del objeto PathfindingLink.

Usando el ejemplo anterior, puedes hacer que el agente use un barco.

PathfindingLink mostrando cómo un agente puede usar un barco.

Para crear un PathfindingLink usando este ejemplo:

  1. OPCIONAL
    Activa Enlaces de búsqueda de caminos desde el widget de Opciones de visualización en la esquina superior derecha de la vista 3D. Esto ayuda con la visualización y la depuración al implementar enlaces de búsqueda de caminos.

  2. Crea dos Attachments, uno en el asiento del barco y otro cerca del punto de aterrizaje del barco.

    Adjuntos creados para el inicio y el final del enlace de búsqueda de caminos.
  3. Crea un objeto PathfindingLink en el espacio de trabajo, luego asigna sus propiedades Attachment0 y Attachment1 a los adjuntos de inicio y fin respectivamente.

    Propiedades Attachment0/Attachment1 de un PathfindingLink. PathfindingLink visualizado en el mundo 3D.
  4. Asigna un nombre significativo como UseBoat a su propiedad Label. Este nombre se utiliza como una bandera en el script de búsqueda de caminos para activar una acción personalizada cuando el agente alcanza el punto de inicio del enlace.

    Propiedad Label especificada para PathfindingLink.
  5. Incluye una tabla de Costs dentro de CreatePath() que contenga tanto una clave Water como una clave personalizada que coincida con el nombre de la propiedad Label. Asigna a la clave personalizada un valor menor que el de Water.

    PlayerPathFollow (LocalScript)
    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINO = Vector3.new(20, 0.5, 20)
    local ESPERA_SUELO = 0.01
    local MULTIPLICADOR_VELOCIDAD = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    UseBoat = 2, Water = 20
    }
    })
  6. En la función moveToNextWaypoint() (

    LÍNEAS 93–114
    ), se puede usar una verificación personalizada para el nombre del modificador Label para realizar una acción diferente a Humanoid:Move(); en este caso, podrías llamar a una función para sentar al agente en el barco, mover el barco a través del agua, desasentar al agente en el punto de aterrizaje del barco y luego continuar el camino del agente hacia su destino final.

Compatibilidad con streaming

El streaming de instancias en el juego es una característica poderosa que carga y descarga dinámicamente contenido 3D a medida que el personaje de un jugador se mueve por el mundo. A medida que exploran el espacio 3D, nuevos subconjuntos del espacio se transmiten a su dispositivo y algunos de los subconjuntos existentes pueden transmitirse fuera.

Considera las siguientes mejores prácticas para usar PathfindingService en juegos habilitados para streaming:

  • El streaming puede bloquear o desbloquear un camino dado a medida que un personaje se mueve a lo largo de él. Por ejemplo, mientras un personaje corre a través de un bosque, un árbol puede transmitirse en algún lugar delante de ellos y obstruir el camino. Para que la búsqueda de caminos funcione sin problemas con el streaming, se recomienda encarecidamente que utilices la técnica de manejo de caminos bloqueados y vuelvas a computar el camino cuando sea necesario.

  • Un enfoque común en la búsqueda de caminos es usar las coordenadas de objetos existentes para la computación, como establecer un destino de camino en la posición de un modelo TreasureChest existente en el mundo. Este enfoque es completamente compatible con Scripts del lado del servidor, ya que el servidor tiene una vista completa del mundo en todo momento, pero LocalScripts y ModuleScripts que se ejecutan en el cliente pueden fallar si intentan computar un camino hacia un objeto que no se ha transmitido.

    Para abordar este problema, considera establecer el destino en la posición de una BasePart dentro de un modelo persistente. Los modelos persistentes se cargan poco después de que el jugador se une y nunca se transmiten fuera, por lo que un script del lado del cliente puede conectarse al evento PersistentLoaded y acceder de manera segura al modelo para crear waypoints después de que se dispare el evento.

Limitaciones y factores de fallo

El motor de búsqueda de caminos incluye limitaciones específicas para garantizar un procesamiento eficiente y un rendimiento óptimo. Además, las computaciones de búsqueda de caminos pueden fallar por varias razones, como se describe a continuación.

©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.