Przez jakiś czas interesowałem się systemem encji opartym na komponentach i czytałem o nim niezliczone artykuły ( gry Insomiac , dość standardowy Evolve Your Hierarchy , T-Machine , Chronoclast ... żeby wymienić tylko kilka).
Wszystkie wydają się mieć strukturę na zewnątrz czegoś takiego jak:
Entity e = Entity.Create();
e.AddComponent(RenderComponent, ...);
//do lots of stuff
e.GetComponent<PositionComponent>(...).SetPos(4, 5, 6);
A jeśli wprowadzisz pomysł udostępniania danych (jest to najlepszy projekt, jaki do tej pory widziałem, pod względem nie powielania danych wszędzie)
e.GetProperty<string>("Name").Value = "blah";
Tak, to jest bardzo wydajne. Jednak nie jest to najłatwiejszy do odczytu lub zapisu; czuje się bardzo niezręcznie i działa przeciwko tobie.
Osobiście chciałbym zrobić coś takiego:
e.SetPosition(4, 5, 6);
e.Name = "Blah";
Chociaż oczywiście jedynym sposobem, aby dostać się do tego rodzaju projektu, jest powrót w rodzaju hierarchii Entity-> NPC-> Enemy-> FlyingEnemy-> FlyingEnemyWithAHatOn.
Czy ktoś widział projekt tego rodzaju systemu komponentów, który jest wciąż elastyczny, ale zachowuje poziom przyjazności dla użytkownika? A jeśli chodzi o tę sprawę, udaje się obejść (prawdopodobnie najtrudniejszy problem) przechowywanie danych w dobry sposób?
Jakie są projekty systemu jednostek opartego na komponentach, który jest przyjazny dla użytkownika, ale nadal elastyczny?
źródło
Sugerowałbym jakąś klasę interfejsu dla twoich obiektów Entity byłby miły. Może to zrobić obsługę błędów z sprawdzaniem, aby upewnić się, że encja zawiera również składnik o odpowiedniej wartości w jednym miejscu, abyś nie musiał tego robić wszędzie, gdy uzyskujesz dostęp do wartości. Alternatywnie, większość projektów, które kiedykolwiek zrobiłem z systemem opartym na komponentach, zajmuję się komponentami bezpośrednio, prosząc na przykład o ich komponent pozycyjny, a następnie bezpośrednio uzyskując / aktualizując właściwości tego komponentu.
Bardzo podstawowa na wysokim poziomie, klasa bierze pod uwagę byt i zapewnia łatwy w użyciu interfejs do podstawowych części składowych w następujący sposób:
źródło
W Pythonie możesz przechwycić część „setPosition”
e.SetPosition(4,5,6)
poprzez zadeklarowanie__getattr__
funkcji na Entity. Ta funkcja może iterować po komponentach, znajdować odpowiednią metodę lub właściwość i zwracać ją, aby wywołanie funkcji lub przypisanie trafiło we właściwe miejsce. Być może C # ma podobny system, ale może nie być to możliwe, ponieważ jest statycznie wpisany - prawdopodobnie nie może się skompilować,e.setPosition
chyba że e gdzieś ustawił pozycjęPozycja.Być może możesz również zmusić wszystkie swoje jednostki do implementacji odpowiednich interfejsów dla wszystkich komponentów, które kiedykolwiek możesz do nich dodać, za pomocą metod pośredniczących, które zgłaszają wyjątek. Następnie wywołując addComponent, po prostu przekierowujesz funkcje encji dla interfejsu tego komponentu do komponentu. Trochę niezręcznie.
Ale może najłatwiej byłoby przeciążać operatora [] na swojej klasy Entity szukać załączonego komponenty na obecność ważnej własności i wrócić, że tak może być przypisany do tak:
e["Name"] = "blah"
. Każdy komponent będzie musiał zaimplementować własną GetPropertyByName, a Entity wywoła każdy z nich po kolei, dopóki nie znajdzie komponentu odpowiedzialnego za daną właściwość.źródło
GetProperty
metoda .. Jedynym minusem jest manipulowanie ciągiem i porównywanie. Ale to kwestia sporna.Aby rozwinąć odpowiedź Kylotana, jeśli używasz C # 4.0, możesz statycznie wpisać zmienną, aby być dynamiczną .
Jeśli odziedziczysz po System.Dynamic.DynamicObject, możesz zastąpić TryGetMember i TrySetMember (spośród wielu metod wirtualnych), aby przechwycić nazwy komponentów i zwrócić żądany komponent.
Przez lata pisałem trochę o systemach bytów, ale zakładam, że nie mogę konkurować z gigantami. Niektóre notatki ES (nieco przestarzałe i niekoniecznie odzwierciedlają moje obecne rozumienie systemów bytów, ale warto je przeczytać, imho).
źródło
object
parametr wyjściowy - ale wszystko to zostanie rozwiązane w czasie wykonywania. Myślę, że to świetny sposób na utrzymanie płynnego interfejsu np. Entity.TryGetComponent (nazwa_komputera, składnik wyjściowy). Nie jestem jednak pewien, czy zrozumiałem pytanie :)Widzę duże zainteresowanie tutaj w ES na C #. Sprawdź mój port doskonałej implementacji ES Artemis:
https://github.com/thelinuxlich/artemis_CSharp
Ponadto, przykładowa gra na początek, jak to działa (za pomocą XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
Sugestie są mile widziane!
źródło
Zdecydowałem się użyć doskonałego systemu odbicia C #. Oparłem go na ogólnym systemie komponentów oraz mieszance odpowiedzi tutaj.
Komponenty są dodawane bardzo łatwo:
I można zapytać o nazwę, typ lub (jeśli takie jak Zdrowie i Pozycja) o właściwości:
I usunięty:
Ponownie dostęp do danych mają zwykle właściwości:
Który jest przechowywany po stronie komponentu; mniejszy narzut, jeśli nie ma HP:
Dużej ilości pisania pomaga Intellisense w VS, ale w większości przypadków mało jest pisania - tego szukałem.
Za kulisami przechowuję
Dictionary<Type, Component>
iComponents
zastępujęToString
metodę sprawdzania nazw. Mój oryginalny projekt używał przede wszystkim ciągów nazw i rzeczy, ale stał się świnią do pracy (och, muszę rzucić wszędzie, a także brak łatwo dostępnych właściwości).źródło