Fragen zur Spielearchitektur mit XNA

12

Also habe ich es endlich geschafft, mit XNA herumzuspielen und ein 2D-Spiel zu erstellen (ich habe einige Kunstobjekte von einem Freund, der es für iOS entwickelt hat).

Viele Dinge scheinen einfach zu erledigen zu sein und kommen aus dem Kasten, aber ich bin verblüfft, weil die meiste Literatur (zum Beispiel die Bücher, die ich gekauft habe) 2D nicht allzu viel Aufmerksamkeit schenkt.

Ich würde mich über jede Klarstellung oder jeden Hinweis auf weitere Informationen zu den folgenden Fragen sehr freuen:

  1. Was ist der Sinn eines Game Service? Ich verstehe die ganze Redewendung, ein Objekt als Dienst zu registrieren, damit es von jeder anderen GameComponent abgerufen werden kann. Wie schlägt sich das jedoch einfach nieder, wenn es möglicherweise öffentlich oder statisch ist? In meinem Buch wurde beispielsweise empfohlen, das SpriteBatch-Objekt in der Game.cs-Klasse zu registrieren. Ich bin mir nicht sicher, wie das vorzuziehen ist, es einfach öffentlich / statisch zu machen, da es sowieso nur eine Instanz von Game geben sollte (Singleton)?

  2. Ich bin verwirrt, wann ich von GameComponent oder RenderableGameComponent erben soll. Ich versuche, einem Manager- / Controller-Design zu folgen, sodass alle Entitäten von einem einzigen Manager erstellt werden und für andere Dinge die gleichen. Ich habe derzeit jeden Manager / Controller von GameComponent geerbt. Wie schlägt es sich jedoch, wenn das Game-Objekt alle Manager besitzt und die Aktualisierung manuell aufruft und zeichnet?

  3. Mir ist aufgefallen, dass Initialize vor ContentLoad () aufgerufen wird. Ich fand das ärgerlich, da ich in Initialize einige meiner Entitäten (z. B. Sprite, Player usw.) erstellen möchte, aber ich kann sie nicht mit ihren geladenen Entitäten versehen SpriteSheets oder Textures sind seit dem Aufruf zum Laden noch nicht aufgetreten. Initialisiere ich möglicherweise falsch oder weisen die Benutzer die Textur nur weiter unten in ContentLoad zu?

Das scheint mein größter " WTF " zu sein, der nicht wirklich versteht

Setheron
quelle

Antworten:

11

Die Antwort auf Ihre Frage lautet einfach: Tun Sie, was auch immer funktioniert. Im Idealfall tun, was einfach ist und funktioniert. Oft wird die eingebaute Architektur nicht verwendet einfach, da Sie, wie Sie feststellen, durch die Rahmen springen müssen, um Ihr Spiel nach Ihren Wünschen zu strukturieren.

Um die erste Frage zu beantworten, verweise ich Sie auf meine Antwort auf die Frage "Warum Dienste nutzen?" . Die kurze Antwort lautet, dass Dienste ein guter Weg sind, um Kopplungsprobleme (Versionierung, Abhängigkeit, Erweiterbarkeit) zu vermeiden, die in einem eigenständigen Spiel niemals auftreten werden . Meistens ist es einfacher, nur ein Mitglied zu machen public(und vielleicht sogarstatic , wenn Sie es eilig haben) und sich über interessantere Probleme Gedanken zu machen.

Um Ihre zweite Frage zu beantworten, verweise ich Sie auf meine Antwort auf die Frage "Was sind die Nachteile der Verwendung von DrawableGameComponent für jede Instanz eines Spielobjekts?" so gut wie meine Gedanken zur Spielarchitektur . Die kurze Antwort lautet: Es gibt keinen Nutzen , die zur Verwendung von GameComponentmehr als nur die Erstellung Ihrer eigenen Klassen und Aufrufen von Methoden wie Drawund Updatesich selbst. Wenn es GameComponentgenau zu Ihrer gewünschten Architektur passt, verwenden Sie es auf jeden Fall - aber fast immer nicht.

Die Verwendung von Diensten und Komponenten wird nur dann wirklich interessant, wenn Sie eine Bibliothek für den Verbrauch durch Dritte erstellen. XNA selbst macht das - es hatIGraphicsDeviceService und GamerServicesComponentzum Beispiel. Diese erfüllen bestimmte Entkopplungsanforderungen, denen Sie bei der Entwicklung eines Spiels normalerweise nicht begegnen.

Schließlich ist die Antwort auf Ihre dritte Frage ganz einfach: Erstellen Sie einfach Ihre Spielelemente in LoadContent. Es gibt nichts Besonderes Initialize.

Es sei darauf hingewiesen, dass die gesamte Microsoft.Xna.Framework.GameBaugruppe optional ist . Es ist ein äußerst nützlicher Ausgangspunkt - aber es sollte keinesfalls als "der einzig richtige Weg" betrachtet werden, um Dinge zu tun.

Andrew Russell
quelle
Sehr interessante Art und Weise, es zu sehen. Viel realistischer und bodenständiger als meine Antwort. Gut verdient +1, Sir.
Jesse Emond
Ich habe noch eine Frage! Was ist mit dem Ursprung, wenn Sie für eine Texture2D zeichnen. Ist es üblich, den Ursprung in der unteren Mitte der Textur zu platzieren? Ich versuche, Animationen zu machen, und ich stelle fest, dass der Ursprung bei (0,0) dazu führt, dass sich die Textur leicht verschiebt, wenn Breite und Höhe nicht gleich sind
Setheron,
@Setheron: Du solltest wirklich eine neue Frage erstellen - da dies nichts mit der ursprünglichen Frage zu tun hat. Ich werde die Bearbeitung, die Sie an dieser Frage vorgenommen haben, rückgängig machen. Der Ursprung für SpriteBatch.Drawwird in Textur-Pixel-Koordinaten relativ zur oberen linken Ecke der Textur (oder des Quellrechtecks, falls Sie eines verwenden) angegeben.
Andrew Russell
1
@ Andrew: Ich habe mein Spiel kürzlich überarbeitet, sodass es ausschließlich auf Diensten statt auf public/ staticproperties basiert . Warum? Testbarkeit. Es ist nahezu unmöglich, Dinge mit dem Property-Ansatz auszutauschen, während es trivial ist, wenn alles mit einem initialisiert wird IServiceProvider, das durch Ihre Testmethode mit allem gefüllt werden kann, was die Klasse benötigt. Es wird auch vermieden, das GameObjekt selbst in Ihrem Test zu instanziieren , was sehr schnell chaotisch wird.
Matthew Scharley
Abgesehen davon habe ich noch viele öffentliche Eigenschaften für das GameObjekt. Der Zugriff auf sie erfolgt zufällig über Schnittstellen, in Game.Servicesdie sie hineingeschoben werden, und die dann an alles weitergegeben werden, um wieder das herauszufinden, was sie brauchen.
Matthew Scharley
3

Bei Ihrer ersten Frage muss ich zugeben, dass ich die eigentliche Antwort nicht wirklich kenne. Ich denke, es wäre aus dem gleichen Grund, warum Singleton im Allgemeinen ein schlechtes Designmuster ist. Ich verweise Sie auf ein Zitat aus diesem Link :

Der erste Gedanke (und einer der Gründe für die Einführung von Diensten) bestand darin, eine GraphicsDevice-Referenz für ein Spiel verfügbar zu machen. Ursprünglich besaß das Spiel das GraphicsDevice und machte es über eine Eigenschaft wie Game.GraphicsDevice verfügbar. Das Problem dabei war, dass dadurch das GraphicsDevice eng an das Spiel gebunden wurde, niemand anderes das Gerät „besitzen“ durfte (z. B. kein Renderer) und niemand daran gehindert wurde, ein zukünftig aktualisiertes GraphicsDevice zu verwenden, ohne dass eine neue Version des Spiels ausgeliefert wurde, die dies nicht zuließ. nicht abwärtskompatibel sein.

Für Ihre zweite Frage würde ich annehmen, dass die Verwendung von Komponenten wie dieser sinnvoller wäre, wenn Sie stattdessen einen komponentenbasierten Ansatz verfolgen würden, als wenn Sie eine Liste von SpriteKomponenten hätten, die wissen, wie sie sich selbst zeichnen und aktualisieren können, anstatt eines Manager, der weiß, wie man jedes Sprite aktualisiert und zeichnet. Ich stimme zu, es klingt genauso, aber ich bevorzuge ein komponentenbasiertes Design, weil mir der objektorientierte Ansatz wirklich gefällt.

Ja, für Ihre dritte Frage sollten Sie jede Form von Inhalt (Elemente, Schriftarten usw.) in die LoadContent-Methode laden. Bei der Initialisierung erstellen Sie normalerweise das Player-Objekt in der Initialisierungsfunktion und laden dann dessen Inhalt in LoadContent. Sie können aber auch alles in LoadContent ausführen, wenn Sie dies wünschen.

Jesse Emond
quelle