Please note that I don't update this web site anymore. You can visit my new personal blog at www.williamwilling.com.
Writing A Basic Game Engine
Thursday, July 21, 2005
Rejoice! An article about programming! :-)
I have no idea at what skill level you are when it comes to game programming. I'm going to assume you know your way around C++, so that I can focus entirely on the topic at hand. If I'm going to fast for you, just leave a comment and I'll explain as best I can. And if this stuff bores you: a future article will be of more interest to you, so just fast forward in time. Or go do something else for now.
Just about every game follows the same basic structure. First, you read the player's input: keys pressed, movement of the mouse, stuff like that. Depending on what the input actually is, you send instructions to the game world. Second, you update the game world. This ensures that things that don't rely on user input get simulated as well: enemies, landslides, time of day. And finally, the scene is rendered to the screen, so the player can actually see what's happening.
It would be a dull affair if the game would end here. It'd be short, too. So, we need to repeat these steps again and again. And that is why this is called the game loop.
Before the game start, we usually need to run some initialization code, setting up libraries that we use and getting the game world in the proper state. When it's all done, we shut everything down proper. And that's how a game works, basically.
Creating an engine
While the above (pseudo) code is fine, we can make it a bit nicer by putting it all into the game engine. The game engine is a fairly simple creature; you only need to be able to start it and, in time, to stop it. (Pausing the game engine is a topic for another day.) After you start the game engine, it enters the game loop and drives your game from there.
First, let's start our engine. I prefer to keep my
main() function simple. All it has to do is start the game engine and catch any unhandled exceptions so we can display an error message or write something to the log.
catch (std::exception& e)
std::err << e.what();
Note the call to the static member function
Start(). Although there is only one place where you start the game engine, there are usually quite a few places from where you want to stop the game engine. That means that you need easy access to it. Passing around an
engine reference can become quite cumbersome. A singleton is an option, but I like the above syntax better and we don't need lazy instantiation.
// this is what it looks like using a static member function
// using a singleton is slightly more contrived
You can implement the
GameEngine class entirely with static members. It's not necessary, however. As long as you make your public members static, you can implement all private members as instance members. The code below shows how to implement this. As far as I know, there is no real difference between the two options (especially since we don't need inheritance).
static GameEngine engine;
static void Start();
You have already seen the code for the game loop, so the implementation of
Run() won't be much of surprise. The only addition is the
isRunning variable. The code at the beginning of the article had the game loop running indefinitely. Obviously, we want to be able to stop our game at some point. A boolean is all it takes.
isRunning = true;
Stopping the game engine is easy: just set
false. Since we should be able to stop the game engine from outside the
Stop() needs to be public and static.
engine.isRunning = false;
Initialization and shut down
This will go fast, so pay attention. Use the constructor for initialization code and the destructor for shut down code. There.
The game loop still contains calls to functions that don't exist yet. Normally, you'll want to implement these functions in another class than
GameEngine and that means they are outside the scope of this article. Next time, Gadget, next time.
Back to blog index
Great post! I can't wait for the next one.
I have seen that using a Singleton like I have can just make the game engine's functions harder to use. I have since tried to implement the game engine as a regular class, but found it awkward to pass a pointer to the engine in different methods. Your method looks fairly clean and easy to use so I'll likely adopt it myself.
When you say that Process(), Update(), and Render() are implemented in another class, do you mean that GameEngine has these functions and they simply pass through to another set of classes to do the real work, or that those functions shouldn't actually be part of GameEngine in the first place?
Thursday, July 21, 2005 6:33 PM
Those functions shouldn't be part of GameEngine. Instead, they should go in the class that holds your game state. My next article (which I'll probably post tomorrow) talks about the implementation of these three functions. A discussion on game states will have to what for yet another article, though.
By the way, thanks for mentioning me on your blog. :-)
Thursday, July 21, 2005 9:14 PM
Hey, what are blogs for? B-)
Thursday, July 21, 2005 11:08 PM
Thanks for this great post. I look forward to the rest of the series.
It's interesting to note that your GameEngine class and methods look very similar to a WorkFlow / Business Rules engine I'm working on in C# and .NET for a client of mine.
Friday, July 22, 2005 2:28 PM
Tell me what you think
Since I'm not updating this site anymore, I disabled comments. You can visit me at my new site: www.williamwilling.com.