The Basic Idea
The way that games are made on a computer is not much different than the way a movie is shot on film. A film reel contains thousands of still photos that when viewed in quick succession make you believe that what you''re seeing is actually moving. For this effect to take place, movies are shown at somewhere between 24 and 30 frames per second. The same principle exists for games -- a game prepares frames of animation and displays them one after the other rapidly enough to give the impression of constant movement. One difference here though is that with a game, each frame is drawn taking into consideration the input from the player, the physics of the game environment, logic, AI and other factors, before the results are placed onto the screen.
Do you remember that
Game_Main() function from the previous article? It’s a function that’s got its foot in the message loop of the Windows application, and consequently gets called repeatedly. If the purpose of
Game_Main() was to generate one frame of animation, well then, wouldn’t that fit in nicely?
That’s exactly what’s going to happen. So long as
Game_Main() gets called a decent number of times per second, and each iteration of
Game_Main() prepares and displays a single frame, we’ve got a game on our hands.
Like any program, our game would like an opportunity to initialize itself at startup, and tear everything down at closing time. For this facility, we turn back to
WinMain() and notice that there’s a perfect place to call an initialization function before the message loop, and a termination function before
WinMain() has completed its execution (which is the end of the Windows application). What we end up with is something like this:
- Register window class
- Create a window
- Call Game_Initialize()
- Enter Game Loop – this calls Game_Main()
- Call Game_Terminate()
- End of application
At this point, it’s starting to look like a real Windows game template! If we tuck all of this Windows code into a separate file and supply prototypes to the three game-related functions, we can reuse this code by inserting appropriate
Game_Initialize(),
Game_Main() and
Game_Terminate() functions.
When you think about it, the contents of
Game_Initialize() and
Game_Terminate() depend entirely upon what’s required by the code in
Game_Main(). Therefore, that’s where we’ll start our little foray.
Inside the Game Engine
What we do know is that
Game_Main() needs to end up drawing a frame of animation to the display. What we don’t yet know is how to actually accomplish this. This article is going to focus on a generalization of events, and successive articles will ramp you up to an actual implementation method,
so hold onto your pants.
Get it out of your head that there’s a single correct way to write a game. However you manage to alter the colors of the pixels on the display is your business – if it works, it works. Unfortunately, what works doesn’t necessarily work well. We’d like to be able to take advantage of the trials and tribulations of others and apply a little hindsight, and learn about what works well. In other words, a little conformity can be a good thing – we’ll jump off the bandwagon when it’s time to get creative.
First off, it’s important to realize that the screen you’re staring at right now is a reflection of a piece of memory somewhere in the video card. Every time the video card draws a screen, it’s taking a look at this memory buffer for pixel-color information. We’re definitely going to want some control here. Assuming you had a function that changed the color of a pixel by manipulating a location in this memory buffer, you’d be very disappointed at the performance hit you’d be taking. It turns out that there’s overhead involved in requesting access to the video card’s display memory, locking the memory for writing, altering the appropriate pixel’s color value and relinquishing control back to the video card.
A better approach is to create a memory buffer that’s the same size as the display’s video memory, and use this ‘working copy’ to assemble the frame ahead of time. Once ready, you copy the entire contents of this buffer directly to the display’s video memory, and
voila – your image appears on the display.
There are variations of this technique, and although I can tell you we won’t be doing things quite this way, the concept of an off-screen memory buffer is consistent. That’s all you need to know at the moment.
Preliminary Reconnaissance
Alright, so let’s move our assumptions forward: pretend that we now know how to take a memory buffer, alter its contents as to create an image for the current frame of animation, and have this buffer copied into the display’s video memory. Where does that leave us? Let’s compare notes:
(once per frame = one iteration of
Game_Main() )
- collect input from the user
- process user input (update internal data)
- perform physics, logic, AI
- play sounds
- prepare the current frame
- render the current frame to the display
It would seem that there are two types of tasks that
Game_Main() is responsible for – those that interact with hardware, and those that manipulate data. Re-arranging this list a little, we get
External
- collect input from the user
- play sounds
- render the current frame to the display
Internal
- process user input
- perform physics, logic, AI
- prepare the current frame
Take a good look at these two lists. You may have thought coming into this tutorial that the External list was representative of game development, when in reality it’s those items on the Internal list that count – straight coding that you’re already capable of doing. Once we can get our external requirements out of the way, we’ll have the resources available to implement code for any game we can imagine. You can almost consider these external items as ‘technicalities’, having no real basis in game development whatsoever – this also emphasizes what I was saying about your choice of programming language being design-independent. It’s only when we try drilling at the game’s internal elements that we need to choose our tools and apply some ‘elbow grease’.
Looking at the Road Ahead
I hope I’ve convinced you that we need to learn input, display and sound output techniques before we can truly turn our attention towards the ‘brain’ of the game itself. We’re trying to get ourselves acquainted with the programming tools so that we can use them later, not fumble with them. The rest of the Ladder series is devoted to integrate a sufficient amount of each of these areas into your working set of skills. Let me emphasize the word ‘sufficient’ – we’re not trying to learn more than we need to. As is with many things in life, game development works on a need-to-know basis. When you need to do something in particular, you’ll know when it’s time to summon up the reference documentation…
Please reply to this article with your comments and questions.