Pytania dotyczące architektury gier za pomocą XNA

12

Więc w końcu zacząłem bawić się XNA i bawiłem się tworzeniem gry 2D (mam mnóstwo zasobów graficznych od znajomego, który opracował ją na iOS)

Wydaje się, że wiele rzeczy jest łatwych do zrobienia i wychodzi z pudełka, ale jestem zaskoczony wieloma rzeczami, ponieważ większość literatury (na przykład książki, które kupiłam) nie zwraca zbytniej uwagi na 2D.

Byłbym wdzięczny za wszelkie wyjaśnienia lub wskazanie na więcej informacji na temat następujących zadań:

  1. Jaki jest sens usługi gier? Rozumiem cały idiom rejestrowania obiektu jako usługi, aby każdy inny GameComponent mógł go chwycić, ale jak to się bije po prostu czyniąc go publicznym lub statycznym? Na przykład moja książka zaleciła zarejestrowanie obiektu SpriteBatch w klasie Game.cs. Nie jestem pewien, jak to jest lepsze, niż upublicznienie / statyczne, ponieważ i tak powinna istnieć tylko jedna instancja gry (singleton)?

  2. Nie jestem pewien, kiedy powinienem dziedziczyć po GameComponent lub RenderableGameComponent. Staram się postępować zgodnie z projektem menedżera / kontrolera, tak aby wszystkie podmioty były tworzone / posiadane przez jednego menedżera i tak samo w przypadku innych rzeczy. Obecnie każdy menedżer / kontroler dziedziczy po GameComponent, ale jak to się dzieje, że obiekt Game jest właścicielem wszystkich menedżerów i ręcznie wywołuje aktualizację na nich i rysuje?

  3. Zauważyłem, że Initialize jest wywoływane przed ContentLoad (), uważałem to za irytujące, ponieważ w mojej Inicjalizacji jest miejsce, w którym chciałbym utworzyć niektóre z moich bytów (np. Sprite, Player itp.), Jednak nie mogę dać im ich załadowania Arkusze lub tekstury Sprite, ponieważ wywołanie ich załadowania jeszcze nie nastąpiło. Czy być może inicjuję niepoprawnie, czy może ludzie przypisują teksturę dalej w ContentLoad?

To wydaje się być moim największym „ WTF ” niezrozumienia

Setheron
źródło

Odpowiedzi:

11

Odpowiedź na twoje pytanie brzmi: Rób wszystko, co działa. Idealnie rób to, co jest proste i działa. Często korzystanie z wbudowanej architektury nie jest proste, ponieważ, jak się okazuje, musisz skakać przez obręcze, aby ustrukturyzować grę tak, jak chcesz.

Aby odpowiedzieć na pytanie pierwsze, odsyłam cię do mojego odpowiedzi na pytanie „Dlaczego warto korzystać z usług?” . Krótka odpowiedź brzmi: usługi są dobrym sposobem na uniknięcie problemów związanych z łączeniem (wersjonowanie, zależność, rozszerzalność) , których nigdy nie spotkasz w samodzielnej grze . W większości przypadków łatwiej jest po prostu zostać członkiem public(a może nawet static, jeśli się spieszysz) i martwić się o bardziej interesujące problemy.

Aby odpowiedzieć na twoje drugie pytanie, odsyłam cię do mojej odpowiedzi na pytanie „Jakie są wady korzystania z DrawableGameComponent dla każdej instancji obiektu gry?” jak również moje przemyślenia na temat architektury gry . Krótka odpowiedź brzmi: nie ma żadnej korzyści z używania GameComponentover over po prostu tworzenia własnych klas i wywoływania metod takich jak Drawi Updatety. Jeśli GameComponentdokładnie pasuje do pożądanej architektury, na pewno ją wykorzystaj - ale prawie zawsze tak nie jest.

Korzystanie z usług i komponentów naprawdę staje się interesujące tylko wtedy, gdy budujesz bibliotekę do użytku innych firm. XNA to robi - maIGraphicsDeviceService i GamerServicesComponentna przykład. Spełniają one określone wymagania w zakresie oddzielania płatności od produkcji, które zwykle nie występują.

Wreszcie odpowiedź na twoje trzecie pytanie jest dość prosta: po prostu stwórz swoje jednostki gry LoadContent . Nie ma w tym nic szczególnego Initialize.

Warto zauważyć, że cały Microsoft.Xna.Framework.Gamezespół jest opcjonalny . Zapewnia niezwykle przydatny punkt wyjścia - ale w żadnym wypadku nie należy go uważać za „jedyną właściwą drogę” do robienia rzeczy.

Andrew Russell
źródło
Bardzo ciekawy sposób, aby to zobaczyć. Znacznie bardziej realistyczny i realistyczny niż moja odpowiedź. Zasłużony +1, proszę pana.
Jesse Emond
Mam jeszcze jedno pytanie! Co z początkiem, gdy rysujesz dla Texture2D. Czy często umieszcza się początek w dolnej środkowej części tekstury? Próbuję tworzyć animacje i stwierdzam, że początek w (0,0) powoduje nieznaczne przesunięcie tekstury, jeśli szerokość i wysokość nie są takie same
Setheron
@Setheron: Naprawdę powinieneś utworzyć nowe pytanie - ponieważ jest to zupełnie niezwiązane z pierwotnym pytaniem. Cofnę edycję, której dokonałeś w tym pytaniu. Początek SpriteBatch.Drawjest określony za pomocą współrzędnych tekstur-piksel względem lewego górnego rogu tekstury (lub prostokąta źródłowego, jeśli go używasz).
Andrew Russell,
1
@Andrew: Niedawno odnowiłem swoją grę, tak aby była całkowicie oparta na usługach zamiast public/ staticwłaściwości. Dlaczego? Testowalność Jest prawie niemożliwe, aby zmienić rzeczy za pomocą podejścia opartego na właściwościach, podczas gdy jest to trywialne, jeśli wszystko jest inicjowane za pomocą metody, IServiceProviderktóra może być wypełniona wszystkim, czego klasa potrzebuje za pomocą metody testowania. Pozwala to również uniknąć konieczności tworzenia instancji samego Gameobiektu w teście, co bardzo szybko się psuje.
Matthew Scharley,
Nawiasem mówiąc, nadal mam wiele publicznych właściwości Gameobiektu. Zdarza się, że są dostępne za pośrednictwem interfejsów, które są wpychane, Game.Servicesa następnie przekazywane do wszystkiego, aby ponownie uzyskać to, czego potrzebują.
Matthew Scharley
3

W przypadku pierwszego pytania muszę przyznać, że tak naprawdę nie znam rzeczywistej odpowiedzi. Myślę, że byłoby to z tego samego powodu, dla którego singleton jest ogólnie złym wzorcem projektowym. Odsyłam cię do cytatu z tego linku :

Pierwszym, o którym myśleliśmy (i jednym z powodów wprowadzenia usług), było udostępnienie referencji GraphicsDevice w całej grze. Pierwotnie mieliśmy grę będącą właścicielem GraphicsDevice i udostępniamy ją za pośrednictwem właściwości takiej jak Game.GraphicsDevice. Problem polegał na tym, że ściśle wiązało to urządzenie GraphicsDevice z grą, nie pozwalając komuś innemu na „posiadanie” urządzenia (np. Renderera) i uniemożliwiając komuś korzystanie z przyszłej zaktualizowanej karty GraphicsDevice bez wysyłania nowej wersji gry, która nie „ być kompatybilny wstecz.

W przypadku drugiego pytania zakładam, że użycie Komponentów w ten sposób byłoby bardziej przydatne, gdybyś podążał za komponentami podejście , tak jakbyś miał listę Spritekomponentów, które potrafiłyby rysować i aktualizować się zamiast menedżer, który wie, jak zaktualizować i narysować każdą duszkę. Zgadzam się, to brzmi tak samo, ale wolę projektowanie oparte na komponentach, ponieważ bardzo podoba mi się podejście obiektowe.

W przypadku trzeciego pytania tak należy załadować dowolną formę treści (zasoby, czcionki itp.) W metodzie LoadContent. Jeśli chodzi o inicjalizację, zwykle tworzysz obiekt Player w funkcji Initialize, a następnie ładujesz jego zawartość w LoadContent. Możesz też zrobić to wszystko w LoadContent, jeśli chcesz.

Jesse Emond
źródło