Stan gry i obsługa danych wejściowych w systemach jednostek opartych na komponentach

16

Moje pytanie brzmi:

Jak poradzić sobie ze stanami gry w moim systemie encji bez uciekania się do utrzymywania stosu obiektów stanu gry w pobliżu?

Tak więc projekt mojego systemu encji oznacza, że ​​gdy encja musi się zarejestrować na przykład dla zdarzeń wejściowych, komponent wejściowy wywołuje system wejściowy i mówi „zarejestruj tę encję dla tego wejściowego”. Wszystko jest w porządku i dobrze, jednak jeśli dodasz do tego pojęcie stanów gry (powiedzmy ekran pauzy), trudno będzie ustalić, czy jednostka jest w obecnym stanie i powinna otrzymać dane wejściowe.

Mógłbym ulepszyć komponent wejściowy / system tak, aby powiedział: „zarejestruj ten byt dla tego wejścia w tych stanach gry”, ale wymaga to, aby każdy byt wiedział, w których stanach będzie używany, i może to nie być oczywiste. Również utrzymywanie listy stanów gry wokół zarejestrowanych danych wejściowych (i innych systemów korzystających z wywołań zwrotnych) nie wydaje się zbyt wydajne.

Innym pomysłem, jaki miałem, było istnienie bytu reprezentującego stan gry, oznaczenie tego jako wyłączone, a następnie podczas generowania zdarzenia wejściowego sprawdź, czy byt nie jest potomkiem wyłączonego bytu stanu gry. Kosztowne jest ustalenie rodzica dla każdego oddzwaniania.

Innym pomysłem jest, aby wszystkie systemy zapisywały swoje dane w kluczach względem bieżącego stanu, w ten sposób podczas generowania danych wejściowych jednostka docelowa nawet nie będzie kandydatem. Jednak to naprawdę szkodzi możliwości umożliwienia komunikacji między bytami w różnych stanach (nie jest to tak bardzo problemem dla ekranów pauzy, ale pomyśl wybieranie zamka w Oblivion / Skyrim).

Jedynym innym pomysłem, jaki miałem, było to, aby wszystkie komponenty obsługiwały zdarzenia związane ze zmianą stanu i komunikowały się z odpowiednim systemem, aby wyłączyć wszystko, co zarejestrowały, i włączyć je ponownie po przełączeniu do tego stanu.

Drugi (oznacz obiekt jako wyłączony) i czwarty (każą każdemu komponentowi radzić sobie ze zmianami stanu) wydają się najlepszymi z moich pomysłów, ale żaden z nich nie wyskakuje na mnie jako wyjątkowo dobry.

Czy ktoś jeszcze ma jakieś pomysły, jak to zrobić?

edytuj Mówiąc o danych wejściowych w tym pytaniu, może to oznaczać dowolny system zdolny do wysyłania wiadomości / zdarzeń do podmiotów, takich jak kolizje, zdarzenia czasowe itp.

elFarto
źródło
6
Robię to w ten sposób: mam Ekrany, MenuScreen PauseScreen GameScreen, każdy ekran może stworzyć własny Świat (kontener dla Entities) i systemów (takich jak RenderingSystem), a następnie w GameScreen tworzę World, Entity z CameraComponent i ustawiam CameraComponent.RenderTarget na ekrany tła. W ten sposób mogę dodać InventoryScreen, który będzie miał własne encje i systemy (takie jak uproszczony renderer). Dane wejściowe mogą być przekazywane z ekranu na świat, więc interfejs użytkownika zadecyduje, czy przekaże dane wejściowe do ekranu (jeśli jest skoncentrowany, widoczny itp.), A dane wejściowe zostaną przekazane światu i
bytom
2
@ Byte56 Nie bardzo, tylko pierwszy ma związek z gamestate (pozostałe 2 to stany wewnątrz podmiotów) i to tak naprawdę nie rozwiązuje tego samego problemu, który mam. Kiedy gra jest w stanie wstrzymania, coś musi się stać z systemem wejściowym, aby zatrzymać wysyłanie komunikatów ruchowych do jednostki gracza (na przykład), po prostu nie mogę znaleźć dobrego sposobu, aby to zrobić.
elFarto
1
OK, uważaj je za powiązane. Dobre pytanie.
MichaelHouse
1
Coś innego, co należy wziąć pod uwagę, które w przeszłości było uciążliwe dla moich systemów opartych na komponentach: wielowarstwowy interfejs użytkownika. Okno dialogowe pojawiające się na ekranach światowych lub wielopoziomowych. Do tej pory pojawiał się w każdej mojej grze, więc powiedziałbym, aby rozważyć podejście, które może rozwiązać ten problem.
ADB

Odpowiedzi:

14

Często używany jest półprodukt, Intent Systemktóry wyodrębnia dane wejściowe i śledzi kontekst i odpowiednie gry.

System Intent przestanie na przykład przekazywać sygnały wejściowe, gdy symulacja zostanie na przykład wstrzymana. Obsługuje również mapowanie między zdarzeniami kontrolera a zamiarami (poruszanie się w kierunku, bieganie, strzelanie, przeładowanie ...).

W ten sposób twoje inne elementy nie są zależne od konkretnych gamepadów / wejść (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O ...), ale wszystkie reagują na te same zamiary (IntentRun, IntentReload ...).

Kolejną zaletą jest to, że system intencji może być świadomy dodawania / usuwania dostępnych kontrolerów, ponieważ może wysyłać intencje do dowolnego subskrybenta, nawet poza symulacją, z którą można sobie poradzić AddPlayer(controllerID).

Ile informacji o stanie gry przekazujesz systemowi poprzez zdarzenia / wiadomość lub bezpośrednio od Ciebie. Ale czas zainwestowany w system Intent jest zwykle tego wart.

Możesz zarządzać kontekstami intencji, które generują intencje, gdy są one dołączone do systemu.

Kontekst może mieć priorytet, tj .:

  • SimulationAvailableContext wysyła zamiary do symulacji, gdy jest ona dostępna (ale nie jest uruchomiona), na przykład przesuń kamerę, powiększ, pomniejsz, dodaj / usuń odtwarzacz ...
  • SimulationRunningContext wysyła zamiary do symulacji, gdy nie jest wstrzymany ruch gracza, wysyłanie jednostki na pozycję, strzelanie ...

W ten sposób możesz dodawać i usuwać konteksty, które są obecnie istotne.

Jedną rzeczą dotyczącą całego systemu intencji jest to, że powinien on działać, gdy symulacja jest wstrzymana.

Jednym ze sposobów, który jest często używany do grania / wstrzymywania symulacji gry bez przerywania aktualizacji niezwiązanych z symulacją, jest użycie innego zestawu czasów. tj GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).

Dzięki takiemu podejściu silnik może po prostu blokować przyrosty czasu simTime gier, co z kolei blokuje aktualizacje odpowiednich silników animacji i fizyki, które używają, simTime and simDeltaTimejednocześnie umożliwiając ciągłe aktualizacje efektu sprężyny kamery, jeśli musi się poruszać nawet podczas pauzy, animacja efekt ładowania na wirtualnym billboardzie w grze podczas pobierania danych ...

Kojot
źródło
Podoba mi się fakt, że nie musi to wywoływać wielu funkcji „Zmienionych przez państwo” na wszystkich obiektach. Musisz się martwić, że niewłaściwe zamiary zostaną wysłane w niewłaściwym czasie, ale myślę, że to lepsze niż alternatywa.
Thomas Marnell,
twoje istoty mogą ignorować zamiary takie jak Skok, podczas gdy ich stan nie pozwala im skakać (tj. nie dotykać ziemi). ale nie muszą się martwić otrzymaniem takich zamiarów, gdy gra jest wstrzymana.
Coyote
Myślałem już o tym, by istota powiedziała systemowi wejściowemu, w jakich stanach ma dostarczać wiadomości, ale nie pomyślałem o umieszczeniu stanów na samym wejściu, co jest dobrym pomysłem. Przyjemny jest również podział czasu i czasu simTime.
elFarto
Powinieneś unikać rozdęcia stanu związanego z symulacją przez rzeczy niezwiązane z symulacją. Przenieś cały interfejs użytkownika i kod związany z odtwarzaczem jak najdalej od samej symulacji, aw symulacji skoncentruj się tylko na zamiarach.
Coyote
Hej @Coyote, ten system brzmi bardzo interesująco. Czy mógłbyś podać więcej informacji, odpowiadając na to pytanie ? Dzięki!
pek
2

Co powiesz na utworzenie globalnego systemu zdarzeń, a następnie posiadania komponentu detektora zdarzeń dla każdej jednostki? Po wydarzeniu „Zmiana stanu gry” można było bawić się komponentami indywidualnie dla każdego konkretnego podmiotu.

Powiedzmy, że masz komponent wejściowy. Po tym, jak komponent nasłuchiwania zdarzeń otrzyma zdarzenie zmiany stanu gry, zmienia bardzo specyficzne wartości dla tego konkretnego komponentu wejściowego, aby nie odbierał żadnych wywołań wejściowych ani nie wykonywał żadnych ruchów ani odpowiedzi do systemu lub jego właściciela.

Działa to dla mnie, ponieważ większość moich komponentów jest skryptowana (przez Lua). Tzn. Mam komponent wejściowy, który jest uruchamiany raz po naciśnięciu klawisza i uruchamia ruch + kierunek, a następnie jest wyzwalany, gdy klawisz jest zwolniony i uruchamia się w kierunku stop +. Istnieje również komponent nasłuchiwania zdarzeń, który kontaktuje się z komponentem wejściowym (jeśli gra jest wstrzymana), aby przestać uruchamiać jakąkolwiek funkcję i zatrzymać, jeśli to konieczne. Mógłbym wtedy łatwo dodać inny byt z inną reakcją na te same zdarzenia i naciśnięcia klawiszy, używając innego skryptu. W ten sposób można zapisać interakcję między różnymi podmiotami w różnych stanach, a nawet uczynić ją bardziej dostosowywalną. Co więcej, niektóre podmioty mogą nawet nie zawierać w sobie komponentu detektora zdarzeń.

To, co właśnie wyjaśniłem, jest w zasadzie praktycznym przykładem czwartego rozwiązania.

karmalis
źródło