Inhaltsverzeichnis

Impressum

Danksagungen

Teil I: Einführung

Kapitel 1: Architektur, Performance und Spiele

1.1 Was ist Softwarearchitektur?

1.1.1 Was zeichnet eine gute Softwarearchitektur aus?

1.1.2 Wie nimmt man Änderungen vor?

1.1.3 Inwiefern hilft eine Entkopplung?

1.2 Zu welchem Preis?

1.3 Performance und Geschwindigkeit

1.4 Das Gute an schlechtem Code

1.5 Ein ausgewogenes Verhältnis finden

1.6 Einfachheit

1.7 Fang endlich an!

Teil II: Design Patterns neu überdacht

Kapitel 2: Command (Befehl)

2.1 Eingabekonfiguration​

2.2 Regieanweisungen

2.3 Rückgängig und Wiederholen

2.4 Klassen ohne Funktionen?

2.5 Siehe auch ...

Kapitel 3: Flyweight (Fliegengewicht)

3.1 Den Wald vor lauter Bäumen nicht sehen

3.2 Tausend Instanzen

3.3 Das Flyweight-Pattern​​

3.4 Ein Ort, um Wurzeln zu schlagen

3.5 Und die Performance?

3.6 Siehe auch ...

Kapitel 4: Observer (Beobachter)

4.1 Erzielte Leistungen

4.2 Funktionsweise

4.2.1 Der Observer

4.2.2 Das Subjekt​

4.2.3 Beobachtung der Physik-Engine

4.3 »Das ist zu langsam!«

4.3.1 Oder ist es doch zu schnell?

4.4 »Zu viele dynamische Allokationen«

4.4.1 Verkettete Observer

4.4.2 Ein Pool von Listenknoten

4.5 Verbleibende Schwierigkeiten

4.5.1 Subjekte und Observer löschen

4.5.2 Keine Sorge, der Garbage Collector erledigt das schon

4.5.3 Was geht hier vor?

4.6 Heutige Observer

4.7 Zukünftige Observer

Kapitel 5: Prototype (Prototyp)

5.1 Das Design Pattern Prototype

5.1.1 Wie gut funktioniert es?

5.1.2 Spawn-Funktionen

5.1.3 Templates

5.1.4 First-Class-Typen

5.2 Eine auf Prototypen beruhende Sprache

5.2.1 Self​

5.2.2 Wie ist es gelaufen?

5.2.3 Was ist mit JavaScript?​

5.3 Prototypen zur Datenmodellierung

Kapitel 6: Singleton

6.1 Das Singleton-Pattern

6.1.1 Beschränkung einer Klasse auf eine Instanz

6.1.2 Bereitstellung eines globalen Zugriffspunkts

6.2 Gründe für die Verwendung

6.3 Gründe, die Verwendung zu bereuen

6.3.1 Singletons sind globale Variablen

6.3.2 Das Pattern löst zwei Probleme, selbst wenn es nur eins gibt

6.3.3 Die späte Initialisierung entzieht Ihnen die Kontrolle

6.4 Verzicht auf Singletons

6.4.1 Wird die Klasse überhaupt benötigt?

6.4.2 Nur eine Instanz einer Klasse

6.4.3 Bequemer Zugriff auf eine Instanz

6.5 Was bleibt dem Singleton?

Kapitel 7: State (Zustand)

7.1 Altbekanntes

7.2 Zustandsautomaten erledigen das

7.3 Enumerationen und Switch-Anweisungen

7.4 Das State-Pattern

7.4.1 Das Interface für den Zustand

7.4.2 Klassen für alle Zustände

7.4.3 An den Zustand delegieren

7.5 Wo sind die Zustandsobjekte?

7.5.1 Statische Zustände

7.5.2 Instanziierte Zustandsobjekte​

7.6 Eintritts- und Austrittsaktionen

7.7 Wo ist der Haken?

7.8 Nebenläufige Zustandsautomaten

7.9 Hierarchische Zustandsautomaten

7.10 Kellerautomaten

7.11 Wie nützlich sind sie?

Teil III: Sequenzierungsmuster (Sequencing Patterns)

Kapitel 8: Double Buffer (Doppelter Buffer)

8.1 Motivation

8.1.1 Computergrafik kurz und bündig

8.1.2 Erster Akt, erste Szene

8.1.3 Zurück zur Grafik

8.2 Das Pattern

8.3 Anwendbarkeit

8.4 Konsequenzen

8.4.1 Der Austausch selbst kostet Zeit

8.4.2 Zwei Framebuffer belegen mehr Arbeitsspeicher

8.5 Beispielcode

8.5.1 Nicht nur Grafik

8.5.2 Künstliche Unintelligenz

8.5.3 Gebufferte Ohrfeigen

8.6 Designentscheidungen

8.6.1 Wie werden die Buffer ausgetauscht?​

8.6.2 Wie fein ist der Buffer untergliedert?

8.7 Siehe auch ...

Kapitel 9: Game Loop (Hauptschleife)

9.1 Motivation

9.1.1 Interview mit einer CPU

9.1.2 Ereignisschleifen

9.1.3 Eine aus dem Takt geratene Welt

9.1.4 Sekunden pro Sekunde

9.2 Das Pattern

9.3 Anwendbarkeit

9.4 Konsequenzen

9.4.1 Abstimmung mit der Ereignisschleife des Betriebssystems

9.5 Beispielcode

9.5.1 Die Beine in die Hand nehmen

9.5.2 Ein kleines Nickerchen

9.5.3 Ein kleiner und ein großer Schritt

9.5.4 Aufholjagd

9.5.5 In der Mitte hängen geblieben

9.6 Designentscheidungen

9.6.1 Stammt die Game Loop aus Ihrer Feder oder benutzen Sie die der Plattform?

9.6.2 Wie handhaben Sie die Leistungsaufnahme?​

9.6.3 Wie steuern Sie die Spielgeschwindigkeit?​

9.7 Siehe auch ...

Kapitel 10: Update Method (Aktualisierungsmethode)

10.1 Motivation

10.2 Das Pattern

10.3 Anwendbarkeit

10.4 Konsequenzen

10.4.1 Verkomplizierung durch Aufteilen des Codes in einzelne Frames

10.4.2 Der Zustand muss gespeichert werden, um im nächsten Frame fortfahren zu können

10.4.3 Alle Objekte simulieren jeden Frame, aber nicht wirklich exakt gleichzeitig

10.4.4 Obacht bei der Modifizierung der Objektliste während der Aktualisierung

10.5 Beispielcode

10.5.1 Entity-Unterklassen?

10.5.2 Entities definieren

10.5.3 Zeitablauf

10.6 Designentscheidungen

10.6.1 Zu welcher Klasse gehört die update()-Methode?

10.6.2 Wie werden inaktive Objekte gehandhabt?​

10.7 Siehe auch ...

Teil IV: Verhaltensmuster (Behavioral Patterns)

Kapitel 11: Bytecode​

11.1 Motivation

11.1.1 Wettkampf der Zaubersprüche

11.1.2 Daten > Code

11.1.3 Das Interpreter-Pattern​

11.1.4 Faktisch Maschinencode

11.2 Das Pattern

11.3 Anwendbarkeit

11.4 Konsequenzen

11.4.1 Befehlsformat

11.4.2 Fehlender Debugger

11.5 Beispielcode

11.5.1 Eine zauberhafte API

11.5.2 Ein bezaubernder Befehlssatz​

11.5.3 Eine Stackmaschine

11.5.4 Verhalten = Komposition

11.5.5 Eine virtuelle Maschine​

11.5.6 Hexerwerkzeuge

11.6 Designentscheidungen

11.6.1 Wie greifen Befehle auf den Stack zu?

11.6.2 Welche Befehle gibt es?

11.6.3 Wie werden Werte repräsentiert?

11.6.4 Wie wird der Bytecode erzeugt?

11.7 Siehe auch ...

Kapitel 12: Subclass Sandbox (Unterklassen-Sandbox)​​

12.1 Motivation

12.2 Das Pattern

12.3 Anwendbarkeit

12.4 Konsequenzen

12.5 Beispielcode

12.6 Designentscheidungen

12.6.1 Welche Operationen sollen bereitgestellt werden?

12.6.2 Sollen Methoden direkt oder durch Objekte, die sie enthalten, bereitgestellt werden?

12.6.3 Wie gelangt die Basisklasse an die benötigten Zustände?

12.7 Siehe auch ...

Kapitel 13: Type Object (Typ-Objekt)​

13.1 Motivation

13.1.1 Die typische OOP-Lösung

13.1.2 Eine Klasse für eine Klasse

13.2 Das Pattern

13.3 Anwendbarkeit

13.4 Konsequenzen

13.4.1 Typ-Objekte müssen manuell gehandhabt werden

13.4.2 Die Definition des Verhaltens der verschiedenen Typen ist schwieriger

13.5 Beispielcode

13.5.1 Typartiges Verhalten von Typ-Objekten: Konstruktoren​

13.5.2 Gemeinsame Nutzung von Daten durch Vererbung​

13.6 Designentscheidungen

13.6.1 Ist das Typ-Objekt gekapselt oder zugänglich?​

13.6.2 Wie werden Typ-Objekte erzeugt?​

13.6.3 Kann sich der Typ ändern?​

13.6.4 Welche Formen der Vererbung werden unterstützt?

13.7 Siehe auch ...

Teil V: Entkopplungsmuster (Decoupling Patterns)

Kapitel 14: Component (Komponente)

14.1 Motivation

14.1.1 Der Gordische Knoten

14.1.2 Den Knoten durchschlagen

14.1.3 Unerledigtes

14.1.4 Wiederverwendung

14.2 Das Pattern

14.3 Anwendbarkeit

14.4 Konsequenzen

14.5 Beispielcode

14.5.1 Eine monolithische Klasse

14.5.2 Abspalten eines Bereichs

14.5.3 Abspalten der übrigen Bereiche

14.5.4 Robo-Bjørn

14.5.5 Ganz ohne Bjørn?

14.6 Designentscheidungen

14.6.1 Wie gelangt ein Objekt an seine Komponenten?

14.6.2 Wie kommunizieren die Komponenten untereinander?​

14.7 Siehe auch ...

Kapitel 15: Event Queue (Ereigniswarteschlange)

15.1 Motivation

15.1.1 Ereignisschleife der grafischen Benutzeroberfläche

15.1.2 Zentrale Ereignissammlung

15.1.3 Wie bitte?

15.2 Das Pattern

15.3 Anwendbarkeit

15.4 Konsequenzen

15.4.1 Eine zentrale Ereigniswarteschlange ist eine globale Variable

15.4.2 Den Boden unter den Füßen verlieren

15.4.3 Steckenbleiben in Rückkopplungsschleifen

15.5 Beispielcode

15.5.1 Ein Ring-Buffer

15.5.2 Anfragen zusammenfassen

15.5.3 Threads​

15.6 Designentscheidungen

15.6.1 Was soll in die Warteschlange aufgenommen werden?​

15.6.2 Wer darf lesend auf die Warteschlange zugreife​n?

15.6.3 Wer darf schreibend auf die Warteschlange zugreifen?

15.6.4 Wie lang ist die Lebensdauer der Objekte in der Warteschlange?

15.7 Siehe auch ...

Kapitel 16: Service Locator (Dienstlokalisierung)

16.1 Motivation

16.2 Das Pattern

16.3 Anwendbarkeit

16.4 Konsequenzen

16.4.1 Der Dienst muss auch tatsächlich lokalisiert werden können

16.4.2 Dem Dienst ist nicht bekannt, wer ihn nutzt

16.5 Beispielcode

16.5.1 Der Dienst​

16.5.2 Der Dienstanbieter

16.5.3 Ein einfacher Service Locator

16.5.4 Ein leerer Dienst

16.5.5 Protokollierender Dekorierer

16.6 Designentscheidungen

16.6.1 Wie wird der Dienst lokalisiert?

16.6.2 Was geschieht, wenn die Lokalisierung des Dienstes scheitert?

16.6.3 Wer darf auf den Dienst zugreifen?

16.7 Siehe auch ...

Teil VI: Optimierungsmuster (Optimization Patterns)

Kapitel 17: Data Locality (Datenlokalität)

17.1 Motivation

17.1.1 Ein Datenlager

17.1.2 Eine Palette für die CPU

17.1.3 Daten = Performance?

17.2 Das Pattern

17.3 Anwendbarkeit

17.4 Konsequenzen

17.5 Beispielcode

17.5.1 Aneinandergereihte Arrays​

17.5.2 Gebündelte Daten

17.5.3 Hot/Cold Splitting

17.6 Designentscheidungen

17.6.1 Wie wird Polymorphismus gehandhabt?

17.6.2 Wie werden Spielobjekte definiert?

17.7 Siehe auch ...

Kapitel 18: Dirty Flag (Veraltet-Flag)

18.1 Motivation

18.1.1 Lokale Koordinaten und Weltkoordinaten

18.1.2 Gecachete Weltkoordinaten

18.1.3 Verzögerte Berechnung

18.2 Das Pattern

18.3 Anwendbarkeit

18.4 Konsequenzen

18.4.1 Nicht zu lange verzögern

18.4.2 Das Flag bei jedem Zustandswechsel ändern

18.4.3 Vorherige abgeleitete Daten verbleiben im Speicher

18.5 Beispielcode

18.5.1 Nicht-optimierte Traversierung

18.5.2 Let’s Get Dirty

18.6 Designentscheidungen

18.6.1 Wann wird das Dirty Flag gelöscht?

18.6.2 Wie feingranular ist Ihr »Dirty-Tracking«?

18.7 Siehe auch ...

Kapitel 19: Object Pool (Objektpool)​

19.1 Motivation

19.1.1 Der Fluch der Fragmentierung

19.1.2 Das Beste beider Welten

19.2 Das Pattern

19.3 Anwendbarkeit

19.4 Konsequenzen

19.4.1 Der Pool verschwendet möglicherweise Speicherplatz für ungenutzte Objekte

19.4.2 Es steht nur eine feste Anzahl von Objekten zur Verfügung

19.4.3 Die Objekte sind von fester Größe

19.4.4 Wiederverwendete Objekte werden nicht automatisch zurückgesetzt

19.4.5 Unbenutzte Objekte verbleiben im Arbeitsspeicher

19.5 Beispielcode

19.5.1 Eine kostenlose Liste

19.6 Designentscheidungen

19.6.1 Sind Objekte an den Pool gekoppelt?

19.6.2 Wer ist für die Initialisierung der wiederverwendeten Objekte verantwortlich?

19.7 Siehe auch ...

Kapitel 20: Spatial Partition (Räumliche Aufteilung)​​

20.1 Motivation

20.1.1 Kampfeinheiten auf dem Schlachtfeld

20.1.2 Schlachtreihen zeichnen

20.2 Das Pattern

20.3 Anwendbarkeit

20.4 Konsequenzen

20.5 Beispielcode

20.5.1 Ein Bogen Millimeterpapier

20.5.2 Ein Gitternetz verketteter Einheiten

20.5.3 Betreten des Schlachtfeldes

20.5.4 Klirrende Schwerter

20.5.5 Vormarschieren

20.5.6 Um Armeslänge

20.6 Designentscheidungen

20.6.1 Ist die Aufteilung hierarchisch oder gleichmäßig?

20.6.2 Hängt die Zellengröße von der Verteilung der Objekte ab?

20.6.3 Werden die Objekte nur in den Zellen gespeichert?

20.7 Siehe auch ...

Design Patterns für die Spieleprogrammierung

Robert Nystrom

Übersetzung aus dem Amerikanischen von Knut Lorenzen

Impressum

Bibliografische Information der Deutschen Nationalbibliothek

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-nb.de> abrufbar.

ISBN 978-3-95845-092-9

1. Auflage 2015

www.mitp.de

E-Mail: mitp-verlag@sigloch.de

Telefon: +49 7953 / 7189 - 079

Telefax: +49 7953 / 7189 - 082

© 2015 mitp Verlags GmbH & Co. KG

Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.

Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Authorized translation from the English language edition, entitled »Game Programming Patterns«, 978-0-9905829-0-8 by Nystrom, Robert, published by Genever Benning, Copyright © 2014 Robert Nystrom. All rights reserved.

Lektorat: Sabine Schulz

Sprachkorrektorat: Maren Feilen

Coverbild: Robert Nystrom

electronic publication: III-satz, Husby, www.drei-satz.de

Dieses Ebook verwendet das ePub-Format und ist optimiert für die Nutzung mit dem iBooks-reader auf dem iPad von Apple. Bei der Verwendung anderer Reader kann es zu Darstellungsproblemen kommen.

Der Verlag räumt Ihnen mit dem Kauf des ebooks das Recht ein, die Inhalte im Rahmen des geltenden Urheberrechts zu nutzen. Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheherrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und Einspeicherung und Verarbeitung in elektronischen Systemen.

Der Verlag schützt seine ebooks vor Missbrauch des Urheberrechts durch ein digitales Rechtemanagement. Bei Kauf im Webshop des Verlages werden die ebooks mit einem nicht sichtbaren digitalen Wasserzeichen individuell pro Nutzer signiert.

Bei Kauf in anderen ebook-Webshops erfolgt die Signatur durch die Shopbetreiber. Angaben zu diesem DRM finden Sie auf den Seiten der jeweiligen Anbieter.

Für Megan, für Vertrauen und Zeit,
den beiden wichtigsten Bestandteilen.

Danksagungen

Ich habe mir sagen lassen, dass nur andere Autoren einschätzen können, was zum Schreiben eines Buches alles dazugehört. Allerdings gibt es da noch ein anderes Völkchen, das genau weiß, was für eine Belastung das ist – nämlich die Unglücklichen, die in einer Beziehung zum Autor stehen. Ich habe mir die Zeit zum Schreiben mühsam zusammenkratzen müssen, denn der Alltag ist für meine Frau Megan und mich eigentlich schon anstrengend genug. Abzuwaschen und die Kinder zu baden, ist zwar nicht gerade das, was man unter »Schreiben« versteht, aber ohne ihre Hilfe gäbe es dieses Buch nicht.

Als ich mit diesem Projekt begann, war ich bei Electronic Arts als Programmierer tätig. Ich denke, das Unternehmen wusste nicht so recht, was davon zu halten war, und so bin ich Michael Malone, Olivier Nallet und Richard Wifall für ihre Unterstützung und ihr detailliertes, aufschlussreiches Feedback zu den ersten paar Kapiteln umso dankbarer.

Nachdem etwa die Hälfte des Buches erledigt war, entschloss ich mich, auf die Veröffentlichung durch einen traditionellen Verlag zu verzichten. Mir war durchaus bewusst, dass mir damit auch die fachliche Unterstützung eines Lektorats fehlen würde, andererseits hatten mir jedoch schon Dutzende Leser gemailt, die keinen Zweifel daran ließen, in welche Richtung das Buch ihrer Meinung nach gehen sollte. Auch auf das Korrektorat musste ich verzichten, dafür hatte ich aber bereits mehr als 250 Fehlerberichte erhalten, die mir dabei halfen, den Text auf Vordermann zu bringen. Ich hatte es schnell aufgegeben, mich beim Schreiben an einen Zeitplan zu halten – doch wenn einem die Leser nach der Fertigstellung der einzelnen Kapitel auf die Schulter klopfen, ist das Ansporn genug!

Ich musste zwar auf ein verlagsseitiges Lektorat verzichten, nicht aber auf gute Korrekturleser. Lauren Briese war stets zur Stelle, wenn ich Hilfe brauchte, und hat ausgezeichnete Arbeitet geleistet.

Mein besonderer Dank gilt Colm Sloan, der jedes einzelne Kapitel zweimal überprüft hat und mir haufenweise fabelhaftes Feedback lieferte – nur weil er so ein großzügiger Mensch ist. Du hast ein Bier (oder zwanzig) bei mir gut!

Diese Art der Veröffentlichung wird als »Selbstverlag« oder »Self-Publishing« bezeichnet, obwohl der Begriff »Crowd-Publishing« der Wahrheit näher kommt. Schreiben kann man zwar auch alleine, ich selbst war allerdings niemals alleine. Sogar als ich die Arbeit am Buch zwei Jahre ruhen ließ, wurde ich weiterhin ermutigt, es fertigzustellen. Ohne all die Leute (Dutzende!), die mich immer wieder daran erinnerten, dass sie auf weitere Kapitel warteten, hätte ich das Buch wohl nicht zu Ende gebracht.

Ich möchte allen, die E-Mails geschrieben, Kommentare gesendet, getwittert, Fehler berichtet, Freunden von diesem Buch erzählt oder irgendwie Verbindung mit mir aufgenommen haben, sagen: Ich bin euch wirklich von ganzem Herzen dankbar. Dieses Buch fertigzustellen, gehörte zu meinen größten Wünschen. Und ihr habt dafür gesorgt, dass ich es geschafft habe. Danke!

Bob Nystrom, 6. September 2014

Teil I:Einführung

In diesem Teil:

Kapitel 1

Architektur, Performance und Spiele

Im fünften Schuljahr standen in unserem Klassenzimmer ein paar ziemlich ramponierte TRS-80-Computer herum, die meine Freunde und ich benutzen durften. Ein Lehrer, der unsere Begeisterung wecken wollte, gab uns einen Stapel Ausdrucke einiger einfacher BASIC-Programme, mit denen wir experimentieren konnten.

Die Kassettenlaufwerke der Rechner waren defekt, d.h., wenn wir Code ausführen wollten, mussten wir ihn immer sehr sorgfältig komplett neu eingeben. Das führte dazu, dass wir bevorzugt auf Programme zurückgriffen, die nur einige wenige Zeilen lang waren:

10 PRINT "BOBBY IST EIN RADIKALER!!!"
20 GOTO 10

Vielleicht wird es ja auf wundersame Weise wahr, wenn der Computer es nur oft genug ausgibt ...

Trotzdem war das Ganze Glückssache. Wir hatten keine Ahnung von Programmierung, und schon der kleinste Syntaxfehler war für uns unergründlich. Wenn ein Programm nicht funktionierte, und das kam des Öfteren vor, fingen wir einfach von vorne an.

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!