Pfadfinding

*Dieser Inhalt wurde mit KI (Beta) übersetzt und kann Fehler enthalten. Um diese Seite auf Englisch zu sehen, klicke hier.

Pfadfinding ist der Prozess, einen Charakter oder ein Objekt (Agent) entlang eines logischen Pfades um Hindernisse herum zu bewegen, um ein Ziel zu erreichen, wobei optional gefährliche Materialien oder definierte Regionen vermieden werden.

Um bei der Anordnung und Fehlersuche des Pfadfindings zu helfen, kann Studio ein Navigationsnetz und Modifikator-Labels rendern. Um diese zu aktivieren, schalten Sie Navigationsnetz und Pfadfindungsmodifikatoren im Widget Visualisierungsoptionen in der oberen rechten Ecke des 3D-Viewport ein.

Eine Nahaufnahme des 3D-Viewports mit der Schaltfläche für Visualisierungsoptionen, die in der oberen rechten Ecke angezeigt wird.

Mit aktiviertem Navigationsnetz zeigen farbige Bereiche, wo ein Charakter laufen oder schwimmen kann. Kleine Pfeile zeigen Bereiche an, die ein Charakter durch Springen zu erreichen versucht.

Navigationsnetz, das in Studio angezeigt wird

Implementierung

Obwohl Pfadfinding auf verschiedene Weisen über PathfindingService und dessen zugehörige Methoden wie CreatePath() implementiert werden kann, verwendet dieser Abschnitt das folgende Pfadfindungs-Skript für den Charakter des Spielers.

Um beim Lesen zu testen:

  1. WICHTIG
    Wählen Sie im Explorer den StarterPlayer-Container aus. Setzen Sie dann im Fenster Eigenschaften sowohl DevComputerMovementMode als auch DevTouchMovementMode auf Scriptable.

  2. Kopieren Sie den folgenden Code in ein LocalScript innerhalb von StarterCharacterScripts oder holen Sie sich dieses Paket und legen Sie es 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
    -- Berechnen Sie die Entfernung vom Humanoid zur Zielfläche
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Berechnen Sie die Komponente der Humanoidgeschwindigkeit, die zur Fläche zeigt
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Berechnen Sie den Schwellenwert von der Zielfläche basierend auf der Humanoidgeschwindigkeit
    local threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)
    -- Betrachten Sie den Wegpunkt als erreicht, wenn weniger als der Schwellenwert vor der Fläche
    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]
    -- Erstellen Sie die Zielfläche vom nächsten Wegpunkt zur aktuellen Fläche
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Setzen Sie die Normale senkrecht zur Y-Fläche, wenn nicht nach oben geklettert wird
    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
    -- Rufen Sie Ihre eigene angepasste Funktion auf, um den Agenten das Boot benutzen zu lassen
    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()
    -- Pfad berechnen
    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("Pfad nicht berechnet!", errorMessage)
    return
    end
    -- Holen Sie sich die Wegpunkte
    waypoints = path:GetWaypoints()
    -- Erkennen, ob der Pfad blockiert wird
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Überprüfen Sie, ob das Hindernis weiter unten auf dem Pfad ist
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Stoppen Sie die Erkennung von Pfadblockaden, bis der Pfad neu berechnet wird
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Rufen Sie die Funktion auf, um den neuen Pfad neu zu berechnen
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Bearbeiten Sie die DESTINATION-Variable (

    ZEILE 5
    ) auf ein Vector3-Ziel innerhalb der 3D-Welt, das der Spielercharakter erreichen kann.

  4. Fahren Sie mit den folgenden Abschnitten fort, um mehr über die Pfadberechnung und die Charakterbewegung zu erfahren.

Pfaderstellung

Pfadfinding wird über PathfindingService und dessen Methode CreatePath() initiiert (

ZEILEN 9–14
). Diese Methode akzeptiert eine optionale Tabelle von Parametern, die die Art und Weise, wie sich der Charakter (Agent) auf dem Pfad bewegt, feinjustiert.

SchlüsselBeschreibungTypStandard
AgentRadiusAgent-Radius, in Studs. Nützlich zur Bestimmung des minimalen Abstands zu Hindernissen.integer2
AgentHeightAgent-Höhe, in Studs. Leerraum, der kleiner als dieser Wert ist, wie der Platz unter Treppen, wird als nicht durchquerbar markiert.integer5
AgentCanJumpBestimmt, ob Sprünge während des Pfadfindings erlaubt sind.booleantrue
AgentCanClimbBestimmt, ob Klettern von TrussParts während des Pfadfindings erlaubt ist. Ein kletterbarer Pfad hat ein Label, das Climb heißt, und die Kosten für einen kletterbaren Pfad sind standardmäßig 1.booleanfalse
WaypointSpacingAbstand zwischen Zwischenwegpunkten im Pfad. Wenn auf math.huge gesetzt, gibt es keine Zwischenwegpunkte.number4
CostsTabelle von Materialien oder definierten PathfindingModifiers und deren Kosten für die Durchquerung. Nützlich, um den Agenten bestimmte Materialien/Regionen zu bevorzugen. Siehe Modifikatoren für weitere Details.tablenil

Pfadberechnung

Nachdem Sie einen gültigen Pfad mit CreatePath() erstellt haben, muss er berechnet werden, indem Path:ComputeAsync() mit einem Vector3 sowohl für den Ausgangspunkt als auch für das Ziel aufgerufen wird (

ZEILEN 133–139
).

Pfadstart/-ende über zwei Brücken markiert

Sobald der Path berechnet ist, enthält er eine Reihe von Wegpunkten, die den Pfad von Anfang bis Ende zurückverfolgen. Diese Punkte können mit der Methode Path:GetWaypoints() gesammelt werden (

ZEILE 142
). Das zurückgegebene Array ist in der Reihenfolge der Wegpunkte von Pfadstart bis Pfadende angeordnet.

Wegpunkte, die über den berechneten Pfad angezeigt werden
Wegpunkte, die über den berechneten Pfad angezeigt werden

Pfadbewegung

Jeder PathWaypoint besteht aus sowohl einer Position (Vector3) als auch einer Action (PathWaypointAction). Um einen Charakter mit einem Humanoid, wie einem typischen Roblox-Charakter, zu bewegen, ist es am besten, Humanoid:Move() von Wegpunkt zu Wegpunkt aufzurufen und den Callback isCurrentWaypointReached() des Skripts (

ZEILEN 32–56
) zu verwenden, um zu erkennen, wann der Charakter jeden Wegpunkt erreicht.

Blockierte Pfade

Viele Roblox-Welten sind dynamisch; Teile können sich bewegen oder fallen und Böden können zusammenbrechen. Dies kann einen berechneten Pfad blockieren und verhindern, dass der Charakter sein Ziel erreicht. Um damit umzugehen, können Sie das Path.Blocked-Ereignis verbinden und den Pfad um das Blockierte neu berechnen (

ZEILEN 145–154
).

Pfadfindungsmodifikatoren

Standardmäßig gibt Path:ComputeAsync() den kürzesten Pfad zwischen dem Ausgangspunkt und dem Ziel zurück, mit der Ausnahme, dass er versucht, Sprünge zu vermeiden. Dies sieht in einigen Situationen unnatürlich aus; zum Beispiel kann ein Pfad durch Sumpfwasser führen, anstatt darum herum, nur weil der Pfad durch das Wasser geometrisch kürzer ist.

Zwei Pfade angezeigt, wobei der kürzere Pfad nicht unbedingt logischer ist

Um Pfadfinding noch weiter zu optimieren, können Sie Pfadfindungsmodifikatoren implementieren, um schlauere Pfade über verschiedene Materialien, um definierte Regionen herum oder um Hindernisse zu ignorieren.

Materialkosten

Beim Arbeiten mit Terrain und BasePart-Materialien können Sie eine Costs-Tabelle innerhalb von CreatePath() einfügen, um bestimmte Materialien durchquerbarer zu machen als andere. Alle Materialien haben standardmäßig die Kosten 1, und jedes Material kann als nicht durchquerbar definiert werden, indem sein Wert auf math.huge gesetzt wird.

Schlüssel in der Costs-Tabelle sollten String-Namen darstellen, die den Namen von Enum.Material repräsentieren, zum Beispiel Water für Enum.Material.Water oder CrackedLava für 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
}
})

Regionen konfigurieren

In einigen Fällen reicht die Materialpräferenz nicht aus. Zum Beispiel möchten Sie möglicherweise, dass Charaktere eine definierte Region vermeiden, unabhängig von den Materialien darunter. Dies kann erreicht werden, indem Sie ein PathfindingModifier-Objekt zu einem Teil hinzufügen.

  1. Erstellen Sie einen Anchored-Teil um die Region und setzen Sie seine CanCollide-Eigenschaft auf false.

    Angehefteter Teil, der eine Region definiert, auf die ein Pfadfindungsmodifikator angewendet werden soll.
  2. Fügen Sie eine PathfindingModifier-Instanz auf den Teil ein, suchen Sie die Label-Eigenschaft und weisen Sie ihr einen sinnvollen Namen wie DangerZone zu.

    PathfindingModifier-Instanz mit der Label-Eigenschaft, die auf DangerZone gesetzt ist.
  3. Fügen Sie eine passende DangerZone-Schlüssel und den zugehörigen numerischen Wert innerhalb der Costs-Tabelle von CreatePath() hinzu. Ein Modifikator kann als nicht durchquerbar definiert werden, indem sein Wert auf math.huge gesetzt wird.

    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
    }
    })

Hindernisse ignorieren

In einigen Fällen ist es nützlich, durch feste Hindernisse zu pfadfinden, als ob sie nicht existieren würden. Dies ermöglicht es Ihnen, einen Pfad durch bestimmte physische Blockierer zu berechnen, anstatt dass die Berechnung sofort fehlschlägt.

  1. Erstellen Sie einen Anchored-Teil um das Objekt und setzen Sie seine CanCollide-Eigenschaft auf false.

    Angehefteter Teil, der eine Region definiert, auf die ein Pfadfindungsmodifikator angewendet werden soll.
  2. Fügen Sie eine PathfindingModifier-Instanz zu dem Teil hinzu und aktivieren Sie die PassThrough-Eigenschaft.

    PathfindingModifier-Instanz mit aktivierter PassThrough-Eigenschaft.

    Nun, wenn ein Pfad vom Zombie-NPC zum Spielercharakter berechnet wird, erstreckt sich der Pfad über die Tür hinaus, und Sie können den Zombie auffordern, sie zu durchqueren. Selbst wenn der Zombie nicht in der Lage ist, die Tür zu öffnen, reagiert er, als ob er den Charakter hinter der Tür "hört".

    Zombie-NPC-Pfad, der durch die zuvor blockierende Tür führt.

Gelegentlich ist es notwendig, einen Pfad über einen Raum zu finden, der nicht normalerweise durchquert werden kann, wie z.B. über einen Abgrund, und eine benutzerdefinierte Aktion durchzuführen, um den nächsten Wegpunkt zu erreichen. Dies kann durch das Objekt PathfindingLink erreicht werden.

Verwenden Sie das Beispiel von oben, Sie können den Agenten ein Boot benutzen lassen.

PathfindingLink zeigt, wie ein Agent ein Boot benutzen kann.

Um ein PathfindingLink mit diesem Beispiel zu erstellen:

  1. OPTIONAL
    Schalten Sie Pfadfindungslinks im Widget Visualisierungsoptionen in der oberen rechten Ecke des 3D-Viewports ein. Dies hilft bei der Visualisierung und Fehlersuche bei der Implementierung von Pfadfindungslinks.

  2. Erstellen Sie zwei Attachments, eine auf dem Bootsitz und eine in der Nähe des Bootsanlegestelle.

    Befestigungen für den Start und das Ende des Pfadfindungslinks erstellt.
  3. Erstellen Sie ein PathfindingLink-Objekt im Arbeitsplatz, und weisen Sie dann die Eigenschaften Attachment0 und Attachment1 den entsprechenden Start- und Endbefestigungen zu.

    Attachment0/Attachment1-Eigenschaften eines PathfindingLink. PathfindingLink, der im 3D-Raum visualisiert wird.
  4. Weisen Sie seiner Label-Eigenschaft einen sinnvollen Namen wie UseBoat zu. Dieser Name wird als Flag im Pfadfindungs-Skript verwendet, um eine benutzerdefinierte Aktion auszulösen, wenn der Agent den Start-Linkpunkt erreicht.

    Label-Eigenschaft für PathfindingLink festgelegt.
  5. Fügen Sie eine Costs-Tabelle innerhalb von CreatePath() hinzu, die sowohl einen Water-Schlüssel als auch einen benutzerdefinierten Schlüssel enthält, der dem Namen der Label-Eigenschaft entspricht. Weisen Sie dem benutzerdefinierten Schlüssel einen Wert niedriger als den von Water zu.

    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. In der moveToNextWaypoint()-Funktion (

    ZEILEN 93–114
    ) kann eine benutzerdefinierte Überprüfung auf den Namen des Label-Modifikators verwendet werden, um eine andere Aktion als Humanoid:Move() auszuführen; in diesem Fall können Sie eine Funktion aufrufen, um den Agenten im Boot zu platzieren, das Boot über das Wasser zu bewegen, den Agenten am Landungsort des Bootes abzuladen und dann den Pfad des Agenten zu seinem endgültigen Ziel fortzusetzen.

Streaming-Kompatibilität

In-Game Instanz-Streaming ist eine leistungsstarke Funktion, die 3D-Inhalte dynamisch lädt und entlädt, während sich der Charakter eines Spielers durch die Welt bewegt. Während sie den 3D-Raum erkunden, werden neue Teilmengen des Raumes auf ihr Gerät gestreamt und einige der vorhandenen Teilmengen könnten gestreamt werden.

Berücksichtigen Sie die folgenden Best Practices zur Verwendung von PathfindingService in Streaming-aktivierten Spielen:

  • Streaming kann einen bestimmten Pfad blockieren oder freigeben, während sich ein Charakter entlang dieser bewegt. Zum Beispiel, während ein Charakter durch einen Wald läuft, könnte ein Baum irgendwo vor ihnen gestreamt werden und den Pfad blockieren. Um sicherzustellen, dass das Pfadfinding nahtlos mit dem Streaming funktioniert, wird dringend empfohlen, die Technik blockierte Pfade behandeln zu verwenden und den Pfad bei Bedarf neu zu berechnen.

  • Ein gängiger Ansatz im Pfadfinding besteht darin, die Koordinaten vorhandener Objekte für die Berechnung zu verwenden, wie das Setzen eines Pfadziels auf die Position eines vorhandenen TreasureChest-Modells in der Welt. Dieser Ansatz ist vollständig kompatibel mit serverseitigen Scripts, da der Server die volle Sicht auf die Welt zu jeder Zeit hat, aber LocalScripts und ModuleScripts, die auf dem Client ausgeführt werden, könnten fehlschlagen, wenn sie versuchen, einen Pfad zu einem Objekt zu berechnen, das nicht gestreamt ist.

    Um dieses Problem zu beheben, ziehen Sie in Betracht, das Ziel auf die Position eines BasePart innerhalb eines beständigen Modells zu setzen. Beständige Modelle werden kurz nach dem Beitritt des Spielers geladen und sie werden niemals gestreamt, sodass ein clientseitiges Skript an das Ereignis PersistentLoaded anschließen und sicher auf das Modell zugreifen kann, um Waypoints zu erstellen, nachdem das Ereignis ausgelöst wurde.

Einschränkungen und Fehlerfaktoren

Die Pfadfindungs-Engine hat spezifische Einschränkungen, um eine effiziente Verarbeitung und optimale Leistung sicherzustellen. Darüber hinaus können Pfadfindungs-Berechnungen aus verschiedenen Gründen fehlschlagen, die nachfolgend aufgeführt sind.

©2026 Roblox Corporation. Roblox, das Roblox-Logo und "Powering Imagination" gehören zu unseren eingetragenen und nicht eingetragenen Markenzeichen in den USA und anderen Ländern.