Navigazione

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

Navigazione è il processo di spostamento di un personaggio o oggetto (agente) lungo un percorso logico attorno agli ostacoli per raggiungere una destinazione, evitando opzionalmente materiali pericolosi o regioni definite.

Visualizzazione della navigazione

Per assistere con il layout e il debug della navigazione, Studio può renderizzare una mesh di navigazione e etichette di modificatori. Per abilitarli, attiva Mesh di navigazione e Modificatori di navigazione dal widget Opzioni di visualizzazione nell'angolo in alto a destra della vista 3D.

Una vista ravvicinata della vista 3D con il pulsante Opzioni di visualizzazione indicato nell'angolo in alto a destra.

Con Mesh di navigazione abilitata, le aree colorate mostrano dove un personaggio potrebbe camminare o nuotare. Piccole frecce indicano le aree che un personaggio tenterà di raggiungere saltando.

Mesh di navigazione mostrata in Studio

Implementazione

Sebbene la navigazione possa essere implementata in vari modi attraverso PathfindingService e i suoi metodi associati come CreatePath(), questa sezione utilizza il seguente script di navigazione per il personaggio del giocatore.

Per testare mentre leggi:

  1. IMPORTANTE
    Nel Esplora, seleziona il contenitore StarterPlayer. Quindi, nella finestra Proprietà, imposta sia DevComputerMovementMode che DevTouchMovementMode su Scriptable.

  2. Copia il seguente codice in un LocalScript all'interno di StarterCharacterScripts, oppure ottieni questo pacchetto e inseriscilo in StarterCharacterScripts.

    PlayerPathFollow (LocalScript in StarterCharacterScripts)
    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINAZIONE = Vector3.new(20, 0.5, 20)
    local ATTESA_GROUND = 0.01
    local MOLTIPLICATORE_VELOCITÀ = 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
    -- Calcola la distanza dall'umanoide al piano di destinazione
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Calcola la componente della velocità dell'umanoide che è verso il piano
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Calcola la soglia dal piano di destinazione in base alla velocità dell'umanoide
    local threshold = math.max(1.0, MOLTIPLICATORE_VELOCITÀ * velocity)
    -- Considera il waypoint raggiunto se meno della soglia davanti al piano
    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]
    -- Costruisci il piano di destinazione dal prossimo waypoint verso quello attuale
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Imposta la normale perpendicolare al piano Y quando non si sta salendo
    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(ATTESA_GROUND)
    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
    -- Chiama la tua funzione personalizzata per far usare la barca all'agente
    elseif nextWaypointAction == Enum.PathWaypointAction.Jump then
    humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    while humanoid.FloorMaterial ~= Enum.Material.Air do
    task.wait(ATTESA_GROUND)
    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()
    -- Calcola il percorso
    pathfinderWorking = true
    waitForGround()
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, DESTINAZIONE)
    end)
    if not success or path.Status ~= Enum.PathStatus.Success then
    warn("Percorso non calcolato!", errorMessage)
    return
    end
    -- Ottieni i waypoint del percorso
    waypoints = path:GetWaypoints()
    -- Rileva se il percorso diventa bloccato
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Controlla se l'ostacolo è più avanti lungo il percorso
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Ferma il rilevamento del blocco del percorso fino a quando il percorso non viene ricalcolato
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Chiama la funzione per ricalcolare un nuovo percorso
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Modifica la variabile DESTINAZIONE (

    LINE 5
    ) in una destinazione di tipo Vector3 all'interno del mondo 3D che il personaggio del giocatore può raggiungere.

  4. Procedi attraverso le seguenti sezioni per apprendere il calcolo del percorso e il movimento del personaggio.

Creazione del percorso

La navigazione viene avviata attraverso PathfindingService e il suo metodo CreatePath() (

LINES 9–14
). Questo metodo accetta una tabella di parametri opzionali che affinano come il personaggio (agente) si muove lungo il percorso.

ChiaveDescrizioneTipoPredefinito
AgentRadiusRaggio dell'agente, in stud. Utile per determinare la separazione minima dagli ostacoli.intero2
AgentHeightAltezza dell'agente, in stud. Spazio vuoto più piccolo di questo valore, come lo spazio sotto le scale, sarà contrassegnato come non attraversabile.intero5
AgentCanJumpDetermina se il salto durante la navigazione è consentito.booleanotrue
AgentCanClimbDetermina se arrampicarsi su TrussParts durante la navigazione è consentito. Un percorso arrampicabile ha un'etichetta Label chiamata Climb e il costo per un percorso arrampicabile è 1 per impostazione predefinita.booleanofalse
WaypointSpacingDistanza tra i waypoint intermedi nel percorso. Se impostato su math.huge, non ci saranno waypoint intermedi.numero4
CostsTabella di materiali o PathfindingModifiers definiti e il loro costo per la traversata. Utile per far preferire all'agente determinati materiali/regioni rispetto ad altri. Vedi modificatori per dettagli.tabellanil

Calcolo del percorso

Dopo aver creato un percorso valido con CreatePath(), deve essere calcolato chiamando Path:ComputeAsync() con un Vector3 sia per il punto di partenza che per la destinazione (

LINES 133–139
).

Inizio/fine del percorso contrassegnato attraverso due ponti

Una volta che il Path è calcolato, conterrà una serie di waypoints che tracciano il percorso dall'inizio alla fine. Questi punti possono essere raccolti con il metodo Path:GetWaypoints() (

LINE 142
). L'array restituito è ordinato in base ai waypoint dall'inizio del percorso alla fine del percorso.

Waypoints indicati lungo il percorso calcolato
Waypoints indicati lungo il percorso calcolato

Movimento lungo il percorso

Ogni PathWaypoint consiste sia in una Position (Vector3) che in un Action (PathWaypointAction). Per muovere un personaggio contenente un Humanoid, come un tipico personaggio di Roblox, il modo migliore è chiamare Humanoid:Move() da waypoint a waypoint e utilizzare il callback isCurrentWaypointReached() dello script (

LINES 32–56
) per rilevare quando il personaggio raggiunge ciascun waypoint.

Percorsi bloccati

Molti mondi di Roblox sono dinamici; le parti potrebbero muoversi o cadere e i pavimenti potrebbero crollare. Questo può bloccare un percorso calcolato e impedire al personaggio di raggiungere la sua destinazione. Per gestire questo, puoi connettere l'evento Path.Blocked e ricalcolare il percorso attorno a ciò che lo ha bloccato (

LINES 145–154
).

Modificatori di navigazione

Per impostazione predefinita, Path:ComputeAsync() restituisce il percorso più breve tra il punto di partenza e la destinazione, con l'eccezione che cerca di evitare i salti. Questo appare innaturale in alcune situazioni; ad esempio, un percorso potrebbe attraversare acqua paludosa piuttosto che attorno ad essa semplicemente perché il percorso attraverso l'acqua è geometricamente più corto.

Due percorsi indicati con il percorso più corto non necessariamente più logico

Per ottimizzare ulteriormente la navigazione, puoi implementare modificatori di navigazione per calcolare percorsi più intelligenti attraverso vari materiali, attorno a regioni definite, o per ignorare ostacoli.

Costi dei materiali

Quando lavori con i materiali di Terrain e BasePart, puoi includere una tabella Costs all'interno di CreatePath() per rendere alcuni materiali più attraversabili di altri. Tutti i materiali hanno un costo predefinito di 1 e qualsiasi materiale può essere definito come non attraversabile impostando il suo valore su math.huge.

Le chiavi nella tabella Costs dovrebbero essere nomi stringa che rappresentano i nomi di Enum.Material, ad esempio Water per Enum.Material.Water o CrackedLava per Enum.Material.CrackedLava.

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

Configura regioni

In alcuni casi, la preferenza per i materiali non è sufficiente. Ad esempio, potresti voler evitare a un personaggio di entrare in una regione definita, indipendentemente dai materiali sottostanti. Questo può essere ottenuto aggiungendo un oggetto PathfindingModifier a una parte.

  1. Crea una parte Anchored attorno alla regione e imposta la sua proprietà CanCollide su false.

    Parte ancorata che definisce una regione a cui applicare un modificatore di navigazione.
  2. Inserisci un'istanza di PathfindingModifier sulla parte, trova la sua proprietà Label e assegna un nome significativo come DangerZone.

    Istanza di PathfindingModifier con proprietà Label impostata su DangerZone.
  3. Includi una chiave DangerZone corrispondente e un valore numerico associato all'interno della tabella Costs di CreatePath(). Un modificatore può essere definito come non attraversabile impostando il suo valore su math.huge.

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

Ignora ostacoli

In alcuni casi, è utile navigare attraverso ostacoli solidi come se non esistessero. Questo ti consente di calcolare un percorso attraverso specifici blocchi fisici, invece di far fallire il calcolo.

  1. Crea una parte Anchored attorno all'oggetto e imposta la sua proprietà CanCollide su false.

    Parte ancorata che definisce una regione a cui applicare un modificatore di navigazione.
  2. Inserisci un'istanza di PathfindingModifier sulla parte e abilita la sua proprietà PassThrough.

    Istanza di PathfindingModifier con proprietà PassThrough abilitata.

    Ora, quando viene calcolato un percorso dal NPC zombie al personaggio del giocatore, il percorso si estende oltre la porta e puoi invitare lo zombie a attraversarla. Anche se lo zombie non è in grado di aprire la porta, reagisce come se "sentisse" il personaggio dietro la porta.

    Percorso dell'NPC zombie che passa attraverso la porta precedentemente bloccante.

A volte è necessario trovare un percorso attraverso uno spazio che non può essere normalmente attraversato, come attraverso un baratro, e eseguire un'azione personalizzata per raggiungere il prossimo waypoint. Questo può essere ottenuto attraverso l'oggetto PathfindingLink.

Utilizzando l'esempio di cui sopra, puoi far usare una barca all'agente.

PathfindingLink che mostra come un agente può usare una barca.

Per creare un PathfindingLink utilizzando questo esempio:

  1. OPZIONALE
    Attiva Link di navigazione dal widget Opzioni di visualizzazione nell'angolo in alto a destra della vista 3D. Questo aiuta con la visualizzazione e il debug quando si implementano i link di navigazione.

  2. Crea due Attachments, uno sul sedile della barca e uno vicino al punto di atterraggio della barca.

    Attacchi creati per l'inizio e la fine del link di navigazione.
  3. Crea un oggetto PathfindingLink nello spazio di lavoro, quindi assegna le sue proprietà Attachment0 e Attachment1 agli attacchi di partenza e di arrivo rispettivamente.

    Proprietà Attachment0/Attachment1 di un PathfindingLink. PathfindingLink visualizzato nel mondo 3D.
  4. Assegna un nome significativo come UseBoat alla sua proprietà Label. Questo nome viene utilizzato come flag nello script di navigazione per attivare un'azione personalizzata quando l'agente raggiunge il punto di inizio del link.

    Proprietà Label specificata per PathfindingLink.
  5. Includi una tabella Costs all'interno di CreatePath() contenente sia una chiave Water che una chiave personalizzata che corrisponde al nome della proprietà Label. Assegna alla chiave personalizzata un valore inferiore a quello di Water.

    PlayerPathFollow (LocalScript)
    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINAZIONE = Vector3.new(20, 0.5, 20)
    local ATTESA_GROUND = 0.01
    local MOLTIPLICATORE_VELOCITÀ = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    UseBoat = 2, Water = 20
    }
    })
  6. Nella funzione moveToNextWaypoint() (

    LINES 93–114
    ), un controllo personalizzato per il nome del modificatore Label può essere utilizzato per eseguire un'azione diversa da Humanoid:Move(); in questo caso, potresti chiamare una funzione per sedere l'agente nella barca, muovere la barca attraverso l'acqua, dissedere l'agente al punto di atterraggio della barca e poi continuare il percorso dell'agente verso la sua destinazione finale.

Compatibilità con lo streaming

Lo streaming delle istanze in gioco è una potente funzionalità che carica e scarica dinamicamente contenuti 3D mentre il personaggio di un giocatore si muove nel mondo. Mentre esplorano lo spazio 3D, nuovi sottoinsiemi dello spazio vengono trasmessi al loro dispositivo e alcuni dei sottoinsiemi esistenti potrebbero essere scaricati.

Considera le seguenti migliori pratiche per utilizzare PathfindingService nei giochi abilitati per lo streaming:

  • Lo streaming può bloccare o sbloccare un dato percorso mentre un personaggio si muove lungo di esso. Ad esempio, mentre un personaggio corre attraverso una foresta, un albero potrebbe essere trasmesso in un punto davanti a loro e ostruire il percorso. Per far funzionare la navigazione senza problemi con lo streaming, è altamente consigliato utilizzare la tecnica gestione dei percorsi bloccati e ricalcolare il percorso quando necessario.

  • Un approccio comune nella navigazione è utilizzare le coordinate degli oggetti esistenti per il calcolo, come impostare una destinazione del percorso sulla posizione di un modello TreasureChest esistente nel mondo. Questo approccio è completamente compatibile con gli Scripts lato server poiché il server ha una visione completa del mondo in ogni momento, ma gli LocalScripts e ModuleScripts che vengono eseguiti sul client potrebbero fallire se tentano di calcolare un percorso verso un oggetto che non è stato trasmesso.

    Per affrontare questo problema, considera di impostare la destinazione sulla posizione di una BasePart all'interno di un modello persistente. I modelli persistenti vengono caricati subito dopo l'ingresso del giocatore e non vengono mai scaricati, quindi uno script lato client può connettersi all'evento PersistentLoaded e accedere in sicurezza al modello per creare waypoint dopo che l'evento è stato attivato.

Limitazioni e fattori di fallimento

Il motore di navigazione include limitazioni specifiche per garantire un'elaborazione efficiente e prestazioni ottimali. Inoltre, i calcoli di navigazione possono fallire per vari motivi come delineato di seguito.

© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.