My brain makes things explode.
C++/D3D Pong Tutorial 02: Setting up Direct3D and our Engine Framework
Tutorial Description
In the last tutorial, we set up the basics required for the Windows API to open our window. While basic, the task was also essential, though many people who are familiar with Win32 Programming will likely have gained nothing from it. Here, we are going to do several tasks. In no particular order, they are as follows:
1) Set up Direct3D rendering
2) Set up the framework for our game engine
3) Implementing game timing
Downloads
Binary Only (10.4KB)
Full VS2010 Project Dir (13.1MB)
Starting Words
Notice: This tutorial continues from where “C++/D3D Pong Tutorial 01” article left off. Please read it if you have not already.
Libraries
You will need to add the following three libraries to your project:
d3d9.lib
d3dx9.lib
dxguid.lib
Code Listing 1: CGameTimer.h
#pragma once
// Global Headers
#include <time.h>
#include <Windows.h>
namespace D3DPong
{
class CGameTimer
{
private:
// Marks the starting point of the timer
DWORD _start;
public:
/** Constructor & Destructor **/
CGameTimer(void);
/** Function Declarations **/
DWORD GetTime(void); // Retrieve the current time (in milliseconds)
DWORD GetStartTime(void); // Get the Timer's start time (in milliseconds)
DWORD GetRunTime(void); // Get the Timer's running time (in millseconds)
void Reset(void); // Reset the timer.
};
};
This small bit of code will mark the base of our game timing system. This system will make it possible to use delta timing, so that we do not have to cap the frame limit by using unnecessary processing. You will see how this works when we start setting up the rendering engine, but for now, allow me to give you an explanation of the class.
private: // Marks the starting point of the timer DWORD _start;
The game timer’s only variable, this DWORD value will store the start time, in milliseconds, for when the timer started (its creation or a call to reset()).
/** Function Declarations **/ DWORD GetTime(void); // Retrieve the current time (in milliseconds) DWORD GetStartTime(void); // Get the Timer's start time (in milliseconds) DWORD GetRunTime(void); // Get the Timer's running time (in millseconds) void Reset(void); // Reset the timer.
Constructor declarations aren’t anything special, so I won’t bother going into detail about that. These four functions, however, are very important to the engine.
GetTime() will give us an easier method of getting the system time, in milliseconds. It takes away some of our reliance on the Windows API.
GetStartTime() will allow us to retrieve the time that the Timer was last started or reset.
GetRunTime() will give us the running time of our timer object. This will be vital when we implement Delta timing.
Reset() will reset the timer’s “start time.” This will allow us to more easily handle timed events, if a timer doesn’t need to continually run.
Code Listing 2: CGameTimer.cpp
#include "CGameTimer.h"
namespace D3DPong
{
CGameTimer::CGameTimer(void)
{
this->_start = timeGetTime();
}
DWORD CGameTimer::GetTime(void)
{
return timeGetTime();
}
DWORD CGameTimer::GetStartTime(void)
{
return this->_start;
}
DWORD CGameTimer::GetRunTime(void)
{
return (timeGetTime() - this->_start);
}
void CGameTimer::Reset(void)
{
this->_start = this->GetTime();
}
};
And here we are, the flesh and bones of the game timer class. All of these are single-line functions, but this will make a large portion of our engine’s logic. Most of it should be pretty obvious, if you read the descriptions above.
Creating our Entry Point
Before we get into the nitty-gritty portions of creating our engine framework and setting up Direct3D, I’m going to do two things. First, I’m going to show you where we are going to set up and handle our game logic, which will be separate from our Engine code. After that, I am going to give you a brief roadmap before jumping into the large bits of code that will make up our game engine.
Code Listing 3: Game.h
#pragma once #include <windows.h> extern bool GamePreLoad(void); extern bool GameInit(void); extern void GameUpdate(float); extern void GameRender(float); extern void GameEnd(void);
These functions, when fleshed out, will contain all of our game’s logic.
GamePreLoad() – Within this method, we will do some initial setup (such as setting up the game window dimensions)
GameInit() – And here, we will load our initial game resources and set up initial logic settings.
GameUpdate(delta) – This function will be called every frame, and will contain our game logic. The floating-point value that it is passed is the timing delta (the time between the last frame and this one). We will use this timing delta
GameRender(delta) – This function will also be called every frame, and will always be called after GameUpdate. Our rendering logic will be executed here.
GameEnd() – This will be called to execute last-minute cleanup for the game.
Code Listing 4: Game.cpp
#include "Game.h"
bool GamePreLoad(void)
{
return true;
}
bool GameInit(void)
{
return true;
}
void GameUpdate(float delta)
{
}
void GameRender(float delta)
{
}
void GameEnd(void)
{
}
This is just the empty framework for now. All of our game logic, when we begin work on it in future tutorials, will go here. We’ll modify this at the end of this tutorial to do some basic setup in the PreLoad function.
What Lies Ahead
Alright, we’ve got some fun work ahead of us. First, we are going to be setting up the game engine’s code. That will be a pretty decent-sized trek through the woods, but I’m going to try to cover it as thoroughly as I can without stepping into a discussion about the various pieces of clockwork that aren’t entirely necessary.
Code Listing 5: CGameEngine.h
#pragma once
// Global Header Files
#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h>
// Local Header Files
#include "CGameTimer.h"
#include "Game.h"
namespace D3DPong
{
class CGameEngine
{
private:
CGameEngine(void); // ctor
CGameEngine(CGameEngine const&) { }; // copy ctor
virtual ~CGameEngine(void); // dtor
static CGameEngine* _instance;
HWND _windowHandle;
LPDIRECT3D9 _d3d;
LPDIRECT3DDEVICE9 _device;
LPD3DXSPRITE _spriteHandler;
LPCWSTR _windowTitle;
int _screenWidth, _screenHeight, _colorDepth;
const static D3DCOLOR _ambientColor;
CGameTimer* _gameTimer;
bool _gameRunning;
public:
static CGameEngine* Instance(void) { return _instance; }
bool Init(int, int, int);
void End(void);
void Update(void);
void BeginRender(void);
void EndRender(void);
LPDIRECT3DDEVICE9 GetDevice(void) { return this->_device; }
LPD3DXSPRITE GetSpriteHandler(void) { return this->_spriteHandler; }
HWND GetWindowHandle(void) { return this->_windowHandle; }
LPCWSTR GetWindowTitle(void) { return this->_windowTitle; }
int GetScreenWidth(void) { return this->_screenWidth; }
int GetScreenHeight(void) { return this->_screenHeight; }
int GetColorDepth(void) { return this->_colorDepth; }
bool GetGameRunning(void) { return this->_gameRunning; }
CGameTimer* GetGameTimer(void) { return this->_gameTimer; }
void SetWindowHandle(HWND val) { this->_windowHandle = val; }
void SetWindowTitle(LPCWSTR);
void SetScreenWidth(int val) { this->_screenWidth = val; }
void SetScreenHeight(int val) { this->_screenHeight = val; }
void SetColorDepth(int val) { this->_colorDepth = val; }
};
};
Aaaannnd here we go. Time for the explanations. I’ll leave out the accessor & mutators, as those should be second nature to anybody who has been able to follow along thus far.
CGameEngine(void); // ctor
CGameEngine(CGameEngine const&) { }; // copy ctor
virtual ~CGameEngine(void); // dtor
static CGameEngine* _instance;
Here, we have the constructor, copy constructor, and destructor. These are all defined in the private section because we only want to allow them to be initialized when the engine instance (which is a singleton) is first defined (in CGameEngine.cpp). The copy constructor is left empty (it wont be used at all), and the destructor is made virtual in case we decide to create a class that inherits from it at a later point (which is unlikely, but the option will still be available). Lastly, we create a static pointer that will serve as the singleton. We will be able to access the game engine at any time by calling CGameEngine::Instance().
HWND _windowHandle; LPDIRECT3D9 _d3d; LPDIRECT3DDEVICE9 _device; LPD3DXSPRITE _spriteHandler; LPCWSTR _windowTitle; int _screenWidth, _screenHeight, _colorDepth; const static D3DCOLOR _ambientColor; CGameTimer* _gameTimer; bool _gameRunning;
Most of the member variables are self-explanatory. The engine will need an instance of the game window’s handle for some of the Direct3D setup, as well as for other tasks, such as changing the window’s title. The three Direct3D variables are the Direct3D instance, which gives us access to a lot of the device creation features, the Direct3D device itself, which will handle our game’s rendering backbone, and then the D3DX sprite handler, which will allow us to do 2D rendering without going through the trouble of creating and texturing 3D quads, which anybody with experience doing that method of 2D will tell you is a royal pain in the ass. Finally, we create variables to store our window’s attributes (title, screen size, color depth), a static variable to store our ambient color (which will not change throughout the game), a boolean to keep track of whether or not the game is running (which will be important when we start modifying our game loop), and a game timer, which we will use to track the time between frames in the game.
CGameEngine* Instance(void) { return _instance; }
Again, back to our Singleton implementation. Although this is simply an accessor, I wanted to point it out because of its importance to our singleton implementaiton.
bool Init(int, int, int); void End(void); void Update(void); void BeginRender(void); void EndRender(void);
And these methods make up the meat of our game engine.
Init(width, height, depth) – This method will initialize the Direct3D systems, and will configure our render settings.
End() – This is called to end the game.
Update() – This method is called every frame, will calculate the timing delta and call our GameUpdate() and GameRender() methods.
BeginRender() – Basic rendering setup (starting the D3D device, starting the sprite handler, etc)
EndRender() – The inverse of our BeginRender function – this method ends the rendering systems started in BeginRender, then presents the scene (swaps the render buffers)
void SetWindowTitle(LPCWSTR);
You may have noticed this lonely mutator sitting here, undefined. Fight your urge to follow the other mutators on this one – we’re going to be defining this one in CGameEngine.cpp, so that if we would decide to change our window title in-game, we can, and doing so requires a touch more than changing the value of _windowTitle.
And here we go, the meat of our game engine.
Code Listing 6: CGameEngine.cpp
#include "CGameEngine.h"
#include "WinMain.h"
namespace D3DPong
{
const D3DCOLOR CGameEngine::_ambientColor = D3DCOLOR_RGBA(255, 255, 255, 0);
CGameEngine* CGameEngine::_instance = new CGameEngine();
CGameEngine::CGameEngine(void)
: _windowHandle(NULL), _windowTitle(L"D3DPong"),
_screenWidth(640), _screenHeight(480), _colorDepth(32),
_gameRunning(false),
{
}
CGameEngine::~CGameEngine(void)
{
if(this->_gameRunning)
this->_gameRunning = false;
if(this->_spriteHandler)
this->_spriteHandler->Release();
if(this->_device)
this->_device->Release();
if(this->_d3d)
this->_d3d->Release();
delete this->_spriteHandler;
delete this->_device;
delete this->_d3d;
}
bool CGameEngine::Init(int width, int height, int depth)
{
// Set up Engine Vars
this->_screenWidth = width;
this->_screenHeight = height;
this->_colorDepth = depth;
// Initialize Direct3D
this->_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(!this->_d3d)
return false;
// Get the Display Mode (to ensure maximum compatibility)
D3DDISPLAYMODE disp;
this->_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &disp);
// Presentation Parameters
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.BackBufferFormat = disp.Format;
d3dpp.BackBufferWidth = this->_screenWidth;
d3dpp.BackBufferHeight = this->_screenHeight;
d3dpp.hDeviceWindow = this->_windowHandle;
// Create the Direct3D Device
this->_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, this->_windowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &this->_device);
if(!this->_device)
return false;
// Enable Z-buffering, set the fill mode, and set the ambient color (white)
this->_device->SetRenderState(D3DRS_ZENABLE, true);
this->_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
this->_device->SetRenderState(D3DRS_AMBIENT, this->_ambientColor);
// Initialize the Sprite handler
if(FAILED(D3DXCreateSprite(this->_device, &this->_spriteHandler)))
return false;
// Game Initialization
if(!GameInit())
return false;
return true;
}
void CGameEngine::End(void)
{
this->_gameRunning = false;
GameEnd();
}
void CGameEngine::Update(void)
{
static float lastFrame = 0.0f;
float delta = ((float)((int)this->_gameTimer->GetTime())) - lastFrame;
lastFrame = ((float)((int)this->_gameTimer->GetTime()));
GameUpdate(delta);
this->BeginRender();
GameRender(delta);
this->EndRender();
}
void CGameEngine::BeginRender(void)
{
if(!this->_device)
return;
if(FAILED(this->_device->BeginScene()))
return;
if(FAILED(this->_spriteHandler->Begin(D3DXSPRITE_ALPHABLEND)))
return;
}
void CGameEngine::EndRender(void)
{
if(!this->_device)
return;
this->_spriteHandler->End();
if(FAILED(this->_device->EndScene()))
return;
if(FAILED(this->_device->Present(NULL, NULL, NULL, NULL)))
return;
}
void CGameEngine::SetWindowTitle(LPCWSTR val)
{
this->_windowTitle = val;
if(this->_windowHandle)
SetWindowText(this->_windowHandle, this->_windowTitle);
}
};
Ready? Here we go.
const D3DCOLOR CGameEngine::_ambientColor = D3DCOLOR_RGBA(255, 255, 255, 0); CGameEngine* CGameEngine::_instance = new CGameEngine();
Here, we are creating and defining the singleton instance, and setting up the ambient color constant. Pretty simple.
CGameEngine::CGameEngine(void)
: _windowHandle(NULL), _windowTitle(L"D3DPong"),
_screenWidth(640), _screenHeight(480), _colorDepth(32)
_gameRunning(false),
{
}
Technically, this is an empty constructor, but we’re using a shorthand method of setting default values for some of our class’s member variables.
CGameEngine::~CGameEngine(void)
{
if(this->_gameRunning)
this->_gameRunning = false;
if(this->_spriteHandler)
this->_spriteHandler->Release();
if(this->_device)
this->_device->Release();
if(this->_d3d)
this->_d3d->Release();
delete this->_spriteHandler;
delete this->_device;
delete this->_d3d;
}
A standard cleanup within the Game Engine’s destructor. First, we have to release all of the DirectX devices, to ensure that none of them are currently being used, and so that they can do their internal cleanup. Finally, we delete the pointers, as a final cleanup. Because this will mark the end of the application, this wouldn’t cause any sort of memory leak, but I’d prefer not to rely on the operating system to clean up our leftovers.
bool CGameEngine::Init(int width, int height, int depth)
{
// Set up Engine Vars
this->_screenWidth = width;
this->_screenHeight = height;
this->_colorDepth = depth;
// Initialize Direct3D
this->_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(!this->_d3d)
return false;
// Get the Display Mode (to ensure maximum compatibility)
D3DDISPLAYMODE disp;
this->_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &disp);
Onto the Initialization. This one’s a big one. First, we’re just setting our member variables, so that we can keep track of our size and depth changes. Initializing Direct3D itself is easy and straightforward – you just call Direct3DCreate9 to create a Direct3D 9 manager, and pass the SDK version (Always pass it as D3D_SDK_VERSION, to ensure that the engine is compatible with future Direct3D releases).
Finally, we get the display mode of the desktop. This will be used when we are setting the Direct3D Presentation Parameters, which define hour our rendering is handled with the graphics card. While we could stick with a generic D3DFMT_R5G6B5, which will work almost universally, this will ensure that the display format used is compatible with the current video card and monitor.
// Presentation Parameters D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); d3dpp.Windowed = true; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; d3dpp.BackBufferFormat = disp.Format; d3dpp.BackBufferWidth = this->_screenWidth; d3dpp.BackBufferHeight = this->_screenHeight; d3dpp.hDeviceWindow = this->_windowHandle;
The presentation parameters are incredibly important to Direct3D. It tells the system how large to make the back buffer (we’re using our screen dimensions), what to do when it swaps the buffers (discards the currently-rendering, then displays the new buffer), and sets the window handle that is used.
// Create the Direct3D Device this->_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, this->_windowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &this->_device); if(!this->_device) return false; // Enable Z-buffering, set the fill mode, and set the ambient color (white) this->_device->SetRenderState(D3DRS_ZENABLE, true); this->_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); this->_device->SetRenderState(D3DRS_AMBIENT, this->_ambientColor);
Now we’re creating the Direct3D rendering device. We are configuring it as follows:
1) It will use the default adapter (the primary monitor/video card), everything will be handled on the hardware level (graphics card) for rendering, rather than software rendering (basically, use the GPU instead of the CPU), and we pass the presentation parameters and the address to map the newly created device to. Finally, we set three render states: Z-buffering, Solid fill mode, and ambient color. These settings will enhance our graphics quality, and give us some fun tools to use in later games. (Have you noticed, yet, that the engine is something that can easily be re-used in later projects?)
// Initialize the Sprite handler if(FAILED(D3DXCreateSprite(this->_device, &this->_spriteHandler))) return false; // Game Initialization if(!GameInit()) return false;
Getting back to more simple-to-comprehend steps. Here, we’re ensuring that we are able to create our sprite handler (which will make our two-dimensional rendering a lot easier to implement), and calling the game’s initialization method, so that it can handle game setup. And we’re done.
void CGameEngine::End(void)
{
this->_gameRunning = false;
GameEnd();
}
This method will allow us to end the game. First, it changes the “running” flag, which will tell our engine to jump out of the loop, and then it calls the GameEnd method, allowing us to do some last-minute cleanup before the engine itself closes out.
void CGameEngine::Update(void)
{
static float lastFrame = 0.0f;
float delta = ((float)((int)this->_gameTimer->GetTime())) - lastFrame;
lastFrame = ((float)((int)this->_gameTimer->GetTime()));
GameUpdate(delta);
this->BeginRender();
GameRender(delta);
this->EndRender();
}
Another important one. The first thing we do is declare a static floating-point value, to store the time (in millseconds) of our last frame. We then calculate the delta (the time between calls to the Update() method) by subtracting the time the last frame started from the current game time. After calculating the delta, it changes frameTime to reflect the start of this frame. After that, we give a call to our GameUpdate method, passing the timing delta, start rendering, call GameRender, and then end rendering. In case you were wondering, the delta passed to GameRender is for animation purposes – the rendering method is the best place to put frame calculations for animations, in my opinion.
void CGameEngine::BeginRender(void)
{
if(!this->_device)
return;
if(FAILED(this->_device->BeginScene()))
return;
if(FAILED(this->_spriteHandler->Begin(D3DXSPRITE_ALPHABLEND)))
return;
}
Here, we start rendering. First of all, if our device isn’t active, we’re going to crash if we try to call any methods on it, so we do a check to make sure it exists. Then, we begin the Direct3D scene, and begin 2D rendering within the sprite handler. By passing the D3DXSPRITE_ALPHABLEND parameter to the sprite handler, we ensure that alpha values will be honored by the rendering system, making transparency an easy task to deal with – it’s the artist’s problem now. Transparent PNGs are the best thing to use for two-dimensional game art, at the moment (excluding, of course, custom, optimized file formats created for an engine).
void CGameEngine::EndRender(void)
{
if(!this->_device)
return;
this->_spriteHandler->End();
if(FAILED(this->_device->EndScene()))
return;
if(FAILED(this->_device->Present(NULL, NULL, NULL, NULL)))
return;
}
Now, we’re doing the opposite. We still need to make sure our device exists before we stop rendering, but beyond that, we stop the systems in the order they were presented. The last method, Present, swaps the display buffers, and is required if we want our rendering to actually appear.
void CGameEngine::SetWindowTitle(LPCWSTR val)
{
this->_windowTitle = val;
if(this->_windowHandle)
SetWindowText(this->_windowHandle, this->_windowTitle);
}
Do you remember how I said our SetWindowTitle was a special mutator? Here it is. We could just stick with setting our _windowTitle member variable, but that would make this function useful only in the GamePreLoad method, which is called before the game window is created. This method, however, allows us to find out if the window has been created already. If it has, it updates the title there, as well, allowing for us to, for example, display the game score in the window’s title (not recommended, and I will not be showing you how to do so.)
Moving on
Well, we’ve got our engine laid out, and it’s something that we’ll be able to use in future projects as well as this one – we’ll just need to change the contents of our game functions (GamePreLoad, GameInit, etc), though we might want to replace the namespace (D3DPong) with another at a later date. Now that we’ve got the hard part out of the way, we are going to make changes to the WinMain.cpp file that we created in the last tutorial, so that we can call our GamePreLoad function and actually make use of the game engine. You just got done with a lot of code, so I won’t hold it against you if you go take a breather, get some soda, or play a game for a while.
Code Change Set 1: WinMain.cpp
All of these changes will be to the WinMain.cpp file.
For starters, scroll to the top of the file, and include CGameEngine.cpp after including WinMain.h
#include "WinMain.h" #include "CGameEngine.h"
Remove g_bGameOver variable declaration. We’re going to be using our game engine’s GetGameRunning accessor in its place. Don’t worry about replacing it anywhere in code yet, we’ll do that on our way through.
We’re going to add a preprocessor directive to create an alias for CGameEngine::Instance(), to make it easier to access our game engine. Add the following after the two include directives:
#define GEInstance D3DPong::CGameEngine::Instance() using namespace D3DPong;
This will allow us an easier method of getting our game engine’s instance (make sure you don’t add a semicolon to that preprocessor macro – if you do, we can’t use GEInstance->method. We are then telling our engine that this source file will be using the D3DPong namespace, so that we don’t have to prefix all of our engine features with D3DPong::.
Now, search for:
case WM_QUIT:
case WM_CLOSE:
case WM_DESTROY:{
g_bGameOver = true;
} break;
and replace it with:
case WM_QUIT:
case WM_CLOSE:
case WM_DESTROY:{
GEInstance->End();
} break;
Makes things easier, doesn’t it? It also ensures that our GameEnd function is called when we end the game.
Remove the initialization of g_bGameOver from our WinMain function.
// Setup Globals g_bGameOver = false;
We want to allow our game the chance to set up the window and do some other system initialization before we set everything up, so add a call to GamePreLoad() where the g_bGameOver global used to be defined.
WNDCLASSEX wc; // Window Class Structure if(!GamePreLoad()) return 0; // Fill the structure
Now, we want to update our CreateWindowEx call to use the engine’s width and height, which will be set in GamePreLoad() (or left to their default 640×480 values, if not set):
Replace the entire call with:
// Create the Program Window hWnd = CreateWindowEx(NULL, // dwExStyle L"GameWindow", // Class Name - must match the lpszClassname of a registered window class GEInstance->GetWindowTitle(), // Window Title WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // dwStyle 0, 0, // Screen Coordinates (X, Y) GEInstance->GetScreenWidth(), GEInstance->GetScreenHeight(),// Screen Size (Width, Height) NULL, // Parent Window NULL, // Menu hInstance, // Instance Handle NULL); // Extra Parameters
Below that, after we Show and Update the window, we wil make a call to the game engine’s Init function:
// Initialize the Game Engine GEInstance->SetWindowHandle(hWnd); if(!GEInstance->Init(GEInstance->GetScreenWidth(), GEInstance->GetScreenHeight(), GEInstance->GetColorDepth())) return 0;
We pass the width, height, and color depth to the init function. It was designed this way, again, to allow later classes to inherit from the current engine. A note, for anybody who would do that: you’ll need to go into CGameEngine.cpp and change CGameEngine* CGameEngine::_instance = new CGameEngine(); to CGameEngine* CGameEngine::_instance = new NewClassName();. The singleton method that I used for this project is somewhat rough, and certainly isn’t the best method, but it’s one of the easiest to understand for new developers.
Finally, we update our application’s loop:
// Message Loop
while(GEInstance->GetGameRunning())
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GEInstance->Update();
}
Build, run, and see if it comes up with a black screen for you.
Game Setup Test
Now we’re going to do a quick, easy test to make sure we can properly configure our game at startup (in GamePreLoad). Open Game.cpp, and change GamePreLoad to the following:
bool GamePreLoad(void)
{
D3DPong::CGameEngine::Instance()->SetScreenWidth(800);
D3DPong::CGameEngine::Instance()->SetScreenHeight(600);
D3DPong::CGameEngine::Instance()->SetWindowTitle(L"Direct3D Pong Tutorial 02");
return true;
}
Note: I recommend creating the same Macro we defined in WinMain.cpp, so that you can shorten calls as GEInstance.
Run it, and you should have an 800×600 window, titled “Direct3D Pong Tutorial 02″.
Conclusion
That concludes the second tutorial in the D3D Pong series. Stay tuned for the next tutorial series, where we create a class to handle and render game sprites and scene objects.
| Print article | This entry was posted by Samantha on February 15, 2010 at 3:16 PM, and is filed under C++, Languages, Pong (C++/DirectX), Series, Tutorials. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
