Czy obiekt architektury Entity Component System jest z definicji zorientowany?

20

Czy architektura Entity Component System jest z definicji obiektowa? Wydaje mi się bardziej proceduralne lub funkcjonalne. Uważam, że nie przeszkadza to we wdrożeniu go w języku OO, ale nie byłoby idiomatyczne, aby robić to zdecydowanie OO.

Wygląda na to, że ECS oddziela dane (E i C) od zachowania (S). Jako dowód :

Chodzi o to, aby w jednostce nie było żadnych metod gry.

I :

Składnik składa się z minimalnego zestawu danych potrzebnych do określonego celu

Systemy to funkcje jednego celu, które przyjmują zestaw jednostek, które mają określony komponent


Myślę, że nie jest to obiektowe, ponieważ duża część bycia zorientowanym obiektowo polega na połączeniu twoich danych i zachowania razem. Jako dowód :

Natomiast podejście obiektowe zachęca programistę do umieszczania danych tam, gdzie nie są one bezpośrednio dostępne dla reszty programu. Zamiast tego dostęp do danych uzyskuje się przez wywołanie specjalnie napisanych funkcji, zwanych zwykle metodami, które są dołączone do danych.

Z drugiej strony, ECS wydaje się polegać na oddzieleniu danych od twojego zachowania.

Daniel Kaplan
źródło

Odpowiedzi:

21

Wprowadzenie


Systemy encja-komponent są zorientowaną obiektowo techniką architektoniczną.

Nie ma uniwersalnego konsensusu co do tego, co oznacza ten termin, tak samo jak programowanie obiektowe. Oczywiste jest jednak, że systemy encja-komponent są specjalnie przeznaczone jako architektoniczna alternatywa dla dziedziczenia . Hierarchie dziedziczenia są naturalne do wyrażania tego, co obiekt jest , ale w niektórych rodzajów oprogramowania (takich jak gry), czy raczej wyrazić to, co obiekt robi .

Jest to inny model obiektowy niż „klasy i dziedziczenie”, do którego najprawdopodobniej jesteś przyzwyczajony do pracy w C ++ lub Javie. Jednostki są tak wyraziste jak klasy, podobnie jak prototypy jak w JavaScript lub Self - wszystkie te systemy mogą być zaimplementowane względem siebie.

 

Przykłady


Powiedzmy Powiedzmy, że Playerjest to jednostka z Position, Velocityi KeyboardControlledkomponentów, które wykonują oczywistych rzeczy.

entity Player:
  Position
  Velocity
  KeyboardControlled

Wiemy, że Positionmusi mieć wpływ Velocity, a Velocityprzez KeyboardControlled. Pytanie brzmi, w jaki sposób chcielibyśmy modelować te efekty.

 

Podmioty, komponenty i systemy


Załóżmy, że składniki nie mają ze sobą żadnych odniesień; system zewnętrzny Physicsprzechodzi przez wszystkie Velocitykomponenty i aktualizuje Positionodpowiedni byt; InputSystem przemierza wszystkie KeyboardControlledkomponenty i aktualizuje Velocity.

          Player
         +--------------------+
         | Position           | \
         |                    |  Physics
       / | Velocity           | /
  Input  |                    |
       \ | KeyboardControlled |
         +--------------------+

Spełnia to kryteria:

  • Jednostka nie wyraża logiki gry / biznesu.

  • Komponenty przechowują dane opisujące zachowanie.

Systemy są teraz odpowiedzialne za obsługę zdarzeń i wprowadzanie zachowania opisanego przez komponenty. Są również odpowiedzialne za obsługę interakcji między jednostkami, takich jak kolizje.

 

Podmioty i komponenty


Załóżmy jednak, że składniki zrobić mają odniesienia do siebie. Teraz istota jest po prostu konstruktorem, który tworzy niektóre komponenty, łączy je ze sobą i zarządza ich żywotnością:

class Player:
  construct():
    this.p = Position()
    this.v = Velocity(this.p)
    this.c = KeyboardControlled(this.v)

Jednostka może teraz wysyłać dane wejściowe i aktualizować zdarzenia bezpośrednio do swoich komponentów. Velocityodpowiadałby na aktualizacje i KeyboardControlledodpowiadałby na dane wejściowe. To nadal spełnia nasze kryteria:

  • Istota jest „głupim” pojemnikiem, który przekazuje zdarzenia tylko do komponentów.

  • Każdy składnik ma swoje własne zachowanie.

W tym przypadku interakcje komponentów są jawne, a nie narzucane z zewnątrz przez system. Dane opisujące zachowanie (jaka jest prędkość?) I kod, który je wprowadza (czym jest prędkość?) Są sprzężone, ale w naturalny sposób. Dane mogą być przeglądane jako parametry zachowania. Niektóre elementy w ogóle nie działają - Positionjest to zachowanie przebywania w miejscu .

Interakcje mogą być obsługiwane na poziomie bytu („gdy Playerzderza się z Enemy...”) lub na poziomie poszczególnych składników („gdy byt Lifezderza się z bytem z Strength…”).

 

składniki


Jaki jest powód istnienia bytu? Jeśli jest to tylko konstruktor, możemy go zastąpić funkcją zwracającą zestaw komponentów. Jeśli później chcemy zapytać o jednostki według ich typu, równie dobrze możemy mieć Tagkomponent, który pozwala nam to zrobić:

function Player():
  t = Tag("Player")
  p = Position()
  v = Velocity(p)
  c = KeyboardControlled(v)
  return {t, p, v, c}
  • Istoty są tak głupie, jak tylko mogą być - to tylko zestawy komponentów.

  • Komponenty reagują bezpośrednio na zdarzenia jak poprzednio.

Interakcje muszą być teraz obsługiwane przez abstrakcyjne zapytania, całkowicie oddzielając zdarzenia od typów jednostek. Nie ma więcej typów jednostek do zapytania - Tagdo debugowania prawdopodobnie lepiej są wykorzystywać dowolne dane niż logika gry.

 

Wniosek


Jednostki nie są funkcjami, regułami, aktorami ani kombinatorami przepływu danych. Są rzeczownikami, które modelują konkretne zjawiska - innymi słowy, są obiektami. Jest tak, jak mówi Wikipedia - systemy encji-komponentów są wzorcem architektury oprogramowania do modelowania obiektów ogólnych.

Jon Purdy
źródło
2
Wydaje się, że główna alternatywa dla opartego na klasach OO, opartego na prototypie OO również łączy dane i zachowanie. W rzeczywistości wygląda na to, że różni się od ECS tak samo, jak OO oparty na klasach. Czy mógłbyś więc wyjaśnić, co rozumiesz przez OO?
Aby dodać do pytania @ delnan, czy nie zgadzasz się z fragmentem cytowanego przeze mnie artykułu w Wikipedii OO?
Daniel Kaplan
@tieTYT: Cytat z Wikipedii mówi o enkapsulacji i ukrywaniu informacji. Nie sądzę, że jest to dowód na konieczność łączenia danych z zachowaniem, tylko że jest powszechny.
Jon Purdy
@delnan: OO nie mam na myśli niczego. Programowanie obiektowe jest dla mnie dokładnie tym, co mówi na opakowaniu: programowanie za pomocą „obiektów” (w przeciwieństwie do funkcji, reguł, aktorów, łączników przepływu danych itp.), W których konkretna definicja obiektu jest zdefiniowana za pomocą implementacji.
Jon Purdy
1
@tieTYT: Właśnie opisywałem implementacje, które widziałem na wolności, aby przekazać, że jest to szeroki termin - nie sprzeczny, ale z pewnością szerszy niż opis Wikipedii.
Jon Purdy,
20

NIE. I jestem zaskoczony, ilu ludzi głosowało inaczej!

Paradygmat

Jest to zorientowane na dane, czyli oparte na danych, ponieważ mówimy o architekturze, a nie o języku, w którym jest napisany. Architektury są realizacjami stylów programowania lub paradygmatów , które zwykle można omijać w danym języku.


Funkcjonalny?

Porównanie z programowaniem funkcjonalnym / proceduralnym jest trafnym i znaczącym porównaniem. Należy jednak pamiętać, że język „funkcjonalny” różni się od paradygmatu „proceduralnego” . I można wdrożyć ECS w języku Functional jak Haskell , co ludzie zrobili.


Gdzie zachodzi spójność

Twoje spostrzeżenie jest istotne i spot-on :

„... [ECS] nie przeszkadza ci we wdrożeniu go w języku OO, ale nie byłoby idiomatyczne, aby robić to zdecydowanie OO”


ECS / ES nie jest EC / CE

Istnieje różnica między architekturami opartymi na komponentach, „Entity-Component” i „Entity-Component-System”. Ponieważ jest to ewoluujący wzorzec projektowy, widziałem te definicje używane zamiennie. Architektury „EC” lub „CE” lub „Entity-Component” umieszczają zachowanie w komponentach , podczas gdy architektury „ES” lub „ECS” umieszczają zachowanie w systemach . Oto kilka artykułów ECS, z których oba wykorzystują wprowadzającą w błąd nomenklaturę, ale przedstawiają ogólny pomysł:

Jeśli próbujesz zrozumieć te warunki w 2015 r., Upewnij się, że czyjaś wzmianka o „systemie elementów encji” nie oznacza „architektury elementów encji”.

szczenię
źródło
1
To jest poprawna odpowiedź. ECS nie pasuje zbyt dobrze do paradygmatów OOP, ponieważ ECS polega na oddzielaniu danych i zachowania, podczas gdy OOP jest odwrotnie.
Nax 'vi-vim-nvim'
„podczas gdy OOP ma coś wręcz przeciwnego” Nie ma żadnej akceptowanej definicji tego, czym jest OOP, chyba że bezużyteczne akademickie definicje, takie jak SmallTalk, które nigdy nie zostaną wykorzystane w praktyce.
Jean-Michaël Celerier,
10

Systemy elementów encji (ECS) można programować w trybie OOP lub funkcjonalnie, w zależności od sposobu zdefiniowania systemu.

Sposób OOP:

Pracowałem nad grami, w których byt był obiektem złożonym z różnych elementów. Jednostka ma funkcję aktualizacji, która modyfikuje obiekt, wywołując kolejno aktualizację wszystkich jego składników. Ma to wyraźny styl OOP - zachowanie jest powiązane z danymi, a dane można modyfikować. Istoty to obiekty z konstruktorami / destruktorami / aktualizacjami.

Bardziej funkcjonalny sposób:

Alternatywą jest, aby jednostka była danymi bez żadnych metod. Ten podmiot może istnieć samodzielnie lub być po prostu identyfikatorem powiązanym z różnymi komponentami. W ten sposób możliwe jest (ale nie jest to powszechnie robione) bycie w pełni funkcjonalnym i posiadanie niezmiennych bytów i czystych systemów, które generują nowe stany składowe.

Wydaje się (z własnego doświadczenia), że ten drugi sposób zyskuje większą przyczepność i nie bez powodu. Oddzielenie danych encji od zachowania skutkuje bardziej elastycznym kodem wielokrotnego użytku (imo). W szczególności użycie systemów do aktualizacji komponentów / jednostek w partiach może być bardziej wydajne i całkowicie eliminuje złożoność komunikatów między jednostkami, które nękają wiele OOP ECS.

TLDR: Możesz to zrobić na dwa sposoby, ale argumentowałbym, że zalety dobrych systemów komponentów bytu wynikają z ich bardziej funkcjonalnego charakteru.

AGD
źródło
Większa przyczepność, zwłaszcza że cały punkt komponentów polegał na odejściu od trudnych hierarchii OOP, dobry opis korzyści.
Patrick Hughes,
2

Systemy komponentowe zorientowane na dane mogą współistnieć z paradygmatami zorientowanymi obiektowo: - Systemy komponentowe nadają się do polimorfizmu. - Komponentami mogą być zarówno POD (zwykłe stare dane), jak i obiekty ALSO (z klasą i metodami), a całość jest nadal „zorientowana na dane”, pod warunkiem, że metody klas komponentów przetwarzają tylko dane będące własnością obiektu lokalnego.

Jeśli wybierzesz tę ścieżkę, zalecam unikanie korzystania z metod wirtualnych, ponieważ jeśli je masz, twój komponent nie jest już danymi czysto komponentowymi, a te metody kosztują więcej za wywołanie - to nie jest COM. Z reguły utrzymuj klasy komponentów w czystości od wszelkich odniesień do czegokolwiek zewnętrznego.

Przykładem może być vec2 lub vec3, kontener danych z niektórymi metodami dotyku tych danych i niczym więcej.

Homer
źródło
2
ten post jest raczej trudny do odczytania (ściana tekstu). Czy mógłbyś edytować go w lepszym kształcie? Pomoże to również wyjaśnić czytelnikom, dlaczego link do blogu może być przydatny i odpowiedni do
zadanego
... w przypadku, jeśli w jakiś sposób związane z tym blogu (? jesteś), byłoby również pożądane, aby ujawniać przynależność
gnat
Tak, to jest mój blog, jestem ściśle związany z moim publicznym blogiem, który ujawnia szczegóły systemu obiektowego komponentu encji opartego na zasadach projektowania zorientowanego na dane, które moim zdaniem są istotne i prawdopodobnie przydatne, mimo to usunąłem link do usuń wszelkie uprzedzenia.
Homer,
2

Uważam ECS za zasadniczo odmienny od OOP i zwykle postrzegam go w ten sam sposób, co Ty, jako bliższy funkcjonalnemu lub szczególnie proceduralnemu charakterowi z bardzo wyraźnym oddzieleniem danych od funkcjonalności. Istnieje również pozór programowania w rodzaju centralnych baz danych. Oczywiście jestem najgorszą osobą, jeśli chodzi o formalne definicje. Martwię się tylko tym, jak się rzeczy mają, a nie tym, czym są koncepcyjnie zdefiniowane.

Zakładam rodzaj ECS, w którym komponenty agregują pola danych i udostępniają je publicznie / globalnie, podmioty agregują komponenty, a systemy zapewniają funkcjonalność / zachowanie tych danych. Prowadzi to do radykalnie trudnych cech architektonicznych z tego, co zwykle nazywamy obiektową bazą kodową.

I oczywiście jest pewne zatarcie granic w sposobie, w jaki ludzie projektują / wdrażają ECS, i jest debata na temat tego, co dokładnie stanowi ECS. Jednak takie granice są również zatarte w kodzie napisanym w języku, który nazywamy językiem funkcjonalnym lub proceduralnym. Wśród tych wszystkich zagadek podstawowa stała ECS z oddzieleniem danych od funkcjonalności wydaje mi się znacznie bliższa programowaniu funkcjonalnemu lub proceduralnemu niż OOP.

Jednym z głównych powodów, dla których nie uważam za przydatne uznanie ECS za należące do klasy OOP, jest to, że większość praktyk SE związanych z OOP obraca się wokół stabilności interfejsu publicznego, z funkcjami modelowania interfejsów publicznych , a nie danymi. Podstawową ideą jest to, że większość publicznych zależności płynie w kierunku funkcji abstrakcyjnych, a nie konkretnych danych. Z tego powodu OOP powoduje, że zmiana podstawowych zachowań projektowych jest bardzo kosztowna, a zmiana konkretnych szczegółów (takich jak dane i kod wymagane do wdrożenia funkcji) jest bardzo tania.

ECS jest pod tym względem diametralnie różny, biorąc pod uwagę sposób łączenia, ponieważ większość zależności publicznych płynie w kierunku konkretnych danych: od systemów do komponentów. W rezultacie wszelkie praktyki SE związane z ECS obracałyby się wokół stabilności danych , ponieważ najbardziej publiczne i powszechnie używane interfejsy (komponenty) są w rzeczywistości tylko danymi.

W rezultacie ECS bardzo ułatwia takie rzeczy, jak zamiana silnika renderującego OpenGL na DirectX, nawet jeśli oba są zaimplementowane z radykalnie różną funkcjonalnością i nie dzielą tych samych projektów, pod warunkiem, że zarówno silnik DX, jak i GL mieć dostęp do tych samych stabilnych danych. Tymczasem byłoby to bardzo kosztowne i wymagałoby przepisania kilku systemów, aby zmienić, powiedzmy, reprezentację danych MotionComponent.

Jest to zupełnie odwrotne od tego, co tradycyjnie kojarzymy z OOP, przynajmniej pod względem cech sprzężenia i tego, co stanowi „interfejs publiczny” vs. „prywatne szczegóły implementacji”. Oczywiście w obu przypadkach „szczegóły implementacji” są łatwe do zmiany, ale w ECS jest to projekt danych, które są kosztowne do zmiany (dane nie są szczegółami implementacji w ECS), aw OOP to projekt funkcjonalności, która jest kosztowna do zmiany (projektowanie funkcji nie jest szczegółem implementacji w OOP). To zupełnie inny pomysł na „szczegóły implementacji”, a jednym z głównych apelacji do ECS z punktu widzenia konserwacji było to, że w mojej domenie, dane potrzebne do zrobienia rzeczy łatwiej było ustabilizować i poprawnie zaprojektować raz na zawsze, niż wszystkie różne rzeczy, które moglibyśmy zrobić z tymi danymi (co zmieniałoby się cały czas, gdy klienci zmieniali zdanie i napływały nowe sugestie użytkowników). W rezultacie zauważyłem, że koszty utrzymania gwałtownie spadły, kiedy zaczęliśmy kierować zależności od funkcji abstrakcyjnych w kierunku surowych, centralnych danych (ale nadal z troską o to, które systemy uzyskują dostęp do których komponentów, aby umożliwić utrzymywanie niezmienników w rozsądnym stopniu, pomimo wszystkich danych koncepcyjnie globalnie dostępne).

I przynajmniej w moim przypadku zestaw SDK ECS z interfejsem API i wszystkimi komponentami jest faktycznie zaimplementowany w języku C i nie jest podobny do OOP. Uważam, że C jest bardziej niż wystarczające do takiego celu, biorąc pod uwagę nieodłączny brak OO w architekturach ECS i chęć posiadania architektury wtyczek, która mogłaby być używana przez najszerszą gamę języków i kompilatorów. Systemy są nadal zaimplementowane w C ++, ponieważ C ++ sprawia, że ​​jest tam bardzo wygodnie, a systemy modelują większość złożoności i tam znajduję zastosowanie do wielu rzeczy, które mogą być uważane za bliższe OOP, ale to dotyczy szczegółów implementacji. Sam projekt architektoniczny nadal bardzo przypomina procedurę C.

Sądzę więc, że przynajmniej nieco mylące jest stwierdzenie, że ECS jest z definicji OO. Przynajmniej podstawy wykonują rzeczy, które są całkowicie obrócone o 180 stopni w stosunku do wielu podstawowych zasad ogólnie związanych z OOP, zaczynając od enkapsulacji, a może kończąc na czymś, co można by uznać za pożądane cechy sprzężenia.


źródło