Zaczynam wdrażać sztuczną inteligencję gracza i wroga w grze, ale nie jestem pewien, jak najlepiej wdrożyć to w architekturze gry opartej na komponentach.
Powiedzmy, że mam następującą postać gracza, która może być nieruchoma, biegać i wymachiwać mieczem. Gracz może przejść do stanu miecza huśtawkowego zarówno ze stanu nieruchomego, jak i biegowego, ale wtedy huśtawka musi zostać zakończona, zanim gracz będzie mógł wznowić stanięcie lub bieganie. Podczas zamachu gracz nie może chodzić.
Widzę dwa podejścia do implementacji:
- Utwórz pojedynczy komponent AI zawierający całą logikę gracza (albo oddzielony od rzeczywistego komponentu, albo osadzony jako PlayerAIComponent). Potrafię w łatwy sposób egzekwować ograniczenia stanu bez tworzenia powiązań między poszczególnymi komponentami tworzącymi jednostkę gracza. Jednak składnika AI nie można rozbić. Jeśli mam na przykład wroga, który może tylko stać i chodzić lub tylko chodzić i czasami machać mieczem, muszę stworzyć nowe elementy AI.
- Podziel zachowanie na komponenty, z których każdy identyfikuje określony stan. Następnie otrzymuję StandComponent, WalkComponent i SwingComponent. Aby wymusić reguły przejścia, muszę połączyć każdy komponent. SwingComponent musi wyłączyć StandComponent i WalkComponent na czas trwania zamachu. Kiedy mam wroga, który tylko stoi i od czasu do czasu wymachuje mieczem, muszę się upewnić, że SwingComponent wyłącza WalkComponent tylko, jeśli jest obecny. Chociaż pozwala to na lepsze mieszanie i dopasowywanie komponentów, może to prowadzić do koszmaru utrzymania, ponieważ za każdym razem, gdy dodawana jest zależność, istniejące komponenty muszą być aktualizowane, aby ładnie grać z nowymi wymaganiami zależnymi od postaci.
Idealna sytuacja byłaby taka, że projektant może budować nowych wrogów / graczy, przeciągając komponenty do kontenera, bez konieczności dotykania jednej linii kodu silnika lub skryptu. Chociaż nie jestem pewien, czy można uniknąć kodowania skryptów, chcę, aby było to tak proste, jak to możliwe.
Podsumowując: czy powinienem lobować całą logikę AI na jeden komponent, czy też rozbić każdy stan logiki na osobne komponenty, aby łatwiej tworzyć warianty jednostek?
edytuj : Podejrzewam, że istnieje pewne zamieszanie co do tego, co miałem na myśli z pierwszą i drugą sytuacją. Próbowałem to wyjaśnić na poniższym schemacie.
Zwróć uwagę na związek między poszczególnymi stanami a bytem. W pierwszej sytuacji komponent AI jest wstępnie budowany, zanim zostanie umieszczony w jednostce. Projektant może wybierać tylko z odrębnego zestawu komponentów AIC udostępnionego przez programistę. Druga sytuacja ma różne stany na tym samym poziomie, co inne komponenty. Projektant może teraz tworzyć byty z unikalną sztuczną inteligencją bez ingerencji programisty.
Pytanie brzmi: czy są to jedyne dwie opcje konstruowania sztucznej inteligencji w jednostce opartej na komponentach, a jeśli tak, to co zapewni maksymalną elastyczność?
Odpowiedzi:
Jeśli zamierzasz mieć więcej potencjalnych wrogów lub graczy, których nie możesz sobie teraz wyobrazić, zdecydowanie powinieneś to rozdzielić. To, co opisujesz w drugim punkcie, to w zasadzie wzorzec stanu .
Wydaje mi się, że zgadzam się z Gregory, że nie powinieneś mieć osobnych komponentów stanu i chodzenia. Jest to po prostu komponent ruchu o prędkości 0. Z drugiej strony, jeśli masz obiekty, które nie mogą się poruszać, albo musisz je rozdzielić, albo po prostu wprowadzić jakieś ograniczenie boolowskie w stanie ruchu, który zapobiega niezerowej prędkości .
Dla gracza nie sądzę, że musi być całkowicie osobny. Nadal może korzystać ze wszystkich innych komponentów, z dodatkiem komponentu wejściowego. Ten komponent steruje przejściami między stanami, podczas gdy u wroga jest kontrolowany przez domyślną sztuczną inteligencję lub, jeśli chcesz, różne podklasy AI, z których mogą wybierać twoi projektanci wroga.
edytuj: w rzeczywistości dla swoich stacjonarnych wrogów, zamiast ograniczać komponent ruchu, po prostu daj mu stacjonarny komponent AI, który nigdy nie zdecyduje się go przenieść.
źródło
Przynajmniej zatrzymałbym AI gracza (lub coś, co nazwałbym Player Controller) jako swój własny komponent. W większości gier gracz zasadniczo różni się od NPC, których nie można generalizować od jednego do drugiego, z wyjątkiem podstaw takich jak punkty życia.
W przypadku postaci niezależnych widzę StandComponent i WalkComponent jako aspekty tego samego. Czy kiedykolwiek będziesz miał WalkComponent bez StandComponent? Wątpię. Podobnie RunComponent byłby po prostu WalkComponent o większej prędkości i różnych animacjach. Widzę wartość posiadania NPCMovementComponent i osobnego NPCSwordFighterComponent, ale nawet to wydaje mi się nadmierną inżynierią.
źródło
Najpierw utworzę składnik stanu, a następnie utworzę maszynę stanu do obsługi przejść. Spraw, aby był on wystarczająco ogólny, abyś mógł go użyć dla swoich graczy i AI. Dzięki temu AI będzie grało według tych samych zasad i nie będziesz musiał zmieniać logiki, kiedy zmienisz sposób działania stanów gracza w porównaniu do stanów AI.
Maszyna stanów skończonych C ++
Powyżej ma konkretny przykład automatu stanowego w c ++, który może być używany zarówno przez graczy, jak i AI.
źródło
To, czego chcesz, to komponent obsługujący ruch postaci (gracza i NPC). Komponent AI lub komponent gracza wyśle polecenia do tego komponentu ruchu i sprawdzi, czy można zainicjować akcję. Spowoduje to zamknięcie ograniczeń ruchu w jednym komponencie. Twój kod AI i kod gracza nie muszą wiedzieć, jak wykonuje się miecz huśtawkowy. AI miałaby stany wewnętrzne, np. Bezczynność, Atak, Ucieczka.
źródło