Algorytm dynamicznej ścieżki do gry typu tower defense

16

Robię Tower Defense i potrzebuję do tego dobrego algorytmu ścieżki.

Myślałem o Dijkstrze, ale potrzebuję takiego, który może być dynamiczny; musi być w stanie aktualizować się, gdy jedna krawędź zostanie usunięta lub dodana bez pełnego ponownego obliczenia.

Koduję w C #, jeśli to pomaga.

Dani
źródło

Odpowiedzi:

8

Zazwyczaj będziesz chciał użyć A *, chyba że szukasz czegoś istotnie innego.

John Haugeland
źródło
Jeśli nie znam się tak dobrze na A *, jak mam rozwiązać dynamicznie zmieniające się środowiska? Przelicz ponownie przy każdej klatce? W zderzeniach?
Justin L.,
1
Ogólnie rzecz biorąc, ścieżka byłaby zmieniona w tak prostej grze. Siatka mapy będzie prawdopodobnie bardzo mała, a liczba agentów co najwyżej w setkach. Jeśli okaże się, że jest to ogromny problem z wydajnością, możesz go później zoptymalizować, nie odbudowując drzewa, chyba że zajdzie taka potrzeba.
coderanger
6
Jeśli jest powolny, moje zalecenia: nie obliczaj ścieżki dla każdego moba. Prawdopodobnie wszyscy podążają tą samą ścieżką. Przeliczę tylko na nowe ustawienie wieży, a wieża jest na bieżącej ścieżce . Nawet wtedy, wykonuj ponowną kalkulację tylko dla potworów w punktach na ścieżce przed kwadratem bloku, nie dla mobów, które go miną i dlatego ich to nie obchodzi. Następnie, aby skrócić znajdowanie ścieżki, możesz ograniczyć ją do znalezienia drogi do kwadratu po zablokowanym kwadracie, ponieważ już znasz ścieżkę stamtąd.
seanmonstar
4
W rzeczywistości „mob” to żargon branżowy (i MMO / MUD) dla „obiektu mobilnego”.
3
Niezależnie od tego jest długoletni, pochodzi z MUD1 i jest na tyle standardowy, że pojawił się w wielu publikacjach. en.wikipedia.org/wiki/Mob_%28computer_gaming%29
19

Musiałem rozwiązać podobny problem: znalezienie ścieżki na dużej siatce przypominającej labirynt ze stale zmieniającymi się „kosztami” i barierami.

Chodzi o to, że w grze typu tower defense liczba podmiotów, które muszą mieć dla nich rozwiązaną ścieżkę, jest zwykle znacznie większa niż liczba węzłów na wykresie. * Nie jest najodpowiedniejszym algorytmem do obsługi tego, ponieważ będziesz musiał go rozwiązać od nowa za każdym razem, gdy coś zostanie zmienione. Dobrze jest, jeśli chcesz znaleźć tylko jedną ścieżkę, ale w moim przypadku musiałem być w stanie obsłużyć byty, które mogą pojawiać się w różnych lokalizacjach, a każda z nich ma swoją własną ścieżkę.

Floyd-Warshall algorytm jest bardziej właściwe, choć na moim przypadku Napisałem algorytm niestandardowy że gdy zmiany węzłowe, to ponownie oblicza koszt dla tego węzła z wszystkimi sąsiadami, a następnie , jeśli sąsiedzi zostały zmienione to jest wywoływany rekurencyjnie na nich.

Tak więc na początku gry odpalam ten algorytm na wszystkich moich węzłach „celowych”. Następnie, za każdym razem, gdy zmienia się pojedynczy węzeł (na przykład staje się nie do przejścia), po prostu odpalam go w tym węźle, a zmiana jest propagowana do wszystkich węzłów, które zostaną zmienione, a następnie zatrzymana. Nie ma więc potrzeby globalnego ponownego obliczania, a algorytm jest całkowicie niezależny od liczby jednostek, które będą wymagać szukania ścieżki.

Mój algorytm był w zasadzie czymś w rodzaju (pseudo-kod):

update_node method in Node class:
    $old <- $my_score
    find $neighbor node among all neighbors such that
        $neighbor.score + distance_to($neighbor) is minimal
    $my_score <- $neighbor.score + distance_to($neighbor)
    $next_node <- $neighbor
    if ($my_score != $old)
        for each $neighbor
            $neighbor.update_node()

Z początkowym wynikiem zależy od tego, czy węzeł jest celem, czy jakąś barierą.

Dąb
źródło
1
Łatwo byłoby również dynamicznie rozłożyć to na ramki, gdy zmienia się obciążenie procesora.
tenpn
+1 za A * nie jest właściwym algorytmem do użycia.
Ricket
5

Algorytm trasy, którego użyłem na moim niszczycielu czołgów, był cofnięty od normalnej ścieżki A * ze względu na liczbę jednostek, które miałem w grze. Zamiast kierować się od bramki do złych facetów, kierowałem od bramki do każdego pustego pola na planszy.

Nie trwa to długo, po prostu iterujesz planszę, dopóki nie znajdziesz swoich „kosztów”, i zapewnia dobrą informację zwrotną dla zablokowanych tras (jeśli je wykonujesz). Korzystanie z algorytmu Floyda jest szybkie i przyjazne dla pamięci podręcznej w porównaniu do A *, ponieważ nie wykonuje wyszukiwania zależnego od danych, po prostu ładuje i działa na danych w strumieniach.

Najpierw ustaw planszę na nieskończony koszt, następnie ustaw cel docelowy na zero, a następnie iteruj po planszy, aby sprawdzić, czy sąsiednia komórka kosztuje mniej niż aktualny koszt plus koszt podróży. Koszt podróży to miejsce, w którym umieszczasz swoją heurystykę (w moim przypadku koszt podróży po przekątnej był nieskończony, ale koszt podróży przez wieżę był wysoki, więc mogli jeść przez wieże, ale nie zrobili tego, chyba że nie mieli wybór)

Gdy już masz siatkę kosztów, możesz szybko zbudować siatkę „przepływową”, testując najbardziej stromy gradient kosztów na komórkach. Działa to naprawdę dobrze w przypadku ogromnej liczby złych facetów, ponieważ żaden z nich nigdy nie musi szukać ścieżki, po prostu podążają za wirtualnymi drogowskazami.

Kolejną zaletą jest to, że w ten sposób musisz wykonywać to zadanie za każdym razem, gdy dostosowujesz przeszkody (lub w mojej grze, gdy pełzanie zjada jedną z twoich wież). Nie za każdym razem, gdy pełzacz / mob wkracza na boisko (co przy tysiącach mobów i dziesiątkach na sekundę byłoby dość kłopotem).

Richard Fabian
źródło
4

Wyszukiwanie ścieżek jest szybkie i na czymś takim, jak w normalnej grze w obronę wieży, nie będziesz miał problemów z uruchomieniem pełnego podania A * lub Dijkstra, gdy coś się zmieni. Rozmawiamy dobrze w czasie krótszym niż milisekunda, aby uzyskać pełne odświeżenie. Wszelkie adaptacyjne wyszukiwanie ścieżek kończy się przerażająco skomplikowane. Po prostu zrób to w prosty sposób, chyba że tworzysz największą na świecie siatkę obrony wieży.

ZorbaTHut
źródło
1
Warto zauważyć, że Tower Defense jest popularny w telefonach komórkowych, w których wyszukiwanie ścieżek nie jest szybkie. Zwłaszcza na nieco starszych urządzeniach, takich jak Sidekick lub Android 1.6.
seanmonstar
1
Od dziesięcioleci programiści używają algorytmów wyszukiwania ścieżek, takich jak A * i Dijkstra, na znacznie mniej wydajnym sprzęcie (pomyśl Game Boy). Każdy telefon wystarczająco nowy, aby mieć ekran odpowiedni do grania, nie powinien mieć problemu, pod warunkiem oczywiście, że wdrożenie jest dość wydajne. Omówiono tutaj kilka fajnych
Mike Strobel
+1. Doszedłem do tego samego wniosku, gdy rozwijałem własny klon DTD. Próbowałem stopniowo aktualizować ścieżki, ale algorytm stał się zbyt skomplikowany. Po spędzeniu nad tym dnia zmieniłem pełne przeliczanie każdej zmiany. To zadziałało, a po kilku poprawkach udało mi się zrobić to wystarczająco szybko.
finnw
2

Może to być przesada dla twojego rozwiązania, ale pomyśl o routingu do tyłu zamiast do przodu.

W grze TD wrogowie zazwyczaj starają się dotrzeć do określonego celu. Kieruj się wstecz od tego celu do pierwszego wroga, a następnie buforuj tę ścieżkę. Podczas generowania ścieżki dla kolejnych wrogów użyj pierwszej ścieżki jako punktu początkowego i tak dalej. Jeśli masz wiele punktów wejścia wroga lub wiele celów, możesz zacząć od wielu wstępnie zbuforowanych ścieżek.

tenpn
źródło
1

Odnajdywanie ścieżek waypointów byłoby prawdopodobnie najlepsze dla gry td, ponieważ ogólnie na podstawie poziomu ai podąża bezpośrednią ścieżką. Zasadniczo ustawiasz swoje węzły lub punkty drogi, a następnie masz punkt „ai” w kierunku trasy i podchodzisz do niego, gdy tylko zbliży się wystarczająco, aby przejść do następnego punktu drogi, skieruj go twarzą do niego i przejdź do niego.

Loktar
źródło
Działa to tylko w przypadku gier TD, w których można umieszczać wieżyczki tylko z boku ścieżki - nie w grach, które umożliwiają umieszczanie wież w dowolnym miejscu na planszy.
Iain
tak, początkowo autor nie określił, jakiego typu chce, więc założyłem, że nie jest dynamiczny.
Loktar
1

Ponieważ ktoś zapytał, zwracasz się do dynamicznie zmieniających się środowisk, ponownie obliczając ścieżkę za każdym razem, gdy gracz umieszcza lub usuwa wieżę, i po prostu przechowujesz tę ścieżkę w pamięci w międzyczasie. Środowisko nie zmienia się w każdej klatce.

Pamiętaj, że niektóre gry TD mają ustaloną ścieżkę i nie pozwalają na umieszczanie na niej wież, więc rozwiązują łatwe znajdowanie ścieżek: wpisując ścieżkę na stałe i nie pozwalając jej zablokować :)

Ian Schreiber
źródło
1

Prostym rozwiązaniem jest oszukiwanie.

Zaprojektuj mapę wcześniej, aby upewnić się, że nie ma ślepych uliczek. Następnie na każdym skrzyżowaniu dodaj wyzwalacz, który sprawia, że ​​postać wybiera trasę (przykład: zawsze skręć w lewo, zawsze skręć w prawo lub losowo).

MrValdez
źródło
1
Przypuszczalnie mówi o czymś takim jak Desktop TD, gdzie użytkownik tworzy mapę w locie. Wciąż dla „głupich” wrogów, co może być dobrym rozwiązaniem, dzięki czemu można sprawić, że wrogowie z lepszą AI będą nieco trudniejsi.
coderanger