Entwurfsmuster und Struktur im Microsoft XNA Framework: Game Component und Service Locator
Update/Draw-Schleife
Das Microsoft XNA Framework baut in seiner Struktur zunächst auf eine einfache Trennung zwischen Aktualisierungs- und Zeichenphase auf. Dabei kann man jedoch nicht davon ausgehen, dass immer zunächst update() und dann draw() aufgerufen wird, weshalb man seinen Code auch logisch so trennen sollte, dass bei andersartiger Aufrufreihenfolge keine Fehler auftreten. Wenn die FPS konstant erreicht werden, bleibt diese Aufrufreihenfolge erhalten, wenn sie allerdings abfallen, kann das Framework die Aufrufe von update() und draw() dynamisch anpassen.
Game Components
Game Components sind eine Entwicklung, die sich in der Spieleentwicklung anscheinend in den letzten Jahren verbreitet hat. Früher wurden Objekte hierarchisch organisiert, was zu seltsamen Vererbungsbäumen führte, wenn Objekte nicht eindeutig zugeordnet werden konnten. Dann musste entweder Code doppelt geschrieben werden (da er z.B. im MoveableObject-Ast schon vorhanden wäre, das Objekt aber einer anderen Kategorie angehört) oder die Kategorien wurden immer weiter nach oben geschoben, wodurch alle Kinder evtl. unnötigen Code mit sich tragen.
Um dies zu umgehen, wurden Game Components entwickelt. Das sind im Grunde einzelne Module, die sich nur um einzeln definierte Aufgaben können. Letztlich können die Module, ähnlich wie in anderen modularen Strukturen, auch miteinander kommunizieren, aber die Zuständigkeiten sind zunächst getrennt.
Gerade als Anfänger stellt man sich häufig solche Fragen:
- Wer kümmert sich um die Kollisionserkennung?
- Wer verwaltet die physikalischen Eigenschaften z.B. bei einem Ballwurf?
- Wer ist für die Tonausgabe zuständig?
- Wer nimmt die Eingabe entgegen?
- …
Der erste Anlaufspunkt ist dann die update()-Methode der Hauptklasse (in XNA Game1), welche schließlich hoffnungslos überladen wird.
Hier schaffen die in XNA implementierten Game Components Abhilfe. So kann man beispielsweise für obige Fragen folgende Komponenten erstellen:
- CollisionComponent
- PhysicsComponent
- AudioComponent
- InputComponent
Dies erleichtert es einem einerseits die Trennung der einzelnen Komponenten, hält die Klassen klein und sauber, und andererseits kann man den Code leicht in einem neuen Spiel wiederverwenden. Nun fragt man sich aber: “Es mag ja sein, dass ein Rollenspiel und ein Actionshooter die gleiche Physik haben, aber braucht mein Spiel nicht eine andere Eingabemethode? Das Rollenspiel steuert sich vielleicht vollständig mit der Tastatur und der Shooter benutzt die Maus.”
Services
Um dieses Design noch ein bisschen zu erweitern und obiges Problem zu beheben, kommen die Services zum Einsatz. Dies ist eine Schnittstelle, welche Klassen (z.B. Spielekomponenten) bereitstellen kann, die auf einer Schnittstelle beruhen und diese implementieren. Wenn man dies mit Game Components kombiniert, kann man obiges Problem lösen.
In diesem Fall würde man folgende Klassen und Schnittstellen anlegen:
- interface InputComponent
- class KeyboardComponent: Spielen eines Spiels nur mit der Tastatur
- class MouseKeyboardComponent: Spielen eines Spiels mit Maus und Tastatur
- class GamepadComponent: Spielen eines Spiels mit Gamepad
Wie man sieht, ist das Spiel nun transportabel auf verschiedene Plattformen und die Eingabekomponenten können auch flexibel in weitere Spiele übertragen werden, die vielleicht ein bisschen anders gesteuert werden sollen.
Die Entscheidung, welches Eingabegerät genutzt werden soll, liegt dann nur noch an einer einzigen Stelle und kann sogar zur Laufzeit geändert werden. Lassen wir doch einfach den Benutzer wählen, ob er seinen Shooter mit Maus und Tastatur oder lieber mit einem Gamepad spielen will!
Der Programmierer muss sich dann nicht mehr darum kümmern, an jeder Stelle alle Abfragen nach Gamepad und Tastatur/Maus zu überprüfen, was fehleranfälliger wäre. Stattdessen gibt es eine Klasse, die sich nur um Maus/Tastatur kümmert und eine für das Gamepad. Durch die definierte Schnittstelle ist sichergestellt, dass alles implementiert sein muss, wodurch nichts vergessen werden kann.
Wir könnten uns jetzt sogar überlegen, eine AI an die Eingabe zu koppeln und das Spiel dann von der Maschine steuern zu lassen. So ist alles schön austauschbar.
Links
Einen hochinteressanten Überblick über die beiden Entwurfsmuster gibt es unter:
I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.