Win-Framework For Games
This tutorial concludes the introductory tutorials on Windows programming by summarizing everything that has been discussed so far.
The Utility Classes
The utility classes are helpful helpers to make the life of a game programmer a little easier.
The String Converter Class
The static StringConverter class can be used to convert strings to widestrings and vice versa.
The Expected Class
The expected class is used to enhance error and exception handling within our application.
The Log Class
This class handles the logging of events. So far, in these tutorials, only a file logger has been implemented, writing out a log file of game events to the My Documents folder.
The Service Locator Class
The service locator class provides services to the entire application, without coupling anything together. So far, in these tutorials, the service locator provides a file logging service. Later, it will surely see more use, for example, when an audio service is implemented.
Here is an example of how to use the service locator to retrieve the file logger and to print information to the log file:
The Core Classes
Those are the core components of our application. They are invaluable to any Windows program.
The Timer Class
Keeping track of time is highly important for any game, especially if a mathematical simulation of a physical world is intended. The timer class does just that, by encapsulating a high-performance counter made available by Windows. It is automatically updated and used in the main game loop, and thus we do not really have to worry about timing when proceeding with the upcoming tutorials.
The following times are measured in counts, and are all updated automatically:
long long int startTime;
This is the time at the start of the application, or more precisely, the time at the moment the reset (see below) function was called last. The DirectXApp class calls the reset function at the start of the game loop (see below), and thus the start time will automatically be set to be the start of the game loop.
long long int totalIdleTime
This variable keeps track of the total time the game was idle, for example when the game is paused, or the game window is minimized.
long long int pausedTime
This variable holds the last time the game was paused.
long long int currentTime
This variable is used to store the time at the current frame.
long long int previousTime
This variable stores the time at the last inquiry before the current frame, that is, the time at the previous frame.
The following times are measured in seconds:
double secondsPerCount
This double holds the number of seconds per count, computed by the reciprocal of the frequency. It is computed at the initialization of the Timer class.
double deltaTime
This double member holds the time elapsed between the previous and the current frame, It is automatically updated and used during the game loop. It is essential for correct behaviour of the physics engine.
bool isStopped
This boolean member is true if and only if the timer is paused. It is used to keep track of the total time the game was idle.
Timer()
The constructor of the Timer class queries for the frequency of the high-performance counter, i.e. the number of counts per second, and then computes the seconds per count as the reciprocal of the frequency. If initialization fails, it throws a std::runtime_error exception.
~Timer()
As always, the constructor does what detructors do.
double getTotalTime() const
This public constant function simply returns the total running time, in seconds, of the application, that is, the total time minus the total idle time.
double getDeltaTime() const
This public constant function returns the time, in seconds, elapsed between two frames, it is used in the game loop and essential for a robust physics engine.
util::Expected reset()
This function sets the startTime of the Timer to the moment it was called. It is used just before the game loop to set the start time to be the start of the game.
util::Expected start()
This function starts the timer. It is automatically invoked each time the game becomes active again.
util::Expected tick()
This function lets the timer tick by updating the currentTime and previousTime member variables. It is automatically called at each frame during the game loop.
util::Expected stop()
This function stops the timer, it is automatically invoked each time the game is paused.
The Window Class
The Window class handles all Windows related stuff, such as creating the actual application window and handling all event messages that the operating system might send to our application. With the Window class in place, we no longer really have to care about Windows at all, we can simply program our game and forget about the operating system.
Most members of the Window class are private, but the DirectXApp, as a friend, still has direct access to all of them.
HWND mainWindow
This is the handle to the main window of our application.
DirectXApp* dxApp
This is a pointer to the main class of the application (see below).
int clientWidth and int clientHeight
Those two members store the client dimension of the window.
bool isMinimized
This boolean member is true if and only if the window is minimized
bool isMaximized
This boolean member is true if and only if the window is maximized.
bool isResizing
This boolean member is true if and only if the window is being dragged around by the mouse.
util::Expected init()
This private member function defines and initializes the main window. It is automatically called when the DirectXApp class initializes.
void readDesiredResolution()
This private function simply reads the desired screen resolution from a Lua configuration file. This function is automatically called during window initialization.
Window(DirectXApp* dxApp)
The constructor of the Window class stores the pointer to the DirectXApp class in the appropriate member variable and then calls the initialization function. If an error occurs, a std::runtime_exception is thrown. If the function is successful, the main window handle is available in the mainWindow variable. The creation of the window is started by the DirectXApp class.
~Window()
Well, the destructor does whatever destructors do, it destroys.
inline HWND getMainWindowHandle() const
This little public constant function simply returns the main window handle.
virtual LRESULT CALLBACK msgProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
Last, but not least, behold the most important function of the Window class, the message procedure. The message procedure handles all the events that Windows throws at our application, for example when the window changes in size, or if the user tries to exit the application. To change the way events are handled, this is the place to go to.
The following events are handled by the Window class at the moment:
- WM_ACTIVATE
- WM_CLOSE
- WM_DESTROY
- WM_ENTER(EXIT)SIZEMOVE
- WM_GETMINMAXINFO
- WM_KEYDOWN
- WM_MENUCHAR
- WM_SIZE
The DirectXApp Class
This is the main component of all the core components. It keeps all the other systems together. Once again, initialization is mostly automatic. We will soon see how to use the DirectXApp class to create an application of our own.
The private members of the DirectXApp class can be seen as constant variables for the entire application. The only class able to access them, is the befriended Window class. Not even the derived Game class, that we will talk about next, can access or change most of those private members.
The following member variables and functions are private, thus only accessible by the DirectXApp class and its friend class, the Window class.
std::wstring pathToMyDocuments
This wide string holds the location of the My Documents folder.
std::wstring pathToLogFiles
This wide string holds the path to the desired location to store the game log files. The default is: My Documents/bell0bytes/bell0tutorials/Logs/.
std::wstring pathToConfigurationFiles
This wide string holds the path to the desired location of the game configuration files. The default location is: My Documents/bell0bytes/bell0tutorial/Settings/.
bool validConfigurationFile
This boolean member is true if and only if a valid configuration file was present at the moment the application started. If there was no previous configuration file, the DirectXApp creates one with default settings.
bool activeFileLogger
This boolean member is true if and only if the file logger was created successfully. It is used to tell the cleanup functions whether the file logger is available to log errors or not.
bool hasStarted
This boolean member is true if and only if the DirectXApp class was initialized completely. It is used to delay taking certain actions after encountering Windows events while initializing.
Timer* timer
This is a pointer to a high-precision timer encapsulated in the above mentioned Timer class. The timer is automatically created and initialized at the initialization of the DirectXApp.
int fps
This integer holds the current frames per second, it is automatically updated during the game loop, when the frame statistics are computed.
double mspf
This double precision float holds the milliseconds it took to process the current frame. It is automatically updated during the game loop when the game statistics are computed.
const double dt
This constant double precision float is of utmost importance, as it defines the desired update frequency of the game
world. For further details, re-read the tutorial about the game loop. By default, this member is
set to
const double maxSkipFrames
This double precision float makes sure the game world is not updated too often per frame on slow computers. Re-read
the tutorial about the game loop for further details. By default, this is set to
void calculateFrameStatistics()
This private member function is called during the game loop to compute frame statistics, while doing so, it updates the fps and mspf member variables.
bool getPathToMyDocuments()
This private member function is automatically called during initialization to retrieve the path to the My Documents folder. The retrieved path is stored in the pathToMyDocuments member variable. It then creates and stores the two other path variables in the appropriate variables.
void createLoggingService()
This private member function is automatically called during initialization to start the file logging service.
bool checkConfigurationFile()
This private member function is automatically called at initialization to check for a valid configuration file. If no such file can be found, a configuration file with default settings is created.
The following member variables are protected, thus also available to derived classes.
const HINSTANCE appInstance
This is the handle to the actual instance of this application, handed to us by the WinMain function.
const Window* appWindow
This is a pointer to a constant instance of the Window class. The Window instance is automatically created at the initialization of the DirectXApp class.
bool isPaused
This boolean is true if and only if the game is paused.
DirectXApp(HINSTANCE hInstance)
The constructor of the DirectXApp class stored the instance handle of the application and initializes most member variables to their default settings. The actual game initialization must be started manually from a derived class (see below).
~DirectXApp()
The destructor destroys.
virtual util::Expected init()
This protected virtual member function initializes the DirectXApp, and as such, the entire game:
If initialization fails, it returns an Expected with a nasty runtime error inside.
virtual void shutdown(util::Expected* expected = NULL)
This protected virtual function cleans up all the allocated resources. If the application had to quit with an error, the error is printed to the log file, if possible.
virtual void onKeyDown(WPARAM wParam, LPARAM lParam) const
This protected constant virtual functions acts whenever a WM_KEYDOWN message is received, that it, it is invoked by the window procedure function in the Windows class. By default, it ends the application if the ESCAPE key is pressed.
virtual util::Expected run()
This is the heart of any game, the game loop. This functions enters the game loop and iterates until the user desires to quit the game. All the game action happens here:
virtual void update(double dt)
This function updates the game world based on a fixed frequency
virtual void onResize()
This function is invoked whenever the game window is resized, for now, the function is empty, but eventually it will call on Direct3D to resize the graphics of the game.
virtual void render(double farseer)
This function peeks into the future to render a scene of the game world, re-read the tutorial about the game loop for more details. For now this function is still empty, eventually though it will use Direct3D to render the game world.
bool fileLoggerIsActive() const
This constant function returns true if and only if the file logging service is active.
Putting It All Together
To use the power of the DirectXApp class, we create a derived class, the DirectXGame class.
The DirectXGame Class
To specify and use the DirectXApp class, a derived class must be created, like this:
DirectXGame(HINSTANCE hInstance)
The constructor must pass the handle to application instance to the DirectXApp class:
~DirectXGame()
The destructor destroys.
util::Expected init() override
To initialize the game, we can simply call the initialization function of the DirectXApp class:
void shutdown(util::Expected* expected = NULL) override
To clean up and shut down the game we must override the DirectXApp::shutdown function, like this, for example.
If there was an error, we still try to cleanup and if the logger is active, we write the actual error message to the log file. Else, we simply clean up the game.
util::Expected run() override
To run the game, to enter the main game loop, we simply call the appropriate DirectXApp function:
WinMain
All that is left is for the WinMain function to create the game class and then we are ready to rock and roll:
Note, or remember, that the shutdown function actually handles the error that might be returned from the run function.
You can download the source code from here.
And that’s it, nice, clean and easy. With all this Windows stuff abstracted and encapsulated in delightful little classes, in the next batch of tutorials, we will safely explore adding DirectX components to our application. Stay tuned!
References
(in alphabetic order)
- Game Programming Algorithms and Techniques, by Sanjay Madhav
- Game Programming Patterns, by Robert Nystrom
- Introduction to 3D Game Programming with DirectX 11, by Frank D. Luna
- Microsoft Developer Network (MSDN)
- Tricks of the Windows Game Programming Gurus, by André LaMothe