Spieleprogrammierung
Aus Wikibooks
| Dieser Buchabschnitt benötigt eine Wikifizierung. Hilfe ist erwünscht! Textbezogenen Fragen und Anmerkungen können auf der Diskussionsseite besprochen werden. |
Siehe Diskussionsseite. --Gert alias "Der Doc" 19:58, 26. Dez. 2006 (CET)
Dieses Buch steht im Regal Programmierung.
[Bearbeiten] Zusammenfassung des Projekts
- Zielgruppe:
Programmierer
- Lernziele:
Programmierung eines eigenen 3D-Spiels mit allem was dazugehört.
- Sind Co-Autoren gegenwärtig erwünscht?
Co-Autoren sind erwünscht. Ich schlage inzwischen Geld aus dem Programmieren, wobei ich es immer für mich als Hobby, zum Spaß gemacht habe. Es hat alles sehr lange gedauert und ich habe mich immer gefreut, wenn in einem Tutorial der Satz kam: "Um anderen diesen schweren Weg zu ersparen, hab ich dieses Tutorial geschrieben." Wenn du auch noch etwas hinzufügen kannst: Bitteschön. Wissen für alle.
- Richtlinien für Co-Autoren:
Ein Co-Autor sollte eine gute Rechtschreibung und halbwegs verständliche Grammatik haben. Die Leute sollten geduzt werden, sind doch hier unter uns :).
- Projektumfang und Abgrenzung zu anderen Wikibooks:
Dieses Buch soll nicht zu einem OpenGL-, wxWidgets-, oder C++ - Guide werden, sondern lediglich dort wo es benötigt wird Beispiele in diesen Disziplinen zeigen.
- Themenbeschreibung:
Programmierung: - einer 3D-Engine - eines 3D-Editors - eines Spiels aus diesen Elementen
Inhaltsverzeichnis |
[Bearbeiten] Einführung
Zweck des Buches soll es sein, einen Guide zur Spieleprogrammierung zu machen. Ich kann dazu nur einen Beitrag leisten und der lautet: Programmieren unter C++ unter Zuhilfenahme von wxWidgets und OpenGL in DevCpp. Das klingt ziemlich speziell, aber es ist nach meinen bisherigen Erfahrungen der einfachste kostenlose Weg, ein 3D-Spiel zu schreiben.
Es wäre echt super, wenn jemand Guides zur Programmierung in anderen Programmiersprachen/umgebungen schreiben könnte. Dies ist mein allererstes Tutorial, also bitte nehmt mir meine Fehler nicht so krumm. Ich werde mich bemühen, es ständig zu verbessern und weiterzuentwickeln.
Im Übrigen bin ich neu bei wxWidgets, ich habe bisher unter MSVC mit OpenGL kleinere 3D-Programme und eine 3D-Engine geschrieben, bei der leider nie ein fertiges Spiel herauskam. Das Ganze scheiterte an der wahnsinnig umständlichen Programmierung eines Editors ohne MFC. MFC selbst war mir dann wieder zu sehr auf Windows spezialisiert, da die resultierende 3D-Engine irgendwann auch für Linux verfügbar sein soll. Da bleibt nur eins: wxWidgets lernen. Alle machen mit und lernen voneinander :).
[Bearbeiten] Programmierung mit OpenGL, wxWidgets und DevCpp
[Bearbeiten] Voraussetzungen
Wichtigste Voraussetzung ist, dass ihr echt Bock habt, ein Spiel zu schreiben. Klar, am Anfang hat man einen Haufen Hirngespinnste, die Jahre von der Umsetzung entfernt sind... was euch jedoch nicht daran hindern sollte, sofort mit dem Lernen anzufangen.
Welches Vorwissen muss ich mitbringen, um ein eigenes Spiel zu schreiben?
Ganz einfach: Für dieses Tutorial müsst ihr Vorkenntnisse in C++ und OpenGl haben. Falls ihr mit diesen beiden nichts oder nur wenig anzufangen wisst, dann googelt mal 'ne Runde. Ihr werdet feststellen, dass es massig gute Tutorials zu diesen Themen gibt.
Wo zum Beispiel kann man sich übersichtlich oder umfassend informieren ?
C++
- C++ auf Wikibooks ... (de)
- ... sowie das englische, umfangreichere Pendant (en)
- C++ Kurs einer Privatperson (de)
- OpenBook von GalileoComputing.de zu C++ (de)
- Ein übersichtlich strukturierter C++ Kurs (de)
wxWidgets
- Tutorials zum Thema wxWidgets (en)
- "Cross-Platform GUI Programming with wxWidgets" als PDF (en)
- Das wxWiki (en)
Natürlich kann man sich die mitgelieferte Dokumentation und insbesondere die Klassenbibliothek als Hilfe-Datei anschauen.
OpenGL
- Alles über OpenGL von NeHE ... (en)
- ... und die deutsche Übersetzung (de)
- Grosses deutsches Wiki zu OpenGL (de)
[Bearbeiten] Vorbereitungen
Jetzt gehts los, mit einem Schritt-für-Schritt-Guide zur ersten eigenen 3D-Engine mit OpenGL, wxWidgets und DevCpp. Dazu sind jedoch erstmal einige Vorbereitungen nötig.
Zuerst lade man sich DevCpp von Bloodshed-Software runter.
[Bearbeiten] 3D-Engine
[Bearbeiten] Was ist eine "Engine"?
Wer programmiert kann hoffentlich Englisch und weiß: "Engine" heißt zu deutsch "Motor". Genau das ist es. Eine Engine ist ein Programm, das eine Sammlung von Funktionen enthält, die ein Spiel "zum Laufen" bringen. Typische Aufgaben sind dabei:
- Bereitstellung von Grafikfunktionen, z.B.: "Zeichne eine Kugel mit dem Radius x an der Stelle (5, 2, -3)"
- Bereitstellung von Soundfunktionen insbesondere von Methoden, die wxWidgets nicht bereitstellt, z.B. über PortAudio oder OpenAL, nicht-trivial ist dabei der Wunsch nach Surround-Sound
- Inputhandling, also Verarbeitung von Tastatur-, Maus-, oder ganz allgemein, Eingaben
- Nicht zuletzt ist Wissen um Vektorrechnung im Rahmen der Linearen Algebra von Vorteil
Das sind die grundlegenden Merkmale einer Spiele-Engine. Natürlich kann sie beliebige weitere Funktionen übernehmen und meistens muss sie das auch. Beispiele: Netzwerkzugriff, Menus, "KI" und Dateizugriffe (Karten und Modelle lesen, Speicherstände erstellen/laden, usw..) .
Aufgrund der ständig wachsenden Komplexität der einzelnen Aufgabengebiete, beinhaltet eine moderne Engine wiederrum viele "Unterengines", also Einzelteile. Das sind spezialisierte Bibliotheken. OpenGL ist eine davon, sie ermöglicht den Zugriff auf die Grafikhardware. Vielleicht kann man sich eine 3D-Engine wie folgt vorstellen: Sie ist eine Fabrik, die ein Auto produziert. Die Fabrik hat viele Zulieferer, die einzelne Teile abliefern. Die Arbeiter und Maschinen wissen nicht, wie alle Einzelteile im Detail funktionieren, aber sie wissen genau, wie sie sie zusammenfügen müssen und sind dafür verantwortlich, ein harmonisches Gesamtbild, ein Produkt daraus entstehen zu lassen.
Eine 3D-Engine ist durch diese vielen Einzelteile vielmehr ein "Wrapper", also eine "Hülle", in der alle Einzelteile zu einem Gesamtkunstwerk vereint werden. Natürlich kann man auch diese Einzelteile selbst schreiben, was, Hardwarezugriff ausgenommen, keine Nachteile bringen muss, aber das behandle ich hier nicht. Der Begriff "Wrapper" hat hier nur Beispielcharakter, denn er ist in der Fachsprache der Programmierung schon anderweitig belegt und sollte hier nur der Veranschaulichung dienen. Also: Eine Engine ist eine Engine, basta.
Es ist sinnvoll, die Engine von Anfang an als Zusammenspiel einer DLL und einer ausführbaren Datei, sprich ".exe", zu konstruieren. Will man später Teile der Engine wiederverwenden, spätestens wenn man den dazugehörigen 3D-Editor schreibt, spart man sich somit viel Aufwand.
[Bearbeiten] Selber bauen?
Bevor man ein Spiel schreibt, sollte man sich folgendes überlegen:
Was sind meine Ziele?
Wenn die Antwort lautet: "Ich habe Spaß am Programmieren, Tüfteln und Selbst-Herausfinden." ist man vermutlich hochmotiviert und kann beruhigt anfangen, eine eigene 3D-Engine zu bauen. Die Vorteile einer eigenen 3D-Engine liegen auf der Hand:
- Aneignen vieler neuer Fähigkeiten (wahre Erkenntnis kommt von innen)
- gutes Verständnis für die Funktionsweise der meisten Teile einer 3D-Engine
- Anpassung des Programms an die ganz persönlichen Wünsche und Bedürfnisse
Für alle die diesen Weg beschreiten wollen, wird dieses Buch gemacht. Einige werden aber auch schon die Nachteile erahnen:
- großer Zeitaufwand, unter Umständen sehr lange Entwicklungszeit
- Frustration durch (scheinbar!) unlösbare Probleme / mangelnde Hilfe
- "Verzettelung" in Details
Zum letzten Punkt: Wer nicht in einem professionellen oder semi-professionellen Team arbeitet, also die allermeisten Hobby-Programmierer, wird sich reichlich schwer damit tun, einen festen und genauen Plan mit Timeline und bestimmten Vorstellungen vom Programm, hinsichtlich Aufbau und Schnittstellen, zu machen. Dieser Plan wird "Designscript" genannt. Um vorher genau zu wissen, wie ein Programm aussehen soll, ist ein sehr genaues und tiefgehendes Verständnis für die anstehnde Materie und vor allem für die Programmiersprache von nöten. Trotzdem ist es natürlich sinnvoll, einen groben Plan zu schreiben, was zur Engine gehören soll, was nicht und was die einzelnen Teile in etwa einmal leisten sollen. Meistens hat man ja irgendeine Grundidee, weshalb man überhaupt ein Spiel machen möchte. Man hat sich zum Beispiel mit einem oder mehreren Freunden eine coole Story zusammengesponnen oder man ist es endgültig leid, sich immer nur über das zu ärgern, was wieder nicht(!) in den neuen Teil des Lieblingsspiels eingeflossen ist. Also: Erstmal 'nen Plan machen und sich fragen wo man hin möchte, sonst landet man irgendwann in einer Sackgasse.
Kommen wir zu der anderen Gruppe von Lesern, die jetzt sagt: "So ein Quatsch, ich will meine Spielideen umsetzen. Sofort!". Für solche Zwecke gibt es bereits gute Freeware- und OpenSource-Engines, die teilweise alles übersteigen, was man alleine jemals zustande bringen wird. Die momentan bekannteste OpenSource-3D-Engine ist wohl die OGRE. Das ist eine frei verfügbare OpenSource-Engine. OGRE selbst ist ein Ackronym für "Object Oriented Graphics Rendering Engine". Sie ist von einem ganzen Team geschrieben und kann mit so mancher kommerzieller Engine mithalten. Beherrscht man einmal diese Engine, ist ein Spiel schneller geschrieben. Wem das immer noch zu langsam und umständlich ist, der schaut sich mal bei Google oder sonstwo nach einer Software zum Spiele bauen um...
[Bearbeiten] Selber bauen!
Beim Bau der 3D-Engine kommt man wahrscheinlich nur bis zu dem Punkt, an dem man ein paar Dreiecke rendert und vielleicht noch mit ein paar Partikeln oder einer automatisch generierten Landschaft herumspielt, aber dann ist Ende Gelände. Es ist schon lustig und motivierend mal ein paar Würfel "hart" reinzuprogrammieren, so wie das für manche Demos und Tutorials gemacht wird. Das sollte jetzt aber nicht Überhand nehmen, wie zum Beispiel die Programmierung! einer ganzen Map :O . Das war das was ich am Anfang mit "Verzettelung in Details" meinte ;).
Ansonsten sollte man sich jetzt zunächst mal um den Bau eines Editors, bzw um die Anbindung an einen vorhandenen kümmern, damit man auch irgendwas im angehenden Spiel hat, mit dem man "arbeiten kann". Anbindung heißt zum Beispiel: Ihr baut mit GTK-Radiant und ladet MD2-Modelle in die Engine und schreibt ein Script, das dem sagt, wie für euer Spiel abgespeichert wird usw.. Das soll möglich sein, darüber hab' ich mir bisher kaum Gedanken gemacht. Sonst baut ihr den Editor selbst, siehe unten, "3D-Editor".
[Bearbeiten] 3D-Editor
[Bearbeiten] Warum ein eigener Editor?
Beim Schreiben eines eigenen 3D-Editors lernt man mit Sicherheit eine Menge dazu und außerdem macht es einfach Spaß. Natürlich könnte man auch einen vorhandenen Editor nehmen, aber dann müsste es die Überschrift "3D-Editor" hier ja auch nicht geben ;).
Zum dem Editor, den wir hier bauen, wäre noch zu sagen, dass es ein externer Editor wird. Er läuft also außerhalb des Spiels als autonomes Programm und wird extra gestartet. Das ist auf den ersten Blick selbstverständlich und praktisch. Schließlich kann man einen externen Editor auch besser updaten, als einen spielinternen und die Zeiten der 2D-Spiele, wie z.B. bei "Stunts 4D", in denen ein Editor eingebaut war, sind im Grunde auch vorbei. Ich möchte trotzdem kurz darauf hinweisen, dass es neue und interessante Ansätze gibt, einen dreidimensionalen Editor direkt in einem 3D-Spiel unterzubringen. Beim allseitsbekannten "Doom 3" kann man immerhin schon das Spiel direkt aus dem Editor starten und umgekehrt. Es geht aber noch radikaler: Die Cube"-Engine, welche inzwischen in der 2. Version vorliegt und sowohl Freeware, als auch Open-Source ist, bietet die Möglichkeit die Spielwelt direkt im Spiel zu verändern. Es gibt sogar einen sogenannten "coop edit"-Modus; soll heißen: Man kann die Spielwelt direkt im Spiel zusammen mit anderen Mitspielern online editieren und das soll angeblich sehr spaßig sein. Diese Variante kann man sich mal als Alternative nach Abschluss der "normalen" Arbeiten im Hinterkopf behalten..
Offensichtlich ist es einfacher, einen Editor erst im Nachhinein zu implementieren oder sofort auf offene Standards der Grafikdaten zurückzugreifen.
[Bearbeiten] Die Prinzipien
[Bearbeiten] MDI
Zu Recht fragst du jetzt: "Was zum Henker ist MDI?"
MDI ist die Abkürzung für "Multiple Document Interface". Mit Hilfe von MDI ist es möglich mehrere Dokumente in einem einzigen Programm gleichzeitig geöffnet zu haben.
Meistens ist davon nur ein Dokument im Vollbild geöffnet und man kann im Menü die anderen Dokumente aktivieren. Man kann das Dokumentenfenster aber auch verkleinern oder sämtliche Fenster nebeneinander anordnen lassen, um einen guten Überblick zu haben.
[Bearbeiten] Vorbereitungen
Zu wxWidgets gibt es im Moment noch recht wenige Tutorials... leider. Es existiert sogar ein eigenes WxWiki, aber auch das ist immer noch sehr oberflächlich. Wie gesagt, ich versuche selbst grad hinter das Prinzip dieses Frameworks zu steigen. Das erste Ziel ist jetzt erstmal ein Fenster zu erzeugen.
Ich bin davon überzeugt, dass es die Übersichtlichkeit erhöht und die Wartung vereinfacht, wenn man seine Programme von Anfang an in möglichst kleine, aber funktionelle Teile gliedert. Deshalb wird auch das "Hello World"-Programm gleich in zwei Teile gegliedert. Es gibt eine Möglichkeit, sich diesen Aufwand zu sparen und alles in einer Funktion zu erledigen; das Problem ist nur: Man freut sich über das selbst geschriebene Programm und im nächsten Schritt baut man es wieder ab, weil wir hier schließlich eine MDI-Applikation schreiben...
DevCpp ist vielleicht noch nicht sehr verbreitet. Ich tue einfach mal so, als hätte noch nie jemand was davon gehört oder gesehen:
[Bearbeiten] Projekt erstellen
Zu allererst erstellen wir ein leeres Project mit DevCpp. Die allermeisten wissen, wie das geht, für völlige Neueinsteiger in dieser Entwicklungsumgebung: Dazu geht man ins Menu unter "Datei", "Neu" und wählt dort "Projekt". Auf der ersten Karteikarte findet ihr die Option "Empty Project". Diese wählt man an und gibt unten links erst einen Projektnamen ein und betätigt dann den "Ok"-Button. Voilà, ein leeres Projekt ;). Intuitiv und einfach.
Jetzt fügen wir dem Projekt eine neue Datei hinzu. Ganz links ist ein Seitentrenner zu sehen, der Karteikarten namens "Projekt", "Klassen" und "Fehlersuche" enthält. Befindet man sich auf "Projekt", müsste darunter in der (noch fast leeren) Liste nur ein Symbol mit dem Namen, den ihr dem Projekt gegeben habt, stehen. Das klickt man mit der rechten Maustaste an und wählt "neue Datei". Jetzt öffnet sich ein neues Fenster mit dem Namen "Unbenannt1", unsere neue Datei im Projekt. Diese speichern wir als "CEditor.h" ab. Das ist mein Name dafür, ihr könnt sie auch "MeinEditor.h" oder sonstwie nennen, hauptsache der Name gibt einen Hinweis auf die in der Datei stattfindenden Vorgänge und wozu sie gehört. Wäre jetzt echt übertrieben, das "en détail" zu erklären, stimmts ^^. Wichtig ist nur, dass man als Dateiendung "Header files(*.h;*.hpp;*.rh;*.hh)" wählt.
[Bearbeiten] Erzeugung des "precompiled headers" unter DevCpp
Im allerersten Header kann man sich dafür entscheiden, einen namens "wxprec.h" des wxWidgets-Frameworks zu nutzen, um einen Geschwindigkeitsgewinn beim Kompilieren zu erzielen. Dummerweise kann man in DevCpp nicht ohne Weiteres den vorinstallierten precompiled header der wxWidgets nutzen. Entweder man generiert sich einen eigenen precompiled header oder man nimmt einen kleinen Geschwindigkeitsverlust in Kauf, der bei jeder Kompilierung durch die ständige Neukompilierung nicht veränderter Header der wxWidgets entsteht. Ist eine Frage der Geduld und der Systemleistung; vielleicht gibt es Minimalisten mit Win98 und 400 Mhz unter den Lesern, bei denen sich das durchaus bemerkbar machen könnte. Durch
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif //WX_PRECOMP
wird erklärt: "Wenn keine precompiled header unterstützt werden, dann nimm die uncompilierten header der wxWidgets". Das heißt also, dass sie dann bei jedem Kompilieren mit neu generiert werden --> laaaaangsaaaam!
...in Arbeit...
[Bearbeiten] Erstes wxWidgets-Fenster mitsamt OpenGL
Jetzt erstellen wir den wichtigsten, den "Haupt"-Header (oder Main-Header oder wie man will ;] ). Er soll die Includes zusammenfassen und zur Hauptdatei gehören. Mein Header heißt "CEditor.h". Die dazugehörige Quelldatei sollte "CEditor.cpp" heißen. Die kann man gleich mal dem Projekt hinzufügen und erstmal leer lassen, dazu komme ich später. Ihr könnt den Header auch "Main.h" und die Quelldatei "Main.cpp" nennen, falls euch "CEditor" zu sehr verwirrt. Es ist aber der Übersicht halber wichtig, dass Header und Quelldatei den gleichen Namen tragen. Das trifft hier insbesondere zu, weil in den beiden zusammen gehörenden Dateien eine Klasse definiert werden wird. Die Dateinamen stehen im Besten Fall eng im Zusammenhang mit den Klassen in einer Datei. Das dient der Übersichtlichkeit, die sonst bei zwei Dutzend Dateien und mehr sehr schnell verloren geht.
[Bearbeiten] Der Main-Header
So soll der Haupt-Header aussehen:
#ifndef _CEDITOR_H_
#define _CEDITOR_H_
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif //WX_PRECOMP
#include "wx/glcanvas.h"
#include "gl/gl.h"
#include "gl/glu.h"
#include "CEditorFrame.h"
class CEditor : public wxApp {
public:
virtual bool OnInit();
};
#endif
Erklärung:
Wie im Abschnitt "Erzeugung des "precompiled headers" unter DevCpp" bereits erklärt wurde, kann man sich für oder gegen precompiled header entscheiden bzw. wenn man sich dafür entscheidet, muss man den genannten Abschnitt lesen und einen erzeugen. Ansonsten wird durch:
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif //WX_PRECOMP
automatisch "wx.h" eingebunden.
Danach includiert man "wx/glcanvas.h". Man braucht diesen, wenn man OpenGL in den wxWidgets initialisieren will (und nach meinen bisherigen Erkenntnissen geht es nur so). Anschließend kommen die üblichen gl-Header.
Der fünfte include ist ein anderer Header, den wir selbst schreiben müssen. In ihm wird unser Frame definiert. Dazu gleich mehr, erstmal der Rest der Datei:
Wie man sieht, definieren wir eine Klasse "CEditor", die von "wxApp" erbt. "wxApp" ist die wxWidgets-Klasse, die die Anwendung an sich abstrahiert. In unserer neuen Klasse definieren wir außerdem eine Funktion "OnInit()". Richtigerweise muss man eigentlich sagen, wir überschreiben diese Funktion, denn sie ist in den wxWidgets bereits vordefiniert. Sie ist aber virtuell und überschreibbar. Von dieser Sorte Funktionen gibt es in den wxWidgets einige Exemplare. Weitere Beispiele wären "OnPaint()" oder "OnEnterWindow()". Diese Funktionen sind Teil des Schemas, nach dem die wxWidgets (und auch die WinAPI) funktionieren. Es wird zum Beispiel ein Button betätigt und irgendwo ist definiert: "Wenn einer den Button klickt, mache dieses und jenes.", eine OnIrgendwas()-Funktion. "OnInit()" ist insofern ein Spezialfall, als das sie, wie der Name sagt, bei der Initialisierung der Applikation aufgerufen wird. Wie das alles funktioniert, sehen wir später, jetzt komme ich erstmal zu diesem mysteriösen Begriff "Frame"...
[Bearbeiten] Frame
Wer die WinAPI und die MFC kennt, wird sich hier sofort heimisch fühlen. Oben wurde der Header für die Initialisierung des Hauptprogramms gezeigt. Dort: wir leiten "unser Programm" von der allgemeinen Programmklasse der wxWidgets ab. Das was auf dem Bildschirm an Menus, Buttons und sonstigen Ausgaben dargestellt wird, kommt jedoch in einem anderen Teil unter, dem Frame. Es wird ein Frame definiert, der dann zu unserem Programm, gehören wird. Der Frame bildet die eigentliche Benutzeroberfläche.
Applikation (das Hauptprogramm, Klasse "CEditor" von "wxApp") und dazugehöriger Frame sind zwei Programmteile ein und derselben Medaille. Das Hauptprogramm macht die Hintergrundarbeit und repräsentiert das Programm gegenüber dem Betriebssystem (so wie die WinMain), der Frame (re-)präsentiert das Programm nach außen. Man übersetze "Frame" mit "Rahmen"; also das Programm wird eingerahmt; ein Rahmen trägt etwas nach außen, usw...
Also, bevor ich jetzt die Quelldatei zum Header unserer Applikation male, kommen wir erstmal zum Frame. Alles fängt natürlich mit dem Header an:
#ifndef _CFRAME_H_ #define _CFRAME_H_
class CFrame : public wxFrame {
public:
CFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
void OnExit(wxCommandEvent &event);
wxMenuBar *pMenuBar;
wxMenu *pMenu_File;
wxToolBar *pToolBar;
DECLARE_EVENT_TABLE()
};
#endif
Erklärung:
TO DO...
[Bearbeiten] Das Spiel
[Bearbeiten] Links
Isogames.de: Alles über Spieleprogrammierung
Blitz Basic - Programmiersprache für Spiele
Die Robsite: Tutorials, Onlinebücher rund um die Programmierung
Nehe bei GameDev: DIE OpenGl-Tutorial-Seite!
Google.de: nur zur Erinnerung, dass man ganz besonders viel Suchen sollte. Wer sucht der findet!

