W silniku systemu encja-komponent: jak postępować z grupami jednostek zależnych?

47

Po przejrzeniu kilku wzorców projektowania gier, zdecydowałem się na Entity-Component-System (ES System) dla mojego silnika gry. Czytam artykuły (głównie T = Maszyna ) i przeglądam kod źródłowy i myślę, że mam dość, aby zacząć.

Jest tylko jeden podstawowy pomysł, z którym się zmagam. Jak postępować z grupami podmiotów, które są od siebie zależne?

Pozwól mi skorzystać z przykładu:

Załóżmy, że tworzę standardową strzelankę (myślę Jamestown ) i chcę zbudować „byt boski” z wieloma odrębnymi, ale połączonymi częściami. Podział może wyglądać mniej więcej tak:

  • Korpus statku: ruch, renderowanie
  • Cannon: Pozycja (zablokowana względem ciała statku), Śledzenie \ Ogień w bohaterze, Przyjmowanie obrażeń do momentu wyłączenia
  • Rdzeń: Pozycja (zablokowana względem korpusu statku), Śledzenie \ Ogień w bohaterze, Przyjmowanie obrażeń do momentu wyłączenia, Wyłączanie (er ... niszczenie) wszystkich innych istot w grupie statków

Moim celem będzie coś, co zostanie zidentyfikowane (i zmanipulowane) jako odrębny element gry bez konieczności przepisywania podsystemu od podstaw za każdym razem, gdy chcę zbudować nowy element agregujący.

Jak wdrożyć tego rodzaju projekt w ES System?

  1. Czy wdrażam jakiś rodzaj relacji rodzic-dziecko (byty mogą mieć dzieci)? To wydaje się być sprzeczne z metodologią polegającą na tym, że Jednostki są tylko pustymi pojemnikami i sprawia, że ​​wydaje się bardziej OOP.
  2. Czy zaimplementuję je jako osobne jednostki, z jakimś rodzajem łączenia Component (BossComponent) i powiązanego systemu (BossSubSystem)? Nie mogę przestać myśleć, że będzie to trudne do wdrożenia, ponieważ sposób komunikacji komponentów wydaje się być wielką pułapką na niedźwiedzie.
  3. Czy implementuję je jako jeden byt z kolekcją komponentów (ShipComponent, CannonComponents, CoreComponent)? Ten wydaje się odwracać intencję Systemu ES (tutaj komponenty wydają się zbyt podobne do jednostek o dużej wadze), ale wiem o tym, więc pomyślałem, że to tam przedstawię.
  4. Czy wdrażam je jako coś innego, o czym wspomniałem?

Wiem, że można to bardzo łatwo wdrożyć w OOP, ale wybiorę ES zamiast OOP, z którym będę się trzymał. Jeśli będę musiał zerwać z teorią czystego ES, aby wdrożyć ten projekt, zrobię to (nie tak, jakbym wcześniej nie musiał rezygnować z czystego projektu), ale wolałbym to zrobić ze względów wydajnościowych niż zacząć od złego projektu.

Aby uzyskać dodatkowy kredyt, pomyśl o tym samym projekcie, ale każda z „jednostek bossów” była faktycznie połączona z większą „jednostką BigBoss” złożoną z głównego korpusu, głównego rdzenia i 3 „podmiotów bossa”. Pozwoliłoby mi to znaleźć rozwiązanie dla co najmniej 3 wymiarów (dziadek-rodzic-dziecko) ... które powinno mi wystarczyć.

John Daniels
źródło
2
To po prostu różne elementy siatki przymocowane do bytu, siatka armatnia i armata przymocowane do bytu bossa, nie przesadzaj. Btw system komponentu encji JEST OOP!
Maik Semder
2
Tak - najgorsze z tych artykułów na temat T-Machine to błędny pomysł, że nie jest to obiektowo zorientowane. Większość systemów komponentów dla encji jest całkowicie zorientowana obiektowo, ale nie oparta na dziedziczeniu.
Kylotan
3
Myślę, że podkreślają one naturę nieobjętą OOP, ponieważ „myślenie klasyczne OOP” sprawi ci tyle kłopotów. Do tej pory pomogłem kilku osobom rozpocząć pracę z systemami encji i to jest największa przeszkoda. Próba umieszczenia kodu w komponentach, próba uzyskania komponentów, które dzielą się na klasy itp., To początkowo ogromny problem, ale miło jest widzieć, jak światło zapala się, kiedy pomysł jest w pełni zrozumiany.
PSpeed
@ MaikSemder Oczyściłem swoje komentarze i przeniosłem je na czat
MichaelHouse
1
Właśnie rozumiem @MaikSemder, w systemie ES, do którego się odwołujesz, jednostka może mieć wiele komponentów tego samego typu, a podsystem odpowiedzialny za te komponenty musiałby sobie z tym poradzić? Czyli jednostka może mieć wiele komponentów Renderuj, a dane i podsystemy tych komponentów określą, jak prawidłowo renderować każdy z nich? Doprowadziłoby to do mniejszej liczby jednostek, potencjalnie mniejszej liczby komponentów, ale nieco głębszej logiki podsystemu, prawda?
John Daniels,

Odpowiedzi:

41

Gdybym był w takiej sytuacji, stworzyłbym każdą część szefa jako osobny byt. Te „pod-byty” obejmowałyby jakiś rodzaj AttachmentPointlub ParentEntitykomponent. Ten komponent zawierałby odniesienie do jednostki nadrzędnej i przesunięcie względem pozycji nadrzędnej. Podczas aktualizacji pozycji sprawdzają pozycję nadrzędną i stosują przesunięcie, aby wygenerować własną pozycję. Ponadto może sprawdzać, czy jednostka macierzysta nadal istnieje. Ponadto możesz mieć SubEntitykomponent, który śledzi istnienie podelementów dla encji nadrzędnej. Pozwala to na robienie takich rzeczy, jak tylko narażenie rdzenia bossa na zniszczenie broni z tarczami.

Obecnie używam TargetEntitykomponentu w mojej grze, który służy do śledzenia wieżyczek i kiedy gobliny zamierzają odebrać surowiec. Może sprawdzić pozycję jednostki docelowej i odpowiednio zmienić jej zachowanie. Podmioty, które nie mają pozycji, nigdy nie są dodawane jako cel, więc nie ma się o co martwić. Jednak, gdy będziesz bardziej dogłębny, na przykład sprawdzając zdrowie jednostki nadrzędnej lub podrzędnej, tarczę, rezerwy mocy itp., Musisz upewnić się, że jednostka nadrzędna lub podrzędna faktycznie ma powiązany komponent.

Utworzenie każdej części jako osobnej jednostki zachowuje elastyczność struktury encji / komponentów, umożliwiając dodawanie dodatkowych i różnych komponentów do każdej części szefa. Na przykład jedna część bossa może mieć komponent broni i komponent zdrowia, podczas gdy inna miałaby tarczę i komponent zdrowia.

Znalazłem kolejną dyskusję na ten temat tutaj . W którym użytkownicy dyskutują nad dodaniem wielu elementów tego samego typu do jednego elementu (co wydaje mi się złym pomysłem). Wydaje się, że jest to przydatna rozmowa, chociaż nie przeczytałem całej dyskusji.

MichaelHouse
źródło
Wiele dobrych informacji tutaj. Dobrze wyjaśniłeś mi rozwiązanie, daj mi przykład i prawdopodobnie odpowiedz na 1 lub 2 kolejne pytania, na które musiałbym wrócić później. Powiązana dyskusja również wydaje się intrygująca, zwłaszcza gdy zaczynam wdrażać trudniejsze implementacje. Dzięki @ Byte56!
John Daniels,
Nie ma problemu, John! Oczywiście istnieje wiele różnych sposobów wdrażania systemu EC. System, który miałem na myśli dla tej odpowiedzi, to ten, który opisałem w tej odpowiedzi . Powodzenia w grze!
MichaelHouse
Twoje podejście jest najbardziej elastyczne i rozsądne jest użycie go w ogólnym silniku gry.
Coyote
7

Bez znajomości zbyt wielu szczegółów na temat istniejących systemów, sposobem, w jaki bym to modelował (i do pewnego stopnia w moim własnym systemie encji) jest posiadanie komponentu takiego jak AttachedTo (parentEntity). Każde dziecko może otrzymać składnik AttachedTo (boss).

System renderowania (lub cokolwiek innego) następnie pobiera elementy z komponentami: Position, AttachedTo itp. I tworzy odpowiednie hierarchie.

PSpeed
źródło
To wydaje się być konsensusową odpowiedzią. Następnym razem podam więcej szczegółów implementacji, aby ludzie mogli przeżuć. Dzięki @PSpeed!
John Daniels,
4

Jeśli chcesz, aby encja była reprezentowana tylko przez identyfikator, wówczas przechowywanie encji można wykonać za pomocą specjalnego komponentu. Możesz nazwać go CompositeComponent, a zawiera on listę identyfikatorów encji podrzędnych oraz interfejsy do dodawania / usuwania elementów podrzędnych z tej listy.

Oczywiście wszelkie komponenty, które zależą od pozycji itp., Będą musiały współpracować z tym, aby poprawnie umieścić jednostkę. Jak to zaimplementować, zależy nieco od tego, jak obecnie wdrażasz pozycjonowanie.

Nawiasem mówiąc, nie ma „czystej teorii ES” - popularne jest tworzenie jednostek z komponentów, ale precyzyjna metoda nie jest jeszcze znormalizowana.

Kylotan
źródło
Tak, powinienem nauczyć się nie używać słowa „czysty” w żadnej dyskusji na temat projektu… nic takiego. Trasa ConpositeComponent wydaje się tutaj zgodna. Dzięki @Kylotan!
John Daniels,