Damit man mit diesem Tutorial arbeiten kann, muss man sich mit C++ auskennen.
Andernfalls hilft Galileo Computing :).
In dieser Dokumentation gilt übrigens für allgemeine Funktionen: MSDN und Google sind beste Freunde :).
Ziel der Tutorials ist es hier übrigens nicht Code vorzukauen sondern Vorgehensweisen zu zeigen, um daraus selbstständig Wissen zu schaffen. (Wie im echten Leben.)[/color][/b]
Bevor wir beginnen
Man sollte strikt zwischen einer Game Engine und einer eigenen Implementation einer Game Engine unterscheiden. Während es bei einer fertigen Game Engine oder einem Engine-Paket eher darum geht, fertige Codeteile zusammenzuklastern, geht es in der eigenen Game Engine eher darum, den Beton und das Klebemittel für das Zusammenklastern zu schaffen. Wer also möglichst schnell ein Spiel herzaubern will, ist hier falsch positioniert und sollte sich eher mit Bibliotheken wie 3D Gamestudio befassen. Wer Klebemittel erzeugen lernen will ist hier allerdings richtig.
Im ersten Tutorial geht es darum einfach nur ein Fenster zu erzeugen: ganz unobjektorientiert, damit man ein Gefühl für DirectX erstmal bekommt. Ich könnte natürlich auch auf die objektorientierte Schiene fahren - allerdings wirkt das dann doch eher abschreckend. Wir werden zum späteren Zeitpunkt unser Framework objektorientiert zusammenfassen und nicht jetzt.
Vorbereitung
Am Besten fangen wir damit an ein Projekt zu erzeugen. Starte hierzu Visual Studio, wähle dann "Neues Projekt", wähle VisaulC++ und ein Win32-Projekt - keine Konsolenanwendung. Du wählst den Pfad und es öffnet sich folgt wieder ein Fenster. Hier wählt man einfach im zweiten Punkt eine Windows-Anwendung und erstellt ein leeres Projekt. Danach gibt es so gesehen ein nacktes Projekt und es ist eine ziemliche Mundwüste. Keine Angst: Alles wird noch schön feucht. Ich gehe auch davon aus, dass man mit der IDE schon vertraut ist, da ja C++-Kenntnisse vorausgesetzt werden.
Man erstellt eine "main.cpp" und mit dieser Datei können wir uns jetzt hier schon zufrieden geben. Nun ja, nicht ganz: Es fehlt der Quellcode. Darum zeige ich mal, was wir machen müssen: Implementation
Fangen wir mit unseren Globalen Variablen an: Da wir eine Windows-Anwendung erzeugen wollen, benötigen wir eine windows.h. Zudem benötigen wir einen globalen Alias zu unserem Fenster - der ist vom Typ HWND (Handle to a Window):
Code: Alles auswählen
// Header-Datei für die Windowsfunktionen
#include <windows.h>
HWND hWnd; // Das Alias zum Fenster
Code: Alles auswählen
int WINAPI WinMain(HINSTANCE hInstance /* Unser Alias zur Anwendung */,
HINSTANCE hPrevInstance /* Ehemaliger Alias */,
LPSTR lpCmdLine /* Kommandozeile, wenn vorhanden */,
int nShowCmd /* Art, wie das Fenster gestartet werden soll */)
{
// Fenster erstellen & Anwendung registrieren
if(!AssignApplicationAndCreateWindow(hInstance)) return 0;
// Die Hauptschleife
while(RunWindow())
{
// Hier der Programmcode der Hauptschleife
}
// Programm verlassen
return 0;
}
Code: Alles auswählen
// WNDCLASS-Struktur für die Applikationsbeschreibung für Windows füllen
WNDCLASS wc = {0}; // << Auch gleich mit 0 vorinitialisieren
wc.hbrBackground = HBRUSH(GetStockObject(BLACK_BRUSH)); // Schwarzer Hintergrund
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Normaler Cursor
wc.hIcon = LoadIcon(NULL, IDC_ARROW); // Anwendungsicon
wc.hInstance = hInstance; // Alias der Anwnedung übergeben
wc.lpfnWndProc = WndProc; // Unsere Callback-Funktion
wc.lpszClassName = L"MyZFXApp"; // Identifikationsname
wc.style = CS_HREDRAW|CS_VREDRAW; // Anwendungsstil
// Nun Applikation registrieren
RegisterClass(&wc);
Code: Alles auswählen
// Das Fenster erstellen
if(NULL==(hWnd = CreateWindowEx(NULL /* Kein Stil */,
wc.lpszClassName /* Der Identifikationsname */,
L"ZFX Tutorial Application" /* Fenstertitel */,
WS_VISIBLE|WS_POPUP /* Aussehen des Fensters */,
0, 0, // Startposition (x, y)
GetSystemMetrics(0) /* X-Auflösung des Users */,
GetSystemMetrics(1) /* Y-Auflösung des Users */,
NULL, // Ist von keinem Fenster abhängig
NULL, // Kein Menü
hInstance, // Anwendungsalias
NULL))) // Initialnachricht an das Elternfenster
return false;
return true;
Jetzt sind wir gezwungen uns mit der WndProc-Callbackfunktion zu beschäftigen. Zur Erläuterung erstmal der Quellcode:
Code: Alles auswählen
// Unsere Callback-Funktion zum Auswerten der Fensternachrichten
LRESULT CALLBACK WndProc(HWND hWnd /* Das Fenster */, UINT Msg /* Die Nachricht */,
WPARAM wParam, LPARAM lParam) // Jeweils Eingabewerte
{
// Wenn eine Beendigungsnachricht kommt beenden:
if(Msg == WM_DESTROY) { PostQuitMessage(0); return 0; }
// Wenn ESC gedrückt wurde, Anwendung auch verlassen:
if(Msg == WM_KEYDOWN && wParam == VK_ESCAPE) { PostQuitMessage(0); return 0; }
// Andernfalls Nachricht einfach verarbeiten:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
Zuletzt beschäftigen wir uns mit der Auswertung der Nachricht: Der RunWindow()-Funktion.
Code: Alles auswählen
bool RunWindow()
{
MSG msg = {0};
// Nachricht verarbeiten, falls vorhanden
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
// Wenn das Programm beendet werden soll, Hauptschleife verlassen
if(msg.message == WM_QUIT) return false;
// Nachricht verarbeiten
DispatchMessage(&msg);
TranslateMessage(&msg);
}
return true;
}
Man muss im Hinterkopf behalten, dass das Programm nur solange läuft, wie wir immer true zurückliefern, da wir while(RunWindow()) geschrieben haben.
In der nun vorhanden Hauptschleife des Programms würden wir nun unser Programm laufen lassen und KI, Grafik, Eingaben, Sound etc. verwalten. Aber das ist Thema eines späteren Tutorials.
Kompilieren wir das Programm, so sollte sich ein schwarzes Fenster über den gesamten Desktop ausbreiten und per ESC beenden lassen.
Für alle gibt es nochmal den gesamten Quellcode hier kompakt:
Code: Alles auswählen
#pragma region Globale Variablen
// Header-Datei für die Windowsfunktionen
#include <windows.h>
HWND hWnd; // Das Alias zum Fenster
#pragma endregion
#pragma region Callbackfunktion: Auswerten der Fensternachrichten
// Unsere Callback-Funktion zum Auswerten der Fensternachrichten
LRESULT CALLBACK WndProc(HWND hWnd /* Das Fenster */, UINT Msg /* Die Nachricht */,
WPARAM wParam, LPARAM lParam) // Jeweils Eingabewerte
{
// Wenn eine Beendigungsnachricht kommt beenden:
if(Msg == WM_DESTROY) { PostQuitMessage(0); return 0; }
// Wenn ESC gedrückt wurde, Anwendung auch verlassen:
if(Msg == WM_KEYDOWN && wParam == VK_ESCAPE) { PostQuitMessage(0); return 0; }
// Andernfalls Nachricht einfach verarbeiten:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
#pragma endregion
#pragma region Funktion: Ein Fenster erstellen
bool AssignApplicationAndCreateWindow(HINSTANCE hInstance)
{
// WNDCLASS-Struktur für die Applikationsbeschreibung für Windows füllen
WNDCLASS wc = {0}; // << Auch gleich mit 0 vorinitialisieren
wc.hbrBackground = HBRUSH(GetStockObject(BLACK_BRUSH)); // Schwarzer Hintergrund
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Normaler Cursor
wc.hIcon = LoadIcon(NULL, IDC_ARROW); // Anwendungsicon
wc.hInstance = hInstance; // Alias der Anwnedung übergeben
wc.lpfnWndProc = WndProc; // Unsere Callback-Funktion
wc.lpszClassName = L"MyZFXApp"; // Identifikationsname
wc.style = CS_HREDRAW|CS_VREDRAW; // Anwendungsstil
// Nun Applikation registrieren
RegisterClass(&wc);
// Das Fenster erstellen
if(NULL==(hWnd = CreateWindowEx(NULL /* Kein Stil */,
wc.lpszClassName /* Der Identifikationsname */,
L"ZFX Tutorial Application" /* Fenstertitel */,
WS_VISIBLE|WS_POPUP /* Aussehen des Fensters */,
0, 0,
GetSystemMetrics(0) /* X-Auflösung des Users */,
GetSystemMetrics(1) /* Y-Auflösung des Users */,
NULL, // Ist von keinem Fenster abhängig
NULL, // Kein Menü
hInstance, // Anwendungsalias
NULL))) // Initialnachricht an das Elternfenster
return false;
return true;
}
#pragma endregion
#pragma region Funktion: Eine Nachricht in der Hauptschleife verarbeiten
bool RunWindow()
{
MSG msg = {0};
// Nachricht verarbeiten, falls vorhanden
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
// Wenn das Programm beendet werden soll, Hauptschleife verlassen
if(msg.message == WM_QUIT) return false;
// Nachricht verarbeiten
DispatchMessage(&msg);
TranslateMessage(&msg);
}
return true;
}
#pragma endregion
#pragma region Unsere Startfunktion
int WINAPI WinMain(HINSTANCE hInstance /* Unser Alias zur Anwendung */,
HINSTANCE hPrevInstance /* Ehemaliger Alias */,
LPSTR lpCmdLine /* Kommandozeile, wenn vorhanden */,
int nShowCmd /* Art, wie das Fenster gestartet werden soll */)
{
// Fenster erstellen & Anwendung registrieren
if(!AssignApplicationAndCreateWindow(hInstance)) return 0;
// Die Hauptschleife
while(RunWindow())
{
// Hier der Programmcode der Hauptschleife
}
// Programm verlassen
return 0;
}
#pragma endregion
Benötigte Bibliotheken: Visual Studio Windowsbibliotheken (Standard)
Benötigte Headerfiles: windows.h
Schlusswort
Nun ist man in der Lage ein Fenster zu erzeugen. Ausgangspunkt einer fast jeden 3D-Anwendung. Es ist ein kleiner Schritt für die Gesamtheit der Themen, aber ein großer Schritt für den Einsteiger!
Zusatzaufgaben
1. Füge der Anwendung eine Titelleiste hinzu.
2. Ändere den Titel zu "Rock'n'DirectX"
3. Ändere die Hintergrundfarbe zu weiß
3. Lass das Programm auch mit E beenden