Jak zaimplementować zachowanie w architekturze gier opartej na komponentach?

21

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.

Schemat komponentów

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ść?

duch
źródło
Myślę, że dobra odpowiedź będzie zależeć od tego, gdzie chcesz egzekwować wyłączność działań. Jeśli chcesz, aby znajdował się w samych obiektach, projekt byłby znacznie inny w porównaniu do powiedzenia, wymuszając go przez interfejs przeciągnij i upuść (Ten stan ma już akcję ruchu, więc nie może mieć innego, ten kontener przejścia stanu już zawiera stan końcowy oparty na czasie itp.
James
2 nie jest realną opcją.
Kojot

Odpowiedzi:

6

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ść.

Tesserex
źródło
Czy wzorzec stanu nie implikuje pierwszej sytuacji? Powoduje to, że pojedynczy komponent AIC implementuje maszynę stanu zawierającą różne obiekty stanu. Drugą opcją było to, że WalkComponent i SwingComponent są tego samego typu co, powiedzmy, RenderComponent i PhysicsComponent.
duch
@ghostonline Jeśli chodzi o pomysł, w pewnym sensie. W realizacji nie bardzo. AIComponent byłby osobny, jak na drugim schemacie. Nie zawierałby innych składników. Ważniejszym pytaniem dla twojej drugiej sytuacji jest to, że jeśli projektant wybiera komponenty bez programisty, to skąd Entity wie, kiedy zmienić stan? Różne stany implikują różne przejścia stanów - ktoś nadal musi je określić.
Tesserex,
Czy masz na myśli dodanie AIComponent do bytu na schemacie 2, który będzie kontrolował Stand / Walk / Swing-Component? Mój pomysł polegał na tym, że komponenty wysyłają sygnały blokowe lub aktywacyjne pod pewnymi warunkami. Na przykład SwingComponent emitowałby ogólne sygnały, na przykład sygnał „bound_feet” przy uruchomieniu i „release_feet” po zakończeniu zamachu. WalkComponent wyłączy się i włączy na podstawie tych sygnałów. Ponieważ „przejścia stanu” są zamknięte w samych komponentach, projektant nie będzie potrzebował programisty łączącego komponenty razem.
duch
@ghostonline Działa to dobrze w przypadku rzeczy, które mają ustalone zasady, takie jak „nie można chodzić podczas huśtania się”, ale co z przejściami między staniem a chodzeniem? Jeśli stanie jest pod kontrolą, skąd będzie wiedział, że możesz chodzić? Logika stojąca może chcieć wybrać spacer lub huśtawkę, na co wpływa całkowity brak zdolności chodzenia - w takim przypadku zawsze powinna ona wybrać huśtanie. Ale myślę, że jesteś na dobrej drodze.
Tesserex,
2

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ą.

Gregory Avery-Weir
źródło
Nie rozdzieliłbym tak bardzo ruchu NPC i ruchu gracza. Ruchy, które napędzają animacje i fizykę, można zdecydowanie podzielić; to, co wybiera działania lub przejścia, jest różne (gracz przyjmuje dane wejściowe, gdy AI jest ... AI). Zgadzam się, że masz PlayerController, ale masz również AIController, z których oba mogłyby korzystać z komponentów ruchu / komponentów huśtawki, aby wykonać rzeczywistą pracę nad animacją / fizyką.
homebrew
Prawdziwe. Zakładam, że wszystkie poruszające się obiekty mają PhysicsComponent lub MovementComponent, który obsługuje ich ruch, i że PlayerController i AIController użyją tego do obsługi ruchu. Ruch powinien zdecydowanie stanowić oddzielny element, ponieważ mogą istnieć rzeczy, które muszą się poruszać, które nie mają sztucznej inteligencji lub mają najprostszą możliwą sztuczną inteligencję (głupie obiekty fizyczne, takie jak skrzynki lub śmieci).
Gregory Avery-Weir
2

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.

Kyle C.
źródło
1

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.

Stephen
źródło
1
TYPO: „Otrzyma ...” co ze składnika AI?
Pup