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 eventualmente materiali pericolosi o regioni definite.

Visualizzazione della navigazione

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

Una vista ravvicinata della vista 3D con il pulsante delle 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 tramite 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 depositalo in StarterCharacterScripts.

    PlayerPathFollow (LocalScript in StarterCharacterScripts)

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 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 dal humanoide 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, VELOCITY_MULTIPLIER * velocity)
    -- Considera il waypoint raggiunto se meno della soglia di fronte 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(GROUND_WAIT)
    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 utilizzare la barca all'agente
    elseif nextWaypointAction == Enum.PathWaypointAction.Jump then
    humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    while humanoid.FloorMaterial ~= Enum.Material.Air do
    task.wait(GROUND_WAIT)
    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, DESTINATION)
    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
    -- Smetti di rilevare il blocco del percorso fino a quando il percorso non è ricalcolato
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Chiama la funzione per ricalcolare il nuovo percorso
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Modifica la variabile DESTINATION (

    LINE 5
    ) in una destinazione Vector3 all'interno del mondo 3D a cui il personaggio del giocatore può accedere.

  4. Prosegui attraverso le sezioni seguenti per conoscere il calcolo del percorso e il movimento del personaggio.

Creazione del percorso

La navigazione è avviata tramite PathfindingService e il suo metodo CreatePath() (

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

ChiaveDescrizioneTipoDefault
AgentRadiusRaggio dell'agente, in stud. Utile per determinare la separazione minima dagli ostacoli.intero2
AgentHeightAltezza dell'agente, in stud. Spazi vuoti più piccoli di questo valore, come lo spazio sotto le scale, verranno contrassegnati come non attraversabili.intero5
AgentCanJumpDetermina se il salto durante la navigazione è consentito.booleanotrue
AgentCanClimbDetermina se la scalata di TrussParts durante la navigazione è consentita. Un percorso scalabile ha un'etichetta di Label chiamata Climb e il costo per un percorso scalabile è 1 per impostazione predefinita.booleanofalse
WaypointSpacingSpaziatura 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 traversabilità. Utile per far preferire all'agente determinati materiali/regioni rispetto ad altri. Vedi i 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
).

Percorso di inizio/fine contrassegnato attraverso due ponti

Una volta che il Path è calcolato, conterrà una serie di waypoint 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 del percorso

Ogni PathWaypoint è composto sia da una Position (Vector3) e Action (PathWaypointAction). Per muovere un personaggio contenente un Humanoid, come un tipico personaggio 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 Roblox sono dinamici; le parti potrebbero muoversi o cadere e i pavimenti possono 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 blocca (

LINES 145–154
).

Modificatori per la navigazione

Per impostazione predefinita, Path:ComputeAsync() restituisce il percorso più corto tra il punto di partenza e la destinazione, con l'eccezione che tenta di evitare salti. Questo appare innaturale in alcune situazioni; ad esempio, un percorso potrebbe attraversare un'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 per la 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 di Costs all'interno di CreatePath() per rendere certi materiali più facili da attraversare rispetto ad altri. Tutti i materiali hanno un costo predefinito di 1 e qualsiasi materiale può essere definito come non attraversabile impostando il suo valore a math.huge.

Le chiavi nella tabella Costs dovrebbero essere nomi di 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 DESTINATION = Vector3.new(20, 0.5, 20)
local GROUND_WAIT = 0.01
local VELOCITY_MULTIPLIER = 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 attraversare una regione definita, indipendentemente dai materiali sottostanti. Questo può essere realizzato 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 DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 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 permette di calcolare un percorso attraverso specifici blocchi fisici, invece di far fallire direttamente 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 dall'NPC zombie al personaggio del giocatore, il percorso si estende oltre la porta e puoi invitare lo zombie ad 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 bloccante.

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

Utilizzando l'esempio sopra, puoi far utilizzare una barca all'agente.

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

Per creare un PathfindingLink utilizzando questo esempio:

  1. OPZIONALE
    Attiva Link di Navigazione dalle Opzioni di Visualizzazione nel riquadro in alto a destra della vista 3D. Questo aiuta con la visualizzazione e il debug durante l'implementazione dei link di navigazione.

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

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

    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 partenza del link.

    Proprietà Label specificata per PathfindingLink.
  5. Includi una tabella di 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 DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 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 compiere un'azione diversa da Humanoid:Move(); in questo caso, potresti chiamare una funzione per far sedere l'agente nella barca, muovere la barca attraverso l'acqua, far uscire l'agente al punto di attracco 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 degli insiemi esistenti potrebbero uscire.

Considera i seguenti migliori pratiche per utilizzare PathfindingService in giochi abilitati per lo streaming:

  • Lo streaming può bloccare o sbloccare un percorso dato mentre un personaggio si muove lungo esso. Ad esempio, mentre un personaggio corre attraverso una foresta, un albero potrebbe apparire davanti a loro e ostruire il percorso. Per far funzionare la navigazione senza problemi con lo streaming, è altamente consigliato utilizzare la tecnica di 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 per il percorso sulla posizione di un modello TreasureChest esistente nel mondo. Questo approccio è completamente compatibile con gli Scripts lato server poiché il server ha sempre una visione completa del mondo, ma LocalScripts e ModuleScripts che vengono eseguiti sul client possono fallire se tentano di calcolare un percorso verso un oggetto che non è stato caricato.

    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 che il giocatore si unisce e non vengono mai scaricati, quindi uno script client-side può connettersi all'evento PersistentLoaded e accedere in sicurezza al modello per creare waypoint dopo che l'evento è scattato.

Limitazioni e fattori di fallimento

Il motore di navigazione include specifiche limitazioni per garantire un'elaborazione efficiente e prestazioni ottimali. Inoltre, i calcoli della navigazione possono fallire per vari motivi come indicato 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.