Uwaga: Programuję to w Javascripcie, ale w większości powinien to być język.
Zastanawiam się nad zmianą silnika na ECS.
Dostaję podstawowy pomysł ( uwaga: to źle, patrz moja odpowiedź ):
Istoty są obiektami gry.
Składniki to bity funkcjonalności ( reactToInput()
) lub state ( position
), które można „przykleić” do elementów.
Systemy mają listę podmiotów, którymi zarządzają i aktualizują.
Ale nie jestem pewien, czy otrzymałem implementację i kilka szczegółów ...
Pytanie: czy system może działać na różnych rodzajach podmiotów? Zazwyczaj podam przykład klasy wywoływanej Scene
w moim silniku, która również teraz będzie służyć temu celowi. Scena jest kontenerem wszystkich obiektów, które można renderować, aktualizować, wpływać na rendering (światła), a może w przyszłości nawet 2DSoundEmitter
obiekty. Ma interfejs wysokiego poziomu, dzięki czemu użytkownik nie musi się martwić o typ obiektu, który scene.add()
wymyśla i tego typu rzeczy.
Zdaję sobie sprawę, że Scene
może to być system. Pobiera jednostki, przechowuje je, a następnie może wywoływać ich metody aktualizacji, a może nawet wprowadzić pewne zmiany stanu. Ale jest problem: jak opisałem powyżej, Scene
można karmić różnego rodzaju obiektami! Co powinienem zrobić, powiedzmy, w sytuacji, gdy scena zawiera zarówno obiekty do renderowania („drawable”), jak i światła? Czy powinienem sprawić, by sprawdzał typ jednostek przed interakcją? Czy należy go rozwiązać na jeszcze niższym poziomie: dokonać LightSource
składnik, który można dodać do dowolnego obiektu, a światło będzie tylko podmiot posiadający LightSource
i Position
komponenty. Czy to jest dopuszczalne?
Ponadto, czy dobrą praktyką jest nadal korzystanie z tradycyjnego dziedziczenia i klas tradycyjnych? Na przykład po prostu nie mogę ustalić, jaki byłby mój Renderer
! To nie jest system, ponieważ jego jedyną funkcją jest rejestrowanie kamery i sceny, renderowanie wszystkiego i stosowanie efektów (takich jak cienie). Zarządza także kontekstem, szerokością i wysokością gry, wykonuje tłumaczenia ... Ale to wciąż nie jest system!
Edycja: czy możesz połączyć jakieś zasoby znalezione w ECS? Mam problem ze znalezieniem dobrych.
Odpowiedzi:
Pokażę, czy próbując zrozumieć jako programistę JS web / UI, mogę pomóc. Nie sięgaj też zbyt daleko w agnostycyzm językowy. Warto zapoznać się z wieloma wzorcami ustalonymi w innych językach, ale można je stosować w JS w różny sposób ze względu na jego elastyczność lub naprawdę nie są konieczne ze względu na plastyczny charakter języka. Możesz skorzystać z okazji, jeśli napiszesz swój kod, myśląc, że JS ma ten sam zestaw granic, co bardziej klasyczny język zorientowany na OOP.
Przede wszystkim, jeśli chodzi o czynnik „nie używaj OOP”, pamiętaj, że obiekty JavaScript są jak placek zabaw w porównaniu do innych języków i faktycznie musisz zrobić wszystko, aby zbudować koszmar kaskadowo-spadkowy, ponieważ JS nie jest klasą na podstawie i komponowanie przychodzi o wiele bardziej naturalnie. Jeśli wdrażasz jakiś głupiutki lub prototypowy system hand-me-down w swoim JS, rozważ to porzucenie. W JS używamy zamknięć, prototypów i przekazujemy funkcje jak cukierki. Jest obrzydliwe, brudne i złe, ale także mocne, zwięzłe i tak to lubimy.
Podejścia oparte na dziedziczeniu są w rzeczywistości przedstawiane jako anty-wzorzec we wzorach projektowych i nie bez powodu, jak każdy, kto zszedł ponad 15 poziomów klas lub struktur podobnych do klas, aby dowiedzieć się, gdzie, do cholery, wypaliła wersję metody przychodził z może ci powiedzieć.
Nie wiem, dlaczego tak wielu programistów uwielbia to robić (zwłaszcza faceci Java z jakiegoś powodu piszą JavaScript), ale jest to okropne, nieczytelne i całkowicie nie do utrzymania, gdy jest przyzwyczajone do nadmiaru. Dziedziczenie jest w porządku tu i tam, ale tak naprawdę nie jest konieczne w JS. W językach, w których jest to bardziej kuszący skrót, powinien być naprawdę zarezerwowany dla bardziej abstrakcyjnych problemów związanych z architekturą, a nie dla bardziej dosłownych schematów modelowania, takich jak frankensteining implementacji zombie poprzez łańcuch dziedziczenia obejmujący BunnyRabbit, ponieważ tak się stało. To nie jest dobre ponowne użycie kodu. To koszmar konserwacji.
Jako silniki oparte na jednostkach / komponentach / systemach JS dev uderzyły mnie jako system / wzorzec do rozłączania problemów projektowych, a następnie komponowania obiektów do implementacji na bardzo szczegółowym poziomie. Innymi słowy, zabawa dziecka w języku takim jak JavaScript. Ale pozwól, że zobaczę, czy najpierw mam problemy z tym.
Podmiot - konkretna rzecz, którą projektujesz. Mówimy bardziej w kierunku właściwych rzeczowników (ale oczywiście nie). Nie „Scena”, ale „IntroAreaLevelOne”. IntroAreaLevelOne może znajdować się wewnątrz jakiegoś pola sceneEntity, ale skupiamy się na czymś innym niż inne powiązane rzeczy. W kodzie jednostka jest tak naprawdę tylko nazwą (lub identyfikatorem) powiązaną z szeregiem rzeczy, które musi zaimplementować lub ustanowić (komponenty), aby była użyteczna.
Komponenty - rodzaje rzeczy, których potrzebuje jednostka. To są rzeczowniki ogólne. Jak WalkingAnimation. W WalkingAnimation możemy uzyskać bardziej szczegółowe, takie jak „Shambling” (dobry wybór dla zombie i potworów roślinnych) lub „ChickenWalker” (doskonały dla robotów typu ed-209ish z odwrotnym połączeniem). Uwaga: Nie jestem pewien, jak to może oddzielić od renderowania takiego modelu 3D - więc może to bzdura, ale jestem bardziej pro JS niż doświadczonym twórcą gier. W JS umieściłbym mechanizm mapowania w tym samym pudełku z komponentami. Komponenty same w sobie prawdopodobnie nie są logiczne i stanowią raczej mapę drogową informującą systemy, co należy wdrożyć, jeśli systemy są nawet potrzebne (w mojej próbie ECS niektóre komponenty to tylko zbiory zestawów właściwości). Po ustanowieniu komponentu „
Systemy - prawdziwe mięso programowe jest tutaj. Systemy AI są zbudowane i połączone, renderowanie zostało osiągnięte, ustalone sekwencje animacji itp. Rozwalam i pozostawiam je głównie wyobraźni, ale w przykładzie System.AI bierze wiele właściwości i wydziela funkcję, która służy do dodawania procedur obsługi zdarzeń do obiektu, który ostatecznie zostaje wykorzystany przy implementacji. Kluczową rzeczą w System.AI jest to, że obejmuje wiele typów komponentów. Możesz uporządkować wszystkie elementy AI za pomocą jednego komponentu, ale zrobienie tego to niezrozumienie sensu granulacji.
Pamiętaj o celach: Chcemy ułatwić podłączenie interfejsu GUI, aby osoby niebędące projektantami mogły łatwo modyfikować różne rzeczy poprzez maksymalizowanie i dopasowywanie komponentów w paradygmacie, który ma dla nich sens, i chcemy uciec od popularne schematy dowolnego kodu, które są o wiele łatwiejsze do napisania niż do modyfikacji lub utrzymania.
W JS może coś takiego. Deweloperzy gier, proszę powiedz mi, czy okropnie się mylę:
Teraz, kiedy potrzebujesz NPC, możesz budować z nim
npcBuilders.<npcName>();
GUI może podłączyć się do obiektów npcEntities i komponentów i pozwolić projektantom na modyfikowanie starych encji lub tworzenie nowych encji poprzez proste mieszanie i dopasowanie komponentów (chociaż nie ma tam żadnego mechanizmu dla komponentów innych niż domyślne, ale specjalne komponenty mogą być dodawane na bieżąco kod, o ile istnieje dla niego zdefiniowany komponent.
źródło
Przeczytałem o Entity Systems w artykułach, które mili ludzie zamieszczali w komentarzach, ale wciąż miałem wątpliwości, więc zadałem kolejne pytanie .
Po pierwsze, moje definicje były błędne. Podmioty i komponenty to po prostu głupie posiadacze danych, podczas gdy systemy zapewniają całą funkcjonalność.
Nauczyłem się wystarczająco, aby ująć tutaj większość mojego pytania, więc odpowiem na to pytanie.
Scene
Klasa mówiłam o nie powinien być system. Powinien to być jednak centralny menedżer, który może przechowywać wszystkie podmioty, ułatwiać wiadomości, a może nawet zarządzać systemami. Może również funkcjonować jako rodzaj fabryki dla podmiotów i postanowiłem go tak wykorzystać. To może wziąć każdy rodzaj podmiotu, ale to musi karmić temu podmiotowi odpowiedniego systemu (które, ze względu na wydajność, nie należy wykonywać żadnych typu kontroli, chyba że są one mnożenie).Nie powinienem używać żadnego OOP podczas implementowania ES, sugeruje Adam, ale nie znajduję powodu, by nie mieć obiektów z metodami dla encji i komponentów, a nie tylko dla głupich posiadaczy danych.
Renderer
Mogą być realizowane po prostu jako system. Utrzymywałby listę obiektów rysowalnych i wywoływałbydraw()
metodę ich komponentu renderowania co 16 ms.źródło
Wprowadzenie do zarządzania zależnościami 101.
Kurs zakłada, że masz podstawową wiedzę na temat wstrzykiwania zależności i projektowania repozytorium.
Wstrzykiwanie zależności jest tylko fantazyjnym sposobem, aby obiekty rozmawiały ze sobą (za pośrednictwem wiadomości / sygnałów / delegatów / cokolwiek) bez bezpośredniego połączenia.
Jest tak: „
new
jest klejem”.Pokażę to w języku C #.
Jest to podstawowy system do wprowadzania, przemieszczania i renderowania. W
Player
tym przypadku klasa jest bytem, a komponenty to interfejsy.Player
Klasa wie nic na temat wewnętrznych, w jaki sposób konkretnyIRenderSystem
,IMovementSystem
lubIInputSystem
pracy. Jednak interfejsy umożliwiająPlayer
wysyłanie sygnałów (np. Wywołanie Draw na IRenderSystem) bez uzależnienia od tego, w jaki sposób osiągnięty zostanie wynik końcowy.Weźmy na przykład moją implementację IMovementSystem:
MovementSystem
może mieć swoje zależności iPlayer
nawet by się tym nie przejmował. Za pomocą interfejsów można utworzyć automat stanów gry:I to jest początek uroczej gry (która może być również testowana jednostkowo).
I z kilkoma dodatkami i pewnym polimorfizmem:
Mamy teraz prymitywny system encji / komponentów oparty na interfejsach agregujących i luźnym dziedziczeniu.
źródło