Robię małego robota lochowego w kosmosie i chciałbym usłyszeć porady, jak ulepszyć backend silnika. Zasadniczo obecnie wszystko opiera się na crapload menedżerów:
- BackgroundManager: ma
AddBackground(image, parallax)
metodę tworzenia fajnych efektów tła. - ConfigManager: czyta / tworzy plik konfiguracyjny, a także przechowuje dane odczytane z tego pliku konfiguracyjnego.
- DrawManager: ma
Draw(sprite)
metodę rysowania rzeczy na ekranieSetView(view)
,GetView()
itp. - EntityManager: przechowuje wszystkie aktywne encje i ma metody dodawania, usuwania i wyszukiwania encji.
- DungeonManager (w rzeczywistości nazywa się GridManager, ale jest to dla uproszczenia): ma podobne metody
GenerateDungeon()
,PlaceEnemies()
,PlaceFinish()
itd.
Teraz nie jestem nawet w połowie listy wszystkich moich menedżerów, więc myślę, że to musi się zmienić. Moja gra jest również oparta na ekranach, co czyni ją bardziej irytującą, ponieważ nie potrzebuję połowy rzeczy, którymi zarządzają moi menedżerowie, na przykład menu głównego (nie potrzebuję fizyki / bytów / lochów w moim menu głównym !)
Teraz pomyślałem o tym, aby menedżerowie nie byli statyczni i aby moja ScreenMainGame była instancją wszystkich menedżerów specyficznych dla gry. Ale to sprawia, że dzwonienie / zdobywanie czegokolwiek związanego z menedżerami jest ogromnym bałaganem ... na przykład, gdybym chciał narysować lochy z dowolnego miejsca, w którym nie ma ScreenMainGame.Draw()
, musiałbym to zrobić ...
((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()
To naprawdę brzydkie.
Zatem, twórcy gier, czy ktoś z was ma pomysł, jak rozwiązać ten bałagan? Byłbym doceniony!
źródło
Odpowiedzi:
Co z silnikiem opartym na komponentach ?
Miałbyś główną klasę o nazwie
Engine
, która prowadziłaby listęGameScreens
, która sama posiadałaby listęComponents
.Silnik posiada
Update
orazDraw
metody i zarówno zadzwonić doGameScreen
„sUpdate
iDraw
metod, które same przejść przez każdego komponentu i rozmowyUpdate
iDraw
.Tak przedstawiony, zgadzam się, że brzmi jak kiepski i powtarzalny projekt. Ale uwierz mi, mój kod stał się znacznie czystszy dzięki zastosowaniu podejścia opartego na komponentach niż w przypadku wszystkich moich starych klas menedżerów .
Utrzymanie takiego kodu również jest o wiele prostsze, ponieważ przechodzisz właśnie przez dużą hierarchię klas i nie musisz przeszukiwać
BackgroundManager
wszystkich różnych środowisk. Po prostu miećScrollingBackground
,ParallaxBackground
,StaticBackground
, itd., Które wywodzą się zBackground
klasy.W końcu zbudujesz całkiem solidny silnik, który możesz ponownie wykorzystać we wszystkich swoich projektach z wieloma często używanymi komponentami i metodami pomocniczymi (np.
FrameRateDisplayer
Jako narzędzie do debugowania,Sprite
klasa jako podstawowy duszek z metodami tekstury i rozszerzeń dla wektorów i generowanie liczb losowych).Nie miałbyś już
BackgroundManager
klasy, aleBackground
klasę, która sama sobie poradziłaby.Po rozpoczęciu gry wszystko, co musisz zrobić, to w zasadzie:
I to wszystko w przypadku kodu początkowego gry.
Następnie na ekranie menu głównego:
Masz ogólny pomysł.
Zachowałbyś również odniesienie do
Engine
wszystkich swoichGameScreen
klas, aby móc dodawać nowe ekrany nawet w obrębieGameScreen
klasy (np. Kiedy użytkownik kliknie przycisk StartGame w twojej klasieMainMenuScreen
, możesz przejść doGameplayScreen
).To samo dotyczy
Component
klasy: powinna zawierać referencję swojego rodzicaGameScreen
, aby mieć zarówno dostęp doEngine
klasy, jak i jej rodzica,GameScreen
aby dodać nowe komponenty (np. Możesz stworzyć klasę związaną z interfejsem HUD o nazwie,DrawableButton
która zawieraDrawableText
komponent iStaticBackground
komponent).Następnie możesz zastosować inne wzorce projektowe, takie jak „wzorzec projektowy usługi” (nie jestem pewien dokładnej nazwy), w którym możesz przechowywać różne przydatne usługi w swojej
Engine
klasie (po prostu przechowujesz listęIService
s, a inne klasy mogą same dodawać usługi ). np. zachowałbymCamera2D
komponent w całym moim projekcie jako usługę, aby zastosować jego transformację podczas rysowania innych komponentów. Pozwala to uniknąć konieczności przekazywania go jako parametru wszędzie.Podsumowując, z pewnością mogą istnieć inne lepsze konstrukcje silnika, ale uznałem, że silnik zaproponowany przez to łącze jest bardzo elegancki, niezwykle łatwy w konserwacji i wielokrotnego użytku. Osobiście poleciłbym przynajmniej spróbować.
źródło
GameComponent
usług i usług. Nie ma potrzeby oddzielnejEngine
klasy.To, co chcesz zrobić, to podzielić kod na komponenty i usługi. XNA ma już wbudowane wsparcie dla nich.
Zobacz ten artykuł na
GameComponents
i usług. Następnie zapoznaj się z tą odpowiedzią na temat korzystania z usług w XNA i bardziej ogólną odpowiedzią na usługi w dowolnym języku.źródło
To, że są statyczne lub globalne, nie oznacza, że zawsze muszą być ładowane / inicjowane. Użyj wzorca Singleton i pozwól menedżerom być darmowym i ładować je ponownie na żądanie.
źródło