Pfadfindung

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

Pfadfindung 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 der Pfadfindung zu helfen, kann Studio ein Navigationsnetz und Modifikator-Labels rendern. Um diese zu aktivieren, schalten Sie Navigationsnetz und Pfadfindungsmodifikatoren im Visualisierungsoptionen-Widget in der oberen rechten Ecke des 3D-Viewport ein.

Eine Nahaufnahme des 3D-Viewports mit dem Visualisierungsoptionen-Button, der in der oberen rechten Ecke angezeigt wird.

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

Navigationsnetz, das in Studio angezeigt wird

Implementierung

Obwohl die Pfadfindung auf verschiedene Weise über PathfindingService und seine zugehörigen Methoden wie CreatePath() implementiert werden kann, verwendet dieser Abschnitt das folgende Pfadfindungsskript für den Charakter des Spielers.

Um während des Lesens zu testen:

  1. WICHTIG
    Wählen Sie im Explorer den StarterPlayer-Container aus. Setzen Sie dann im Eigenschaften-Fenster 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 ab.

    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
    -- Berechne die Entfernung vom Humanoid zur Ziel-Ebene
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Berechne die Komponente der Humanoidgeschwindigkeit, die zur Ebene zeigt
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Berechne die Schwelle von der Ziel-Ebene basierend auf der Humanoidgeschwindigkeit
    local threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)
    -- Betrachte den Wegpunkt als erreicht, wenn weniger als die Schwelle vor der Ebene
    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]
    -- Baue die Ziel-Ebene vom nächsten Wegpunkt zum aktuellen
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Setze die Normale senkrecht zur Y-Ebene, 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 benutzerdefinierte 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()
    -- Berechne den Pfad
    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
    -- Hole die Wegpunkte des Pfades
    waypoints = path:GetWaypoints()
    -- Erkenne, ob der Pfad blockiert wird
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Überprüfe, ob das Hindernis weiter unten auf dem Pfad ist
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Stoppe die Erkennung von Pfadblockierungen, bis der Pfad neu berechnet wird
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Rufe 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 Bewegung des Charakters zu erfahren.

Pfaderstellung

Die Pfadfindung wird über PathfindingService und seine Methode CreatePath() initiiert (

ZEILEN 9–14
). Diese Methode akzeptiert eine optionale Tabelle von Parametern, die verfeinern, wie sich der Charakter (Agent) entlang des Pfades bewegt.

SchlüsselBeschreibungTypStandard
AgentRadiusAgentenradius in Studs. Nützlich, um den minimalen Abstand zu Hindernissen zu bestimmen.integer2
AgentHeightAgentenhöhe in Studs. Leerraum, der kleiner als dieser Wert ist, wie der Raum unter Treppen, wird als nicht durchquerbar markiert.integer5
AgentCanJumpBestimmt, ob das Springen während der Pfadfindung erlaubt ist.booleantrue
AgentCanClimbBestimmt, ob das Klettern von TrussParts während der Pfadfindung erlaubt ist. Ein kletterbarer Pfad hat ein Label mit dem Namen Climb, und die Kosten für einen kletterbaren Pfad betragen 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 gegenüber anderen bevorzugen zu lassen. Siehe Modifikatoren für Details.tablenil

Pfadberechnung

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

ZEILEN 133–139
).

Pfadstart/-ende, das über zwei Brücken markiert ist

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

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

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

Pfadbewegung

Jeder PathWaypoint besteht sowohl aus einer Position (Vector3) als auch aus 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 Rückruf 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 einstürzen. Dies kann einen berechneten Pfad blockieren und verhindern, dass der Charakter sein Ziel erreicht. Um dies zu handhaben, können Sie das Path.Blocked-Ereignis verbinden und den Pfad um das, was ihn blockiert, neu berechnen (

ZEILEN 145–154
).

Pfadfindungsmodifikatoren

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

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

Um die Pfadfindung noch weiter zu optimieren, können Sie Pfadfindungsmodifikatoren implementieren, um intelligentere Pfade über verschiedene Materialien, um definierte Regionen 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 eine Kosten von 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 Enum.Material-Namen 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 unter ihren Füßen. Dies kann erreicht werden, indem Sie ein PathfindingModifier-Objekt zu einem Teil hinzufügen.

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

    Angeheftetes Teil, das eine Region definiert, auf die ein Pfadfindungsmodifikator angewendet werden soll.
  2. Fügen Sie eine PathfindingModifier-Instanz auf das Teil ein, suchen Sie die Label-Eigenschaft und weisen Sie einen aussagekräftigen Namen wie DangerZone zu.

    PathfindingModifier-Instanz mit der Label-Eigenschaft, die auf DangerZone gesetzt ist.
  3. Fügen Sie eine übereinstimmende 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 spezifische physische Blockaden zu berechnen, anstatt dass die Berechnung vollständig fehlschlägt.

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

    Angeheftetes Teil, das eine Region definiert, auf die ein Pfadfindungsmodifikator angewendet werden soll.
  2. Fügen Sie eine PathfindingModifier-Instanz auf das Teil ein und aktivieren Sie dessen PassThrough-Eigenschaft.

    PathfindingModifier-Instanz mit aktivierter PassThrough-Eigenschaft.

    Jetzt, 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 die Tür nicht öffnen kann, reagiert er so, als ob er den Charakter hinter der Tür "hört".

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

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

Anhand des obigen Beispiels können Sie den Agenten dazu bringen, ein Boot zu benutzen.

PathfindingLink zeigt, wie ein Agent ein Boot benutzen kann.

Um ein PathfindingLink mit diesem Beispiel zu erstellen:

  1. OPTIONAL
    Schalten Sie Pfadfindungslinks im Visualisierungsoptionen-Widget 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 Sitz des Bootes und eine in der Nähe des Landepunkts des Bootes.

    Attachments, die für den Start und das Ende des Pfadfindungslinks erstellt wurden.
  3. Erstellen Sie ein PathfindingLink-Objekt im Workspace und weisen Sie dessen Attachment0- und Attachment1-Eigenschaften den Start- und End-Anhängern zu.

    Attachment0/Attachment1-Eigenschaften eines PathfindingLink. PathfindingLink, der in der 3D-Welt visualisiert wird.
  4. Weisen Sie seiner Label-Eigenschaft einen aussagekräftigen Namen wie UseBoat zu. Dieser Name wird als Flag im Pfadfindungsskript verwendet, um eine benutzerdefinierte Aktion auszulösen, wenn der Agent den Startpunkt des Links erreicht.

    Label-Eigenschaft, die für PathfindingLink angegeben ist.
  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 Funktion moveToNextWaypoint() (

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

Streaming-Kompatibilität

Das 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 Raums auf ihr Gerät gestreamt, und einige der vorhandenen Teilmengen könnten herausgestreamt werden.

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

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

  • Ein gängiger Ansatz in der Pfadfindung besteht darin, die Koordinaten vorhandener Objekte für die Berechnung zu verwenden, wie z.B. das Setzen eines Pfad-Ziels auf die Position eines vorhandenen TreasureChest-Modells in der Welt. Dieser Ansatz ist vollständig kompatibel mit serverseitigen Scripts, da der Server zu jeder Zeit einen vollständigen Überblick über die Welt hat, aber LocalScripts und ModuleScripts, die auf dem Client ausgeführt werden, können fehlschlagen, wenn sie versuchen, einen Pfad zu einem Objekt zu berechnen, das nicht hereingestreamt wurde.

    Um dieses Problem zu beheben, sollten Sie in Betracht ziehen, das Ziel auf die Position eines BasePart innerhalb eines persistenten Modells zu setzen. Persistente Modelle werden kurz nach dem Beitritt des Spielers geladen und sie werden niemals herausgestreamt, sodass ein clientseitiges Skript sich mit dem PersistentLoaded-Ereignis verbinden und das Modell sicher für die Erstellung von Wegpunkten nach dem Auslösen des Ereignisses zugreifen kann.

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, wie unten aufgeführt.

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