Do tej pory systemy komponentów encji, z których korzystałem, działały głównie jak artemidy Javy:
- Wszystkie dane w komponentach
- Bezstanowe niezależne systemy (przynajmniej w takim stopniu, że nie wymagają wkładu przy inicjalizacji) iterujące po każdej jednostce zawierającej tylko komponenty, którymi zainteresowany jest dany system
- Wszystkie systemy przetwarzają swoje byty jednym tikiem, a potem wszystko zaczyna się od nowa.
Teraz próbuję zastosować to po raz pierwszy w grze turowej, z mnóstwem wydarzeń i odpowiedzi, które muszą wystąpić w ustalonej kolejności względem siebie, zanim gra będzie mogła przejść dalej. Przykład:
Gracz A otrzymuje obrażenia od miecza. W odpowiedzi zbroja A włącza się i obniża otrzymywane obrażenia. Prędkość ruchu A jest również obniżana w wyniku osłabienia.
- Otrzymane obrażenia rozpoczynają całą interakcję
- Pancerz musi zostać obliczony i przyłożony do zadawanych obrażeń, zanim obrażenia zostaną zadane graczowi
- Zmniejszenia prędkości ruchu nie można zastosować do jednostki, dopóki obrażenia nie zostaną zadane, ponieważ zależy to od ostatecznej wielkości obrażeń.
Zdarzenia mogą również powodować inne zdarzenia. Zmniejszenie obrażeń miecza za pomocą zbroi może spowodować rozbicie miecza (musi to nastąpić przed zakończeniem redukcji obrażeń), co z kolei może spowodować dodatkowe zdarzenia w odpowiedzi na nie, w zasadzie rekurencyjną ocenę zdarzeń.
Podsumowując, wydaje się, że prowadzi to do kilku problemów:
- Wiele zmarnowanych cykli przetwarzania: większość systemów (z wyjątkiem rzeczy, które zawsze działają, np. Renderowanie) po prostu nie ma nic wartego do zrobienia, gdy nie jest „ich kolej” do pracy i spędza większość czasu czekając na wejście do gry prawidłowy stan pracy. Zaśmieca to każdy taki system czekami, które stają się coraz większe, im więcej stanów jest dodawanych do gry.
- Aby dowiedzieć się, czy system może przetwarzać byty obecne w grze, potrzebują jakiegoś sposobu monitorowania innych niepowiązanych stanów bytu / systemu (systaem odpowiedzialny za zadawanie obrażeń musi wiedzieć, czy zastosowano zbroję, czy nie). To albo wprowadza w błąd systemy z wieloma obowiązkami, albo stwarza potrzebę dodatkowych systemów, które nie mają innego celu niż skanowanie kolekcji jednostek po każdym cyklu przetwarzania i komunikowanie się z zestawem słuchaczy, informując ich, kiedy można coś zrobić.
W powyższych dwóch punktach założono, że systemy działają na tym samym zestawie jednostek, które w końcu zmieniają stan za pomocą flag w swoich komponentach.
Innym sposobem rozwiązania tego problemu byłoby dodanie / usunięcie komponentów (lub stworzenie zupełnie nowych bytów) w wyniku pracy jednego systemu w celu poprawy stanu gry. Oznacza to, że ilekroć system rzeczywiście ma pasujący byt, wie, że może go przetwarzać.
Powoduje to jednak, że systemy są odpowiedzialne za uruchamianie kolejnych systemów, co utrudnia uzasadnienie działania programów, ponieważ błędy nie pojawiają się w wyniku pojedynczej interakcji systemu. Dodawanie nowych systemów również staje się trudniejsze, ponieważ nie można ich wdrożyć, nie wiedząc dokładnie, w jaki sposób wpływają one na inne systemy (a poprzednie systemy mogą wymagać modyfikacji, aby wyzwolić stany, którymi zainteresowany jest nowy system), co w pewnym sensie pokonuje cel posiadania oddzielnych systemów z jednym zadaniem.
Czy to coś, z czym będę musiał żyć? Każdy przykład ECS, który widziałem, był w czasie rzeczywistym i bardzo łatwo jest zobaczyć, jak działa w takich przypadkach pętla jednej iteracji na grę. Nadal potrzebuję go do renderowania, wydaje się po prostu naprawdę nieodpowiedni dla systemów, które zatrzymują większość aspektów siebie za każdym razem, gdy coś się dzieje.
Czy istnieje jakiś wzorzec projektowy do przesuwania stanu gry do przodu, który jest odpowiedni do tego, czy powinienem po prostu przenieść całą logikę z pętli i zamiast tego uruchomić ją tylko w razie potrzeby?