Entity Component System - Jak zaimplementować transformację obiektu?

11

Projektując układ encji-komponentu dla mojego silnika, natknąłem się na małą przeszkodę w sposobie przechowywania i wyszukiwania określonego typu komponentu.

Po pierwsze, pozwól mi wyjaśnić trochę terminologii, której zamierzam użyć w tym pytaniu:

  • Nazywam „ Component ” strukturą danych, która przechowuje odpowiednie dane dla konkretnego systemu.
  • Nazywam „ System ” agregacją metod i struktur danych, która wykorzystuje Składniki do aktualizacji stanu / interfejsu gry z użytkownikiem.
  • Entity ” to w zasadzie tylko identyfikator służący do pobierania określonych składników i modyfikowania ich danych w logice gry.

Każdy system posiada tablicę (odwzorowaną na ID) swojego typu Component (np. Physics-> PhysicsComponent, AI-> AIComponent, Rendering-> RenderingComponent), aby mógł efektywnie iterować dane.

Jednak nie wszystkie komponenty są własnością systemu. Na przykład komponent Transform przechowuje pozycję, obrót i skalę obiektu. Jest to jedna z najważniejszych części bytu (Unity sprawia, że ​​jest ona nawet obowiązkowa), ponieważ jest używana przez wiele systemów, np. Fizykę, sztuczną inteligencję, renderowanie itp.

To właściwie problem, z którym się zmagam. Ponieważ Transform jest używany przez wiele innych systemów, w jaki sposób powinienem zacząć pobierać jeden do użycia dla każdego komponentu? Jednym z możliwych rozwiązań, które widzę, jest skonfigurowanie każdego komponentu do przechowywania własnego identyfikatora jednostki. Łatwo byłoby odzyskać dowolny taki komponent, ale nie byłby tak wydajny, a także byłby sprzeczny z koncepcją komponentu jako izolowanego i niezależnego pakietu danych, który nie jest świadomy żadnego innego.

Czy istnieje właściwy sposób rozwiązania tego problemu? Czy transformacja powinna być nawet składnikiem?

CRefice
źródło
3
+1 za „Najpierw pozwolę sobie wyjaśnić trochę terminologii, której zamierzam użyć w tym pytaniu:”
Vaillancourt
Chciałbym zobaczyć tego rodzaju pytania więcej na tej stronie. +1
S. Tarık Çetin
Po prostu przechowuj wszystkie komponenty jako zmienne globalne
Miles Rout

Odpowiedzi:

2

To dość szerokie pytanie, na które odpowiedź silnie zależy od twojej architektury. Spróbuję jednak udzielić ogólnej odpowiedzi.

Twoje fizyki i systemy renderowania z pewnością będą wymagały transformacji, jednak system AI tego nie zrobi. Dlatego sensowne jest kapsułkowanie transformacji we własną klasę komponentów. Wszystkie takie zainteresowane systemy wykorzystywałyby te same dane, dlatego sensowne jest, aby jednostka miała wskaźnik do obiektu transformacji lub identyfikator obiektu transformacji przechowywanego gdzie indziej.

Jeśli wybierzesz to drugie rozwiązanie, wówczas każdy system, który jest zainteresowany transformacją, będzie wymagał dostępu do dowolnego miejsca, w którym przechowywany jest obiekt transformacji.

Jeśli wybierzesz tę pierwszą, wszystko, co każdy system musi zrobić, to uzyskać dostęp do samej encji i zażądać transformacji.

W pierwszym przypadku problem polega na tym, jak przyznać dostęp do pamięci w celu przekształceń każdego systemu, bez łamania reguł OOP, jeśli zależy Ci na takich rzeczach.

W tym drugim przypadku nie ma takich problemów, ale wymaga zmiany projektu obiektu encji, aby przechowywać wskaźniki na obiektach, a nie identyfikatory obiektów składowych.

Osobiście wolę zaprojektować klasę encji do przechowywania wskaźników do obiektów składowych, ponieważ upraszcza to wiele problemów projektowych. W ten sposób każdy system, który wymaga transformacji, może zażądać od encji i zignorować, jeśli nie będzie. Jednak niesie to ze sobą narzut obliczeniowy związany ze wskaźnikami, które są kosztem utraty pamięci podręcznej.

Więcej informacji na ten temat można znaleźć w tym przeglądzie ECS .

Ostatecznie to Ty decydujesz, co jest dla Ciebie ważniejsze: łatwość rozwoju lub wydajność.

Na koniec chciałbym wskazać, że twoje pytanie jest doskonałym przykładem pytań projektowych, o których myślą zwolennicy ECS, i że nie ma ostatecznego rozwiązania srebrnej kuli.

Ian Young
źródło
Dziękuję za twoje sugestie. Mam jednak pytanie: dlaczego system AI nie potrzebuje pozycji obiektu?
CRefice
Mylona jest pozycja (wektor 3 liczb zmiennoprzecinkowych) z translacją (macierz transformacji zbudowana z wektora pozycji). Macierz transformacji jest zbudowana z transformacji translacji, obrotu i skali. Jest to o wiele więcej informacji, niż system AI potrzebowałby, choć na pewno można z niego wyciągnąć wektor pozycji. Osobiście jednak podzieliłem pozycję, orientację i rozmiary na ich własny komponent i wykorzystałem je do stworzenia i aktualizacji Transformacji.
Ian Young,
@IanYoung Rozdzielenie ich może wyrządzić więcej szkody niż pożytku, potencjalnie jeśli okaże się, że potrzebujesz wspólnej pozycji i orientacji częściej niż pozycji lub orientacji oddzielnie. W takim przypadku umieszczenie atrybutów danych pozycji i orientacji w jednym komponencie może poprawić wydajność pamięci podręcznej.
Naros
1
Wolę łączyć wszystkie trzy elementy, pozycję, orientację i skalę w jeden komponent, a w przypadkach, w których konkretny podsystem wymaga jedynie pozycji lub orientacji, zalecałbym kopiowanie danych i synchronizowanie ich w jasno określonych punktach pętli gry .
Naros,
@Naros tak, właśnie to miałem na myśli: Two Components, Transform i (w mojej strukturze) SpatialData, która zawiera pozycję, prędkość, orientację i prędkość kątową. Pozycja i orientacja są używane do konstruowania i aktualizowania transformacji.
Ian Young,