Tworząc gry, często tworzysz następujący obiekt gry, z którego dziedziczą wszystkie byty:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
Tak więc podczas aktualizacji pętli iterujesz wszystkie obiekty gry i dajesz im szansę na zmianę stanu, a następnie w następnej pętli losowania ponownie iterujesz wszystkie obiekty gry i dajesz im szansę na narysowanie się.
Chociaż działa to dość dobrze w prostej grze z prostym rendererem do przodu, często prowadzi do kilku gigantycznych obiektów gry, które muszą przechowywać swoje modele, wiele tekstur i, co najgorsze, ze wszystkich grubych metod rysowania, które tworzą ścisłe połączenie między obiektem gry, aktualna strategia renderowania i wszelkie klasy związane z renderowaniem.
Gdybym miał zmienić strategię renderowania z naprzód na odroczony, musiałbym zaktualizować wiele obiektów w grze. A obiekty gry, które tworzę, nie nadają się do wielokrotnego użytku. Oczywiście, dziedziczenie i / lub kompozycja może pomóc mi w walce z duplikacją kodu i nieco ułatwić zmianę implementacji, ale nadal wydaje mi się, że jej brakuje.
Być może lepszym sposobem byłoby całkowite usunięcie metody Draw z klasy GameObject i utworzenie klasy Renderer. GameObject nadal musiałby zawierać pewne dane dotyczące jego wyglądu, takie jak model, który ma być reprezentowany i jakie tekstury powinny zostać namalowane na modelu, ale sposób wykonania zostanie pozostawiony do renderowania. Jednak podczas renderowania często występuje wiele przypadków granicznych, więc chociaż usunęłoby to ścisłe powiązanie GameObject z Rendererem, Renderer nadal musiałby wiedzieć wszystko o wszystkich obiektach gry, które sprawiłyby, że byłby gruby, wszystko wiedział i ściśle powiązane. Naruszyłoby to sporo dobrych praktyk. Może projektowanie zorientowane na dane może załatwić sprawę. Obiektami gier z pewnością byłyby dane, ale w jaki sposób mechanizm renderujący byłby przez to sterowany? Nie jestem pewny.
Więc jestem zagubiony i nie mogę wymyślić dobrego rozwiązania. Próbowałem użyć zasad MVC, a kiedyś miałem pewne pomysły, jak to wykorzystać w grach, ale ostatnio nie wydaje się to tak odpowiednie, jak myślałem. Chciałbym wiedzieć, jak wszyscy rozwiązujecie ten problem.
Podsumowując, interesuje mnie, w jaki sposób można osiągnąć następujące cele projektowe.
- Brak logiki renderowania w obiekcie gry
- Luźne sprzężenie między obiektami gry a silnikiem renderowania
- Nie wszyscy znający renderer
- Najlepiej środowisko przełączające między silnikami renderującymi
Idealna konfiguracja projektu byłaby oddzielną „logiką gry” i renderowała projekt logiki, który nie musi się nawzajem odnosić.
Ten pociąg myślowy zaczął się, gdy usłyszałem na Twitterze, że John Carmack mówi, że ma system tak elastyczny, że może wymieniać silniki renderujące w czasie wykonywania, a nawet może powiedzieć swojemu systemowi, aby używał obu rendererów (renderera oprogramowania i renderera przyspieszanego sprzętowo) jednocześnie, aby mógł sprawdzić różnice. Systemy, które do tej pory programowałem, nie są nawet tak elastyczne
źródło
To, co zrobiłem dla własnego silnika, to zgrupowanie wszystkiego w moduły. Mam więc swoją
GameObject
klasę i ma ona uchwyt do:Więc mam
Player
klasę iBullet
klasę. Oba pochodzą zGameObject
i są dodawane doScene
. AlePlayer
ma następujące moduły:I
Bullet
ma te moduły:Ten sposób organizowania rzeczy pozwala uniknąć „Diamentu śmierci”, w którym masz A
Vehicle
, AVehicleLand
i A,VehicleWater
a teraz chceszVehicleAmphibious
. Zamiast tego masz aVehicle
i może miećModuleWater
aModuleLand
.Dodano bonus: możesz tworzyć obiekty za pomocą zestawu właściwości. Wszystko, co musisz wiedzieć, to typ podstawowy (Gracz, Wróg, Pocisk itp.), A następnie utwórz uchwyty do modułów potrzebnych dla tego typu.
W mojej scenie wykonuję następujące czynności:
Update
do wszystkichGameObject
uchwytów.ModuleCollision
uchwyt.UpdatePost
do wszystkichGameObject
uchwytów, aby poinformować o ich ostatecznej pozycji po fizyce.m_ObjectsCreated
listy dom_Objects
listy.I mógłbym to zorganizować dalej: według modułów zamiast według obiektu. Następnie renderowałbym listę
ModuleSprite
, aktualizowałem kilkaModuleScriptingBase
i kolizji z listąModuleCollision
.źródło
GameObject
(na przykład sposobu renderowania „węża” duszków), musisz albo utworzyć element podrzędnyModuleSprite
dla tej określonej funkcji (ModuleSpriteSnake
), albo całkowicie dodać nowy moduł (ModuleSnake
). Na szczęście są to tylko wskaźniki, ale widziałem kod, w którymGameObject
dosłownie wszystko, co mógł zrobić obiekt.