To może być trochę banalne pytanie, ale mam problem ze zrozumieniem tego. Byłbym bardzo wdzięczny za twoją pomoc.
Podczas tworzenia gier z wykorzystaniem projektowania obiektowego chcę zrozumieć, w jaki sposób agenci AI uzyskują dostęp do informacji, których potrzebują ze świata gry, aby wykonywać swoje działania.
Jak wszyscy wiemy, w grach bardzo często agenci AI muszą „postrzegać swoje otoczenie” i działać zgodnie z tym, co się wokół nich dzieje. Na przykład agent może zostać zaprogramowany do ścigania gracza, jeśli zbliży się wystarczająco, uniknie przeszkód podczas ruchu (używając zachowania kierowniczego unikania przeszkód) itp.
Mój problem polega na tym, że nie jestem pewien, jak to zrobić. W jaki sposób agent AI może uzyskać dostęp do potrzebnych informacji na temat świata gry?
Jednym z możliwych podejść jest to, że agenci po prostu proszą o potrzebne informacje bezpośrednio ze świata gry.
Istnieje klasa o nazwie GameWorld. Obsługuje ważną logikę gry (pętla gry, wykrywanie kolizji itp.), A także zawiera odniesienia do wszystkich bytów w grze.
Mógłbym uczynić tę klasę Singletonem. Gdy agent potrzebuje informacji ze świata gry, po prostu pobiera je bezpośrednio z instancji GameWorld.
Na przykład agent może zostać zaprogramowany dla Seek
gracza, gdy jest on / ona blisko. W tym celu agent musi uzyskać pozycję gracza. Więc może po prostu poprosić go bezpośrednio: GameWorld.instance().getPlayerPosition()
.
Agent może również pobrać listę wszystkich bytów w grze i przeanalizować ją pod kątem swoich potrzeb (aby dowiedzieć się, które byty są w pobliżu lub cokolwiek innego): GameWorld.instance().getEntityList()
Jest to najprostsze podejście: agenci kontaktują się bezpośrednio z klasą GameWorld i uzyskują potrzebne informacje. Jest to jednak jedyne znane mi podejście. czy jest lepszy?
Jak zaprojektowałby to doświadczony twórca gier? Czy podejście „zdobądź listę wszystkich bytów i szukaj wszystkiego, czego potrzebujesz” jest naiwne? Jakie są podejścia i mechanizmy umożliwiające agentom AI dostęp do informacji potrzebnych do wykonywania ich działań?
źródło
Odpowiedzi:
Opisujesz klasyczny model „ściągania” zapytań o świat. W większości przypadków działa to całkiem dobrze, szczególnie w przypadku gier z podstawową sztuczną inteligencją (która jest najbardziej). Należy jednak wziąć pod uwagę kilka punktów, które mogą być wadami:
Prawdopodobnie chcesz podwoić bufor. Zobacz wzorce programowania gier na ten temat . Zawsze prosząc o dane bezpośrednio ze świata, możesz uzyskać dziwne warunki wyścigu, w których wynik zależy od kolejności, w której AI jest wywoływana. To, czy jest to ważne dla twojej gry, zależy od ciebie. Jednym z możliwych rezultatów jest to, że przesadza w grze z tym, kto idzie „pierwszy” lub „ostatni”, co czyni grę wieloosobową niesprawiedliwą.
Często może być znacznie bardziej wydajne grupowanie żądań, szczególnie w przypadku niektórych struktur danych. Tutaj możesz zmusić każdego agenta AI, który chce szukać przeszkód, stworzyć „obiekt zapytania” i zarejestrować go w centralnym singletonie przeszkód. Następnie, przed główną pętlą AI, wszystkie zapytania są uruchamiane względem struktury danych, co utrzymuje strukturę danych przeszkód bardziej w pamięci podręcznej. Następnie podczas części sztucznej inteligencji każdy agent przetwarza wyniki zapytań, ale nie wolno wykonywać żadnych dalszych bezpośrednio. Na końcu ramki obiekty AI aktualizują zapytania o nową lokalizację lub dodają lub usuwają je. Jest to podobne do projektowania zorientowanego na dane .
Zauważ, że w zasadzie powoduje to podwójne buforowanie poprzez przechowywanie wyników zapytań w buforze. Wymaga to również uprzedniego przewidywania, czy należy wykonać zapytanie do ramki wcześniej. Jest to model „push”, ponieważ agenci deklarują, jakiego rodzaju aktualizacjami są zainteresowani (tworząc odpowiedni obiekt zapytania), a te aktualizacje są do nich przekazywane. Uwaga: obiekt zapytania może także zawierać wywołanie zwrotne, zamiast przechowywać wszystkie wyniki dla ramki.
Wreszcie, prawdopodobnie chcesz używać interfejsów lub komponentów do przeszukiwalnych obiektów zamiast dziedziczenia, co jest dobrze udokumentowane gdzie indziej. Iteracja nad listy
Entities
kontroliinstanceOf
jest prawdopodobnie przepis na kodzie dość kruche, minuta chcesz zarównoStaticObject
iMovingObject
byćHealable
. (chyba żeinstanceOf
działa dla interfejsów w wybranym języku).źródło
Ponieważ AI jest kosztowne, wydajność jest często czynnikiem napędzającym architekturę.
Aby złagodzić obawy związane z modelami dostępu do danych, rozważmy kilka różnych przykładów sztucznej inteligencji zarówno w branży gier, jak i poza nią, poczynając od tego, co jest najbardziej oddalone od ludzkiej nawigacji do tego, co jest nam najbardziej znane.
(Każdy przykład zakłada pojedynczą globalną aktualizację logiki).
* Najkrótsza ścieżkaKażda sztuczna inteligencja oblicza stan mapy do celów wyszukiwania ścieżek. * Wymaga, aby każda AI znała już całe (lokalne) środowisko, w którym musi znaleźć ścieżkę, dlatego musimy przekazać mu informacje o przeszkodach i przestrzeni na mapie (często tablica boolowska 2D). A * to wyspecjalizowana forma algorytmu Dijkstry, algorytmu wyszukiwania z najkrótszą ścieżką; takie podejścia zwracają listy reprezentujące ścieżkę, a na każdym kroku AI po prostu wybiera następny węzeł na tej liście, aby przejść do celu, aż osiągnie swój cel lub wymagane jest ponowne obliczenie (np. z powodu zmiany przeszkody na mapie). Bez tej wiedzy nie można znaleźć realistycznej najkrótszej ścieżki. A * to dlaczego AI w grach RTS zawsze wiedzą, jak dostać się z punktu A do punktu B - jeśli istnieje ścieżka. Ponownie obliczy najkrótszą ścieżkę dla każdej pojedynczej AI, ponieważ ważność ścieżki zależy od pozycji AI, które wcześniej się poruszały (i potencjalnie blokowały określone ścieżki). Iteracyjny proces, w którym A * oblicza wartości komórek podczas szukania ścieżki, jest konwergencją matematyczną. Można powiedzieć, że efekt końcowy przypomina węch zmieszany z zmysłami wzroku, ale w ogóle jest nieco obcy dla naszego sposobu myślenia.
Współdziałanie dyfuzji Znalezione również w grach, najbardziej przypomina zapach, oparty na dyfuzji gazów i cząstek stałych. Płyta CD rozwiązuje problem kosztownego ponownego przetwarzania znalezionego w A *: Zamiast tego przechowywany jest pojedynczy stan mapy, przetwarzany raz na aktualizację dla wszystkich AI, a następnie wyniki są dostępne dla każdego AI kolejno, aby mógł wykonać odpowiedni ruch . Algorytm wyszukiwania nie zwraca już pojedynczej ścieżki (listy komórek); raczej każda AI sprawdzi mapę po jej przetworzeniu i przejdzie do dowolnej sąsiedniej komórki o największej wartości. To się nazywa wspinaczka . Niemniej jednak faza przetwarzania mapy musi już istniećmieć wcześniejszy dostęp do informacji na mapie, która zawiera także lokalizacje wszystkich ciał AI. Dlatego mapa odwołuje się do AI, a następnie AI do mapy.
Wizja komputerowa i raycasting + najkrótsza ścieżka W robotyce łazika i dronów normą jest określanie zasięgu przestrzeni, po których porusza się robot. Pozwala to robotom skonstruować pełny model wolumetryczny otoczenia, tak jak robilibyśmy to przez wzrok, a nawet dźwięk lub dotyk (dla osób niewidomych lub głuchych), które robot może następnie zredukować do minimalnego wykresu topograficznego (trochę jak wykres punktu orientacyjnego stosowane w grach z A *), na których można zastosować algorytmy najkrótszej ścieżki. W tym przypadku, chociaż „widzenie” może dostarczyć wskazówek dla najbliższego otoczenia, nadal skutkujewyszukiwanie na wykresie, które ostatecznie zapewnia ścieżkę do celu. Jest to bliskie ludzkiej myśli: aby dotrzeć do kuchni z sypialni, muszę przejść przez salon. Fakt, że już je widziałem i znam ich przestrzenie i portale, jest tym, co umożliwia ten wyliczony ruch. Jest to topologia grafu, do której stosuje się algorytm najkrótszej ścieżki, choć osadzony raczej w miękkim białku niż w twardym krzemie.
Możesz więc zobaczyć, że pierwsze dwa polegają na znajomości środowiska w całości. Jest to powszechne w grach ze względu na koszt oceny środowiska od zera. Najwyraźniej ta ostatnia jest najsilniejsza. Robot wyposażony w ten sposób (lub na przykład sztuczna inteligencja gry, która odczytuje bufor głębokości każdej klatki) może nawigować w wystarczającym stopniu w dowolnym środowisku bez wcześniejszej wiedzy o tym. Jak zapewne się domyślasz, jest to również zdecydowanie najbardziej kosztowne z powyższych trzech podejść, aw grach zazwyczaj nie możemy sobie na to pozwolić w przeliczeniu na AI. Oczywiście w 2D jest znacznie tańszy niż w 3D.
Punkty architektoniczne
Wyraźnie widać powyżej, że nie możemy założyć tylko jednego prawidłowego wzorca dostępu do danych dla AI; wybór zależy od tego, co próbujesz osiągnąć.
GameWorld
Bezpośredni dostęp do klasy jest absolutnie standardowy: po prostu zapewnia informacje o świecie. Zasadniczo jest to twój model danych i do tego służą modele danych. Singleton jest do tego odpowiedni.Nie ma w tym nic naiwnego. Jedyną rzeczą, która może być naiwna, jest wykonywanie większej liczby iteracji list niż jest to konieczne. W wykrywaniu kolizji unikamy tego, stosując np. Kwadraty w celu zmniejszenia przestrzeni wyszukiwania. Podobne mechanizmy mogą mieć zastosowanie do AI. A jeśli możesz udostępniać tę samą pętlę, aby robić wiele rzeczy, zrób to, ponieważ oddziały są kosztowne.
źródło
GameWorld
klasę jako klasę zawierającą odniesienia do wszystkie elementy gry, a także zawiera większość ważnej logiki „silnika”: główna pętla gry, wykrywanie kolizji itp. Jest to w zasadzie „główna klasa” gry. Moje pytanie brzmi: czy takie podejście jest powszechne w grach? Masz „klasę główną”? Czy powinienem podzielić go na mniejsze klasy i mieć jedną klasę, ponieważ obiekty „bazy danych encji” mogą sondować?Zasadniczo miałbym 2 sposoby wyszukiwania informacji.
gdy zmienia się AIState, ponieważ wykryłeś kolizję lub inną pamięć podręczną, odniesienie do dowolnego obiektu jest ważne. W ten sposób wiesz, jakie referencje potrzebujesz. Kiedy inne systemy muszą uruchamiać duże wyszukiwania w każdej ramce, zalecam wycofanie się z nich, abyś nie musiał wykonywać wielu wyszukiwań. Wykryto więc „kolizję” ze strefą, która powoduje, że wróg „ostrzega”, wysyła mu wiadomość / zdarzenie, które rejestruje go w tym obiekcie, jeśli jeszcze go nie ma, i zmienia stan gry na taki, w którym wykonuje swoją działalność w oparciu o ten gamestate. Potrzebujesz jakiegoś rodzaju zdarzenia, które każe ci dokonać zmian, po prostu przekażę odwołanie do dowolnego wywołania zwrotnego, którego użyjesz do podania tych informacji. Jest to bardziej rozszerzalne niż samo zajmowanie się graczem. Może chcesz, aby wróg ścigał innego wroga lub inny obiekt. W ten sposób musisz tylko zmienić tag, pod którym go identyfikujesz.
Dzięki tym informacjom wykonasz zapytanie do systemu wyszukiwania ścieżek, który używa A * lub innego algorytmu, aby podać ścieżkę, lub możesz użyć jej z pewnym zachowaniem sterowania. Może połączenie obu lub cokolwiek innego. Zasadniczo dzięki transformacji obu powinieneś być w stanie zapytać system węzła lub navmesh i dać ci ścieżkę. Twój świat gry prawdopodobnie ma wiele innych rzeczy niż szukanie ścieżek. Prześlę twoje zapytanie tylko w poszukiwaniu ścieżki. Również grupowanie tych rzeczy jest prawdopodobnie najlepsze, jeśli masz wiele zapytań, ponieważ może to być dość intensywne, a grupowanie poprawi wydajność.
źródło