Charakter Pathfinding

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

Pathfinding to proces przesuwania postaci w logicznym kierunku, aby dotrzeć do celu, unikając przeszkód i (opcjonalnie) niebezpiecznych materiałów lub zdefiniowanych obszarów.

Wizualizacja Navigacji

Aby pomóc w wykrywaniu układu ścieżki i debugowaniu, Studio może wygenerować siatkę nawigacyjną i modyfikator etykiet. Aby włączyć je, włącz Nawigacja i Modyfikatory z wtyczki 2>Opcje wyświetlania2> w górnym prawym rogu 3D.

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.

Z włączoną siatką nawigacyjną kolorowe obszary pokazują, gdzie może chodzić lub pływaniepostać, podczas gdy niekolorowe obszary są zablokowane. Małe strzałki wskazują obszary, które postać będzie próbować dotrzeć poprzez skok, z założeniem, że ustawiłeś AgentCanJump na true,

Navigation mesh showing in Studio

Z modyfikatorami ścieżki włączonymi, etykiety tekstowe wskazują na materiały i obszary, które są brane pod uwagę podczas używania modyfikatorów ścieżki.

Navigation labels showing on navigation mesh

Wiadomości ograniczenia

Znaki drogowe znajdujące ograniczenia, aby zapewnić wydajne przetwarzanie i optymalne wykonywanie.

Ograniczenie poziomego umieszczania

Kalkulacje algorytmu kierowania uwzględniają tylko części w określonych granicach wertykalnych:

  • Niższy granica — części z końcem Y kodu są ignorowane poniżej -65,536 studs.
  • Górne granice — części z najwyższym topowym Y koordynatami przekraczającymi 65 536 jednostek są ignorowane.
  • Podział — Dystans wertykalny od dołu najniższego elementu Y do najwyższego elementu Y musi przekroczyć 65 536 jednostek; w przeciwnym razie system znalezienia ścieżki ignoruje te części podczas kalkulacji ścieżki.

Ograniczenie zasięgu wyszukiwania

Odległość bezpośredniej linii pola widzenia dla pomiaru ścieżki musi wynosić nie więcej niż 3,000 jednostek. Przekroczenie tej odległości spowoduje wynik NoPath .

Tworzenie ścieżek

Pathfinding jest inicjowany poprzez PathfindingService i jego funkcję CreatePath().

Lokalny Skrypt

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath()

CreatePath() akceptuje opcjonalną tabelę parametrów, która dostosowuje to, jak postępuje postać (agent) po ścieżce.

KluczOpisTypDomyślny
AgentRadiusOdległość agenta, w studs. Przydatny do określenia minimum separacji od przeszkód.liczby2
AgentHeightWysokość agenta, w studs. Mniejsza przestrzeń niż ta wartość, jak przestrzeń pod schodami, zostanie oznaczona jako niedostępna do przejścia.liczby5
AgentCanJumpOkreśla, czy można skakać podczas znajdywania drogi.booleantrue
AgentCanClimbOkreśla, czy można wspinację TrussParts podczas znalezienia ścieżki.booleanfalse
WaypointSpacingDystans między punktami końcowymi w ścieżce. Jeśli ustawiony na math.huge, nie będzie punktów końcowych.liczba4
CostsTabela materiałów lub zdefiniowanych PathfindingModifiers i ich koszt do przejścia. Przydatne do uczynienia agencji preferowanymi materiałami/regionami nad innymi. Zobacz modifiery dla szczegółów.tabelanil
Lokalny Skrypt

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentRadius = 3,
AgentHeight = 6,
AgentCanJump = false,
Costs = {
Water = 20
}
})

Uwaga, że agent może wspinac się TrussParts podczas znalezienia drogi, zazwczaj ustawiając AgentCanClimb na true, gdy tworzysz drogę, a nic nie blokuje agenta z drogi wspinania.

Path going up a climbable TrussPart ladder
Lokalny Skrypt - Ścieżka wspinaczki

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentCanClimb = true,
Costs = {
Climb = 2 -- Koszt schodzenia; domyślny jest 1
}
})

Poruszanie się po ścieżkach

Ta sekcja używa następującego skryptu znalezienia drogi dla postaci gracza. Aby przetestować podczas czytania:

  1. Edytuj linię 11 do celu Vector3, do którego może dotrzeć postać gracza.
  2. Przejdź przez następujące sekcje, aby dowiedzieć się o kalkulacji ścieżki i ruchu postaci.
Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Oblicz drogę
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Zdobądź waypointsy drogi
waypoints = path:GetWaypoints()
-- Wybierz, czy droga stała się zablokowana
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Sprawdź, czy przeszkoda jest dalej w dół drogi
if blockedWaypointIndex >= nextWaypointIndex then
-- Zatrzymaj wykrywanie blokad drogi do momentu ponownego obliczenia drogi
blockedConnection:Disconnect()
-- Wezwij funkcję, aby ponownie obliczyć nową ścieżkę
followPath(destination)
end
end)
-- Wykryj, gdy ruch do następnego punktu końcowego jest ukończony
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Zwiększ wskaźnik punktu drogi i przenieś się do następnego punktu drogi
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Początkowo przenieś się do drugiego punktu końca (pierwszy punkt końca to początek drogi; pomiń go)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
followPath(TEST_DESTINATION)

Obliczanie ścieżki

Po utworzeniu ważnej ścieżki z użyciem CreatePath() , musi być wyliczony poprzez wezwanie Path:ComputeAsync() z użyciem 2>Datatype.Vector32> dla obu punktu wyjścia i docelu.

Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Oblicz drogę
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
end
Path start/end marked on series of islands and bridges

Otrzymywanie Waypoints

Gdy Path zostanie obliczony, będzie zawierał serię waypointów, które śledzą drogę od początku do kończyć. Te punkty można zbierać za pomocą funkcji Path:GetWaypoints().

Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Oblicz drogę
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Zdobądź waypointsy drogi
waypoints = path:GetWaypoints()
end
end
Waypoints indicated across computed path
Wskazówki drogowe wskazujące na drodze obliczonej

Przenieście ścieżki

Każdy waypoint składa się z zarówno pozycji ( Vector3 ) i akcji ( 2>Class.Humanoid:MoveTo

Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Oblicz drogę
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Zdobądź waypointsy drogi
waypoints = path:GetWaypoints()
-- Wybierz, czy droga stała się zablokowana
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Sprawdź, czy przeszkoda jest dalej w dół drogi
if blockedWaypointIndex >= nextWaypointIndex then
-- Zatrzymaj wykrywanie blokad drogi do momentu ponownego obliczenia drogi
blockedConnection:Disconnect()
-- Wezwij funkcję, aby ponownie obliczyć nową ścieżkę
followPath(destination)
end
end)
-- Wykryj, gdy ruch do następnego punktu końcowego jest ukończony
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Zwiększ wskaźnik punktu drogi i przenieś się do następnego punktu drogi
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- Początkowo przenieś się do drugiego punktu końca (pierwszy punkt końca to początek drogi; pomiń go)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end

Przyczepność zablokowanych dróg

Wiele światów Roblox jest dynamicznych; części mogą się poruszać lub spaść, a podłogi mogą się zawalić. To może zablokować wyznaczoną ścieżkę i zapobiec osiągnięciu postaci przez nią. Aby to zrozumieć, możesz połączyć wydarzenie Path.Blocked i ponownie zliczyć ścieżkę wokół wszystkich zablokowanych przez nie.

Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- Oblicz drogę
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- Zdobądź waypointsy drogi
waypoints = path:GetWaypoints()
-- Wybierz, czy droga stała się zablokowana
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- Sprawdź, czy przeszkoda jest dalej w dół drogi
if blockedWaypointIndex >= nextWaypointIndex then
-- Zatrzymaj wykrywanie blokad drogi do momentu ponownego obliczenia drogi
blockedConnection:Disconnect()
-- Wezwij funkcję, aby ponownie obliczyć nową ścieżkę
followPath(destination)
end
end)
end
end

Modyfikatory znalezienia drogi

Domyślnie, Path:ComputeAsync() zwraca najkrótszą ścieżkę między punktem wyjścia a celem, z wyjątkiem tego, że próbuje unikać skoków. To wygląda nieprawidłowo w niektórych sytuacjach, na instancja, ścieżka może przejść przez wodę, ponieważ ścieżka przez wodę jest geometycznie krótsza.

Two paths indicated with the shorter path not necessarily more logical

Aby zoptymalizować znalezienie drogi jeszcze bardziej, możesz zaimplementować modyfikatory znalezienia drogi, aby obliczyć mądrzejsze drogi w różnych materiałach, wokół zdefiniowanych obszarów lub poprzez 2>przeszkody2>.

Ustawienie kosztów materiałów

Podczas pracy z Terrain i BasePart materiałami, możesz włączyć Costs tabelę w 1> Class.PathfindingService:CreatePath()|CreatePath()1>, aby uczynić niektóre mater

Klucze w tabeli Costs powinny być nazwami strungami reprezentującymi Enum.Material nazw, na przykład Water dla 1> En册.Material.Water1> .

Lokalny Skrypt - Doskonałanie ścieżki postępowania

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local path = PathfindingService:CreatePath({
Costs = {
Water = 20,
Mud = 5,
Neon = math.huge
}
})

Praca z regionami

W niektórych przypadkach preferencje materiałowe nie są wystarczające. Na przykład możesz chcieć, aby postacie unikały zdefiniowanej region , niezależnie od materiałów pod stopami. Można to osiągnąć poprzez dodanie obiektu PathfindingModifier do części.

  1. Utwórz część Anchored wokół niebezpiecznego regionu i ustaw jego CanCollide właściwość na fałszywy .

    Anchored part defining a region to apply a pathfinding modifier to
  2. Utwórz instancję PathfindingModifier na części, lokalizuj jego Label property, i przypisz znaczącą nazwę, taką jak DangerZone .

    PathfindingModifier instance with Label property set to DangerZone
  3. Obejmują tabelę Costs w CreatePath() zawierającą klucz i powiązany liczbowy wartość. Modyfikator można zdefiniować jako niedostępny poprzez ustawienie jego wartości na math.huge .

    Lokalny Skrypt - Doskonałanie ścieżki postępowania

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local path = PathfindingService:CreatePath({
    Costs = {
    DangerZone = math.huge
    }
    })

Ignorowanie przeszkód

W niektórych przypadkach może być użyteczne, aby przeprowadzić przez przeszkody, które nie istnieją. Dzięki temu można obliczyć ścieżkę poprzez konkretne bloki fizyczne, a nie przez usterkę obliczania.

  1. Utwórz część Anchored wokół obiektu i ustaw jego właściwość CanCollide na fałszywą .

    Anchored part defining a region to apply a pathfinding modifier to
  2. Utwórz instancję PathfindingModifier na części i włącz własność PassThrough.

    PathfindingModifier instance with PassThrough property enabled

    Teraz, gdy droga z zombie NPC do postaci gracza, droga rozciąga się poza drzwi i możesz poprosić zombie, aby przejechał. Nawet jeśli zombie nie może otworzyć drzwi, reaguje, jakby "słyszał" postać za drzwiami.

    Zombie NPC path passing through the previously blocking door

Linki do znalezienia drogi

Czasami konieczne jest znalezienie drogi przesyłującej się przez przestrzeń, która nie może być przesyłana normalnie, takie jak przez chasm, i wykonanie niestandardowej akcji, aby osiągnąć następny punkt końcowy. To można osiągnąć poprzez obiekt PathfindingLink.

Używając przykładu wyspy z powyższego, możesz sprawić, aby agent używał łodzi zamiast chodzić przez wszystkie mosty.

PathfindingLink showing how an agent can use a boat instead of walking across all of the bridges

Aby utworzyć PathfindingLink używając tego przykładu:

  1. Aby pomóc w wizualizacji i debugowaniu, włącz łączeń ścieżek z w widgetu Opcje wizualizacji w górnym rogu 3D.

  2. Utwórz dwa Attachments, jeden na siedzeniu łodzi i jeden w pobliżu punktu lądowania łodzi.

    Attachments created for pathfinding link's start and end
  3. Utwórz obiekt PathfindingLink w przestrzeni roboczej, a następnie przypisz jego właściwości Attachment0 i Attachment1 do początkowych i końcowych załączeń odpowiednio.

    Attachment0/Attachment1 properties of a PathfindingLink PathfindingLink visualized in the 3D world
  4. Przydziel znaczącą nazwę, taką jak UseBoat jego Label właściwości. Ten nazwa jest używana jako flaga w skrypcie pathfinding script, aby wywołać niestandardową akcję, gdy agencja dotrze do punktu wyjścia linku.

    Label property specified for PathfindingLink
  5. Obejmuj tabelę Costs w CreatePath() zawierającą zarówno klucz Water jak i klucz niestandardowy odpowiadający nazwie właściwości 2>Class.PathfindingLink.Label|Label2>. Przydzij klucz niestandardowy mniejszą wartość niż 5>

    Lokalny Skrypt - Doskonałanie ścieżki postępowania

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local path = PathfindingService:CreatePath({
    Costs = {
    Water = 20,
    UseBoat = 1
    }
    })
  6. W wydarzeniu, które się włącza, gdy modyfikator Class.PathfindingLink.Label|Label zostanie osiągnięty, dodaj niestandardowy czek dla nazwy modyfikatora Class.Humanoid:MoveTo() i podejmij inną akcję niż Class.Humanoid:MoveTo() w tym przypadku, wzywając funkcję, aby siedział agencja na łodzi, przenieść ł

    Lokalny Skrypt - Doskonałanie ścieżki postępowania

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local path = PathfindingService:CreatePath({
    Costs = {
    Water = 20,
    UseBoat = 1
    }
    })
    local player = Players.LocalPlayer
    local character = player.Character
    local humanoid = character:WaitForChild("Humanoid")
    local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)
    local waypoints
    local nextWaypointIndex
    local reachedConnection
    local blockedConnection
    local function followPath(destination)
    -- Oblicz drogę
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, destination)
    end)
    if success and path.Status == Enum.PathStatus.Success then
    -- Zdobądź waypointsy drogi
    waypoints = path:GetWaypoints()
    -- Wybierz, czy droga stała się zablokowana
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Sprawdź, czy przeszkoda jest dalej w dół drogi
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Zatrzymaj wykrywanie blokad drogi do momentu ponownego obliczenia drogi
    blockedConnection:Disconnect()
    -- Wezwij funkcję, aby ponownie obliczyć nową ścieżkę
    followPath(destination)
    end
    end)
    -- Wykryj, gdy ruch do następnego punktu końcowego jest ukończony
    if not reachedConnection then
    reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
    if reached and nextWaypointIndex < #waypoints then
    -- Zwiększ wskaźnik punktu drogi i przenieś się do następnego punktu drogi
    nextWaypointIndex += 1
    -- Użyj łodzi, jeśli label połączenia jest "Użyj łodzi"; w przeciwnym razie przenieś się do następnego punktu połączenia
    if waypoints[nextWaypointIndex].Label == "UseBoat" then
    useBoat()
    else
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    else
    reachedConnection:Disconnect()
    blockedConnection:Disconnect()
    end
    end)
    end
    -- Początkowo przenieś się do drugiego punktu końca (pierwszy punkt końca to początek drogi; pomiń go)
    nextWaypointIndex = 2
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    else
    warn("Path not computed!", errorMessage)
    end
    end
    function useBoat()
    local boat = workspace.BoatModel
    humanoid.Seated:Connect(function()
    -- Rozpocznij ruch łodzi, jeśli agent jest siedzony
    if humanoid.Sit then
    task.wait(1)
    boat.CylindricalConstraint.Velocity = 5
    end
    -- Wybierz pozycję ograniczenia w odniesieniu do wyspy
    local boatPositionConnection
    boatPositionConnection = RunService.PostSimulation:Connect(function()
    -- Zatrzymaj łódź, gdy jest blisko wyspy
    if boat.CylindricalConstraint.CurrentPosition >= 94 then
    boatPositionConnection:Disconnect()
    boat.CylindricalConstraint.Velocity = 0
    task.wait(1)
    -- Zdejmij agent i kontynuuj podróż
    humanoid.Sit = false
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    end)
    end)
    end
    followPath(TEST_DESTINATION)

Kompatybilność z siecią

W przeciągu doświadczenia instans przesyłania instansu jest potężną funkcją, która dynamicznie ładowuje i odłącza 3D-treści, gdy postać gracza porusza się po świecie. Podczas gdy eksplorują przestrzeń 3D, nowe podzestawy przestrzeni przesyłania do ich urządzenia i niektóre z istniejących podzestawów mogą streamować.

Zastanów się nad następującymi lepszymi praktykami dla używania PathfindingService w włączonych do transmisji doświadczeń:

  • Transmisja może zablokować lub odblokować dany ścieżki, gdy postać porusza się po nim. Na przykład, podczas gdy postać biegnie przez las, drzewo może przepływać gdzieś przed nimi i zablokować drogę. Aby uczynić pathfinding płynny z transmisją, bardzo rekomendujemy użyć techniki Przycinanie zablokowanych ścieżek i ponownie obliczyć ścieżk

  • Zwykłym podejściem w badaniu ścieżek jest użycie koordynat istniejących obiektów do liczenia, takich jak ustawienie kierunku docelowego na pozycję istniejącego Skarbca

    Aby rozwiązać ten problem, rozważaj ustawienie celu na pozycję BasePart w modelu persistent . Modele persystentne ładowane są wkrótce po dołączeniu gracza i nigdy nie streamują, więc skrypt strony klienta może się połączyć z wydarzeniem PersistentLoaded i bezpiecznie uzyskać d