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.
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,
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.
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.
Klucz | Opis | Typ | Domyślny |
---|---|---|---|
AgentRadius | Odległość agenta, w studs. Przydatny do określenia minimum separacji od przeszkód. | liczby | 2 |
AgentHeight | Wysokość agenta, w studs. Mniejsza przestrzeń niż ta wartość, jak przestrzeń pod schodami, zostanie oznaczona jako niedostępna do przejścia. | liczby | 5 |
AgentCanJump | Określa, czy można skakać podczas znajdywania drogi. | boolean | true |
AgentCanClimb | Określa, czy można wspinację TrussParts podczas znalezienia ścieżki. | boolean | false |
WaypointSpacing | Dystans między punktami końcowymi w ścieżce. Jeśli ustawiony na math.huge, nie będzie punktów końcowych. | liczba | 4 |
Costs | Tabela 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. | tabela | nil |
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.
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:
- Kopiuj kod w LocalScript w StarterCharacterScripts .
- Edytuj linię 11 do celu Vector3, do którego może dotrzeć postać gracza.
- 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
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
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.
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.
Utwórz część Anchored wokół niebezpiecznego regionu i ustaw jego CanCollide właściwość na fałszywy .
Utwórz instancję PathfindingModifier na części, lokalizuj jego Label property, i przypisz znaczącą nazwę, taką jak DangerZone .
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ępowanialocal 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.
Utwórz część Anchored wokół obiektu i ustaw jego właściwość CanCollide na fałszywą .
Utwórz instancję PathfindingModifier na części i włącz własność PassThrough.
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.
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.
Aby utworzyć PathfindingLink używając tego przykładu:
Aby pomóc w wizualizacji i debugowaniu, włącz łączeń ścieżek z w widgetu Opcje wizualizacji w górnym rogu 3D.
Utwórz dwa Attachments, jeden na siedzeniu łodzi i jeden w pobliżu punktu lądowania łodzi.
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.
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.
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ępowanialocal PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {Water = 20,UseBoat = 1}})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ępowanialocal 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.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)local waypointslocal nextWaypointIndexlocal reachedConnectionlocal blockedConnectionlocal 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 drogiwaypoints = path:GetWaypoints()-- Wybierz, czy droga stała się zablokowanablockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)-- Sprawdź, czy przeszkoda jest dalej w dół drogiif blockedWaypointIndex >= nextWaypointIndex then-- Zatrzymaj wykrywanie blokad drogi do momentu ponownego obliczenia drogiblockedConnection:Disconnect()-- Wezwij funkcję, aby ponownie obliczyć nową ścieżkęfollowPath(destination)endend)-- Wykryj, gdy ruch do następnego punktu końcowego jest ukończonyif not reachedConnection thenreachedConnection = humanoid.MoveToFinished:Connect(function(reached)if reached and nextWaypointIndex < #waypoints then-- Zwiększ wskaźnik punktu drogi i przenieś się do następnego punktu droginextWaypointIndex += 1-- Użyj łodzi, jeśli label połączenia jest "Użyj łodzi"; w przeciwnym razie przenieś się do następnego punktu połączeniaif waypoints[nextWaypointIndex].Label == "UseBoat" thenuseBoat()elsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endelsereachedConnection:Disconnect()blockedConnection:Disconnect()endend)end-- Początkowo przenieś się do drugiego punktu końca (pierwszy punkt końca to początek drogi; pomiń go)nextWaypointIndex = 2humanoid:MoveTo(waypoints[nextWaypointIndex].Position)elsewarn("Path not computed!", errorMessage)endendfunction useBoat()local boat = workspace.BoatModelhumanoid.Seated:Connect(function()-- Rozpocznij ruch łodzi, jeśli agent jest siedzonyif humanoid.Sit thentask.wait(1)boat.CylindricalConstraint.Velocity = 5end-- Wybierz pozycję ograniczenia w odniesieniu do wyspylocal boatPositionConnectionboatPositionConnection = RunService.PostSimulation:Connect(function()-- Zatrzymaj łódź, gdy jest blisko wyspyif boat.CylindricalConstraint.CurrentPosition >= 94 thenboatPositionConnection:Disconnect()boat.CylindricalConstraint.Velocity = 0task.wait(1)-- Zdejmij agent i kontynuuj podróżhumanoid.Sit = falsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endend)end)endfollowPath(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