Staram się omijać projektowanie encji oparte na komponentach.
Moim pierwszym krokiem było stworzenie różnych komponentów, które można by dodać do obiektu. Dla każdego typu komponentu miałem menedżera, który wywoływałby funkcję aktualizacji każdego komponentu, przekazując rzeczy takie jak stan klawiatury itp. Zgodnie z wymaganiami.
Następną rzeczą, którą zrobiłem, było usunięcie obiektu i po prostu posiadanie każdego komponentu o identyfikatorze. Zatem obiekt jest definiowany przez komponenty mające te same identyfikatory.
Teraz myślę, że nie potrzebuję menedżera dla wszystkich moich komponentów, na przykład mam SizeComponent
, który ma tylko Size
właściwość). W rezultacie SizeComponent
nie ma metody aktualizacji, a metoda aktualizacji menedżera nic nie robi.
Moją pierwszą myślą było stworzenie ObjectProperty
klasy, którą komponenty mogłyby sprawdzać, zamiast mieć je jako właściwości komponentów. Obiekt miałby więc liczbę ObjectProperty
i ObjectComponent
. Komponenty miałyby logikę aktualizacji, która pyta obiekt o właściwości. Menedżer zarządzałby wywoływaniem metody aktualizacji komponentu.
Wydaje mi się to nadmierną inżynierią, ale nie sądzę, żebym mógł pozbyć się komponentów, ponieważ potrzebuję sposobu, aby menedżerowie wiedzieli, jakie obiekty potrzebują do uruchomienia logiki komponentów (w przeciwnym razie po prostu usunę komponent całkowicie i wrzuć logikę aktualizacji do menedżera).
- Jest to (o
ObjectProperty
,ObjectComponent
iComponentManager
klasach) over-inżynierii? - Jaka byłaby dobra alternatywa?
źródło
SizeComponent
jest przesadą - możesz założyć, że większość obiektów ma rozmiar - to takie rzeczy, jak rendering, sztuczna inteligencja i fizyka, gdzie używany jest model komponentu; Rozmiar zawsze zachowuje się tak samo - dzięki czemu możesz udostępniać ten kod.RenderingComponent
i aPhysicsComponent
. Czy zastanawiam się nad tym, gdzie umieścić nieruchomość? Czy powinienem po prostu go wsadzić, a następnie poprosić drugą kwerendę o obiekt dla komponentu, który ma wymaganą właściwość?PhysicalStateInstance
(jeden na obiekt) obokGravityPhysicsShared
(jeden na grę); kusi mnie jednak, by powiedzieć, że zapuszcza się w sferę euforii architektów, nie buduj się w dziurze (dokładnie tak, jak to zrobiłem z moim pierwszym systemem komponentów). POCAŁUNEK.Odpowiedzi:
Prosta odpowiedź na twoje pierwsze pytanie brzmi: tak, jesteś nad inżynierią projektu. „Jak daleko się rozbijam?” pytanie jest bardzo częste, gdy podejmowany jest następny krok i obiekt centralny (zwykle nazywany bytem) jest usuwany.
Kiedy rozkładasz obiekty na tak szczegółowy poziom, że same mają rozmiar, projekt poszedł za daleko. Sama wartość danych nie jest składnikiem. Jest to wbudowany typ danych i często można go nazwać dokładnie tak, jak je nazwałeś, właściwością. Właściwość nie jest składnikiem, ale składnik zawiera właściwości.
Oto kilka przewodnich wskazówek, które staram się stosować podczas opracowywania w systemie komponentów:
Ponieważ wytyczne dotyczące komponentów, które nie są strukturami, SizeComponent został zepsuty zbyt daleko. Zawiera tylko dane, a to, co określa rozmiar czegoś, może się różnić. Na przykład w komponencie renderującym może to być skalar 1d lub wektor 2 / 3d. W komponencie fizyki może to być objętość graniczna obiektu. W elemencie ekwipunku może być to, ile miejsca zajmuje na siatce 2D.
Spróbuj narysować dobrą linię między teorią a praktycznością.
Mam nadzieję że to pomoże.
źródło
Już zaakceptowałeś odpowiedź, ale oto mój cios w CBS. Odkryłem, że
Component
klasa ogólna ma pewne ograniczenia, więc wybrałem projekt opisany przez Radical Entertainment na GDC 2009, który zasugerował rozdzielenie komponentów naAttributes
iBehaviors
. („ Teoria i praktyka architektury komponentu obiektu gry ”, Marcin Chady)Moje decyzje projektowe wyjaśniam w dwustronicowym dokumencie. Po prostu opublikuję link, ponieważ jest on zbyt długi, aby wkleić go tutaj. Obecnie obejmuje tylko komponenty logiczne (nie dotyczy to również renderowania i fizyki), ale powinien dać ci wyobrażenie o tym, co próbowałem zrobić:
► http://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1
Oto fragment dokumentu:
Edycja: A oto schemat relacji, który pokazuje, jak komponenty komunikują się ze sobą:
Szczegół implementacji: poziom jednostki
EventManager
jest tworzony tylko wtedy, gdy jest używany.Entity
Klasa tylko przechowuje wskaźnik naEventManager
który jest inicjowany, jeśli tylko niektóre wnioski składowe go.Edycja: Na inne pytanie udzieliłem podobnej odpowiedzi na to pytanie. Można go znaleźć tutaj, być może dla lepszego wyjaśnienia systemu:
► /gamedev//a/23759/6188
źródło
To naprawdę zależy od właściwości, których potrzebujesz i gdzie ich potrzebujesz. Ilość pamięci, którą będziesz miał, oraz moc / typ przetwarzania, którego będziesz używać. Widziałem i próbuję wykonać następujące czynności:
Zasady te mają swoje ograniczenia. Oczywiście będziesz musiał przesunąć geometrię do renderera, ale prawdopodobnie nie będziesz chciał jej stamtąd odzyskać. Główna geometria zostanie zapisana w silniku fizyki w przypadku wystąpienia deformacji i zsynchronizowana z rendererem (od czasu do czasu w zależności od odległości obiektów). W pewnym sensie i tak go skopiujesz.
Nie ma doskonałych systemów. A niektórym grom będzie lepiej z prostszym systemem, podczas gdy inne będą wymagały bardziej złożonych synchronizacji między systemami.
Najpierw upewnij się, że wszystkie właściwości są dostępne w prosty sposób z twoich komponentów, abyś mógł zmienić sposób przechowywania właściwości w przejrzysty sposób po rozpoczęciu dostrajania systemów.
Kopiowanie niektórych właściwości nie jest wstydem. Jeśli kilka komponentów musi przechowywać kopię lokalną, czasami bardziej wydajne jest kopiowanie i synchronizowanie niż uzyskiwanie dostępu do „zewnętrznej” wartości ”
Również synchronizacja nie musi się zdarzać w każdej klatce. Niektóre komponenty mogą być synchronizowane rzadziej niż inne. Komponent renderujący jest często dobrym przykładem. Te, które nie wchodzą w interakcje z graczami, mogą być synchronizowane rzadziej, tak jak te, które są daleko. Te odległe i poza polem kamery mogą być synchronizowane jeszcze rzadziej.
Jeśli chodzi o komponent rozmiaru, prawdopodobnie można go powiązać z komponentem pozycji:
Zamiast rozmiaru można nawet użyć modyfikatora rozmiaru, może się przydać.
Jeśli chodzi o przechowywanie wszystkich właściwości w ogólnym systemie przechowywania nieruchomości ... Nie jestem pewien, czy zmierzasz we właściwym kierunku ... Skoncentruj się na właściwościach, które są kluczowe dla twojej gry, i twórz komponenty, które zawierają jak najwięcej powiązanych właściwości. Dopóki odpowiednio wyodrębnisz dostęp do tych właściwości (na przykład za pomocą modułów pobierających w potrzebnych składnikach), powinieneś być w stanie przenieść je, skopiować i zsynchronizować później, bez naruszania zbyt dużej logiki.
źródło
Jeśli komponenty można dowolnie dodawać do encji, potrzebujesz sposobu, aby zapytać, czy dany komponent istnieje w encji i uzyskać odniesienie do niego. Możesz iterować listę obiektów pochodzących z ObjectComponent, aż znajdziesz ten, który chcesz, i zwrócisz go. Ale zwróciłbyś obiekt poprawnego typu.
W C ++ lub C # zwykle oznacza to, że masz metodę szablonu na obiekcie, takim jak
T GetComponent<T>()
. A kiedy już znajdziesz to odniesienie, wiesz dokładnie, jakie dane członka posiada, więc po prostu uzyskaj do nich bezpośredni dostęp.W czymś takim jak Lua lub Python niekoniecznie masz wyraźny typ tego obiektu i prawdopodobnie nie obchodzi Cię to. Ale znowu, możesz po prostu uzyskać dostęp do zmiennej elementu i obsłużyć każdy wyjątek powstały w wyniku próby uzyskania dostępu do czegoś, czego nie ma.
Zapytanie o właściwości obiektu wyraźnie brzmi jak powielanie pracy, którą język może dla Ciebie wykonać, albo w czasie kompilacji dla języków o typie statycznym, albo w czasie wykonywania w przypadku języków o typie dynamicznym.
źródło
Chodzi o to, że RenderingComponent używa pozycji, ale zapewnia ją fizyka . Potrzebujesz tylko sposobu, aby powiedzieć każdemu komponentowi użytkownika, którego dostawcy użyć. Najlepiej w sposób agnostyczny, w przeciwnym razie wystąpi zależność.
Nie ma wspólnej reguły. Zależy od konkretnej właściwości (patrz wyżej).
Stwórz grę z brzydką, ale opartą na komponentach architekturą, a następnie zmodyfikuj ją.
źródło
PhysicsComponent
powinienem wtedy zrobić. Widzę to jako zarządzanie symulowaniem obiektu w środowisku fizycznym, co prowadzi mnie do tego zamieszania: nie wszystkie rzeczy, które trzeba wyrenderować, będą musiały zostać zasymulowane, więc dodawanie,PhysicsComponent
gdy dodam , wydaje się błędem,RenderingComponent
ponieważ zawiera pozycję któryRenderingComponent
używa. Z łatwością mogłem zobaczyć, jak kończy się sieć połączonych ze sobą komponentów, co oznacza, że wszystkie / większość trzeba dodać do każdej jednostki.Jelit jest informacją, że posiadanie
ThingProperty
,ThingComponent
orazThingManager
dla każdegoThing
rodzaju składnika a trochę przesadą. Myślę, że to prawda.Ale potrzebujesz sposobu, aby śledzić powiązane komponenty pod względem tego, z których systemów korzystają, do jakiej jednostki należą, itp.
TransformProperty
będzie dość powszechny. Ale kto za to odpowiada, system renderowania? System fizyki? System dźwiękowy? DlaczegoTransform
składnik miałby nawet się aktualizować?Rozwiązaniem jest usunięcie dowolnego kodu z właściwości poza obiektami pobierającymi, ustawiającymi i inicjującymi. Komponenty to dane, które są używane przez systemy w grze do wykonywania różnych zadań, takich jak renderowanie, AI, odtwarzanie dźwięku, ruch itp.
Przeczytaj o Artemis: http://piemaster.net/2011/07/entity-component-artemis/
Spójrz na jego kod, a zobaczysz, że jest oparty na systemach, które deklarują swoje zależności jako listy
ComponentTypes
. Piszesz każdą ze swoichSystem
klas, a w metodzie konstruktora / init deklarujesz, od jakiego rodzaju systemu zależy.Podczas konfiguracji poziomów lub czegokolwiek, tworzysz swoje byty i dodajesz do nich komponenty. Następnie każesz temu podmiotowi zgłosić się z powrotem do Artemidy, a Artemis następnie ustala na podstawie składu tego bytu, które systemy byłyby zainteresowane wiedzą o tym bycie.
Następnie, w fazie aktualizacji pętli, masz
System
teraz listę jednostek do aktualizacji. Teraz możesz mieć granulację składników, dzięki czemu można opracować systemy szalone, podmioty zbudować tematyceModelComponent
,TransformComponent
,FliesLikeSupermanComponent
, iSocketInfoComponent
, i zrobić coś dziwnego jak dokonać latający spodek, że muchy między klientami podłączony do gry wieloosobowej. Dobra, może nie to, ale pomysł polega na tym, że utrzymuje to w oddzieleniu i elastycznie.Artemida nie jest doskonała, a przykłady na stronie są trochę podstawowe, ale separacja kodu i danych jest potężna. Jest to również dobre dla twojej pamięci podręcznej, jeśli zrobisz to dobrze. Artemida prawdopodobnie nie robi tego dobrze na tym froncie, ale dobrze jest uczyć się od.
źródło