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?
źródło
Odpowiedzi:
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.
źródło