Introduction
The aim of these tutorials is, initially, to show you how to create a simple game, from scratch, without the help of higher level APIs like XNA or DirectX which automate half the process for you. All we will be using is a Windows Form and GDI+ to do some basic drawing, and for a little extra convenience, a few of the Form's events.
Background
There will be a few slightly odd things with the game loop and the flow of logic; this is because Windows Forms are event based, while on the whole, games are generally not. I will point out the differences there may be between some 'regular' code and the code that must be used for the Windows Form.
Step 1: Setting Up The Game
A Basic Loop
Since games are not usually event driven, we use a loop and perform any logic and drawing from this loop. So, in each loop, we will perform the game's logic, check for input, and draw graphics to the screen. Look at the below code. Can you see any problems?
bool runGame = true;
while(runGame)
{
GetInput();
PerformLogic();
DrawGraphics();
}
If you noticed that the game will run as fast as possible, then well done. With this loop, the game will run as fast as it can, which could be anything from 1fps to upwards of 1000fps. The speed of the game may vary as enemies or objects are added to the game, and the game may simply run too quickly for a player to react in time.
It's All About Timing
We need to control the speed at which the game runs; for this we use a timer. The timer will set a flag so that we know when to run the logic in our main loop; that way the game will run at a much more manageable pace.
Timer MainTimer;
MainTimer.Interaval = 1000/60;
bool runGame = true;
volatile bool doStuff = false;
Main()
{
while(runGame)
{
if(doStuff)
{
GetInput();
PerformLogic();
DrawGraphics();
doStuff = false;
}
}
}
Timer()
{
doStuff = true;
}
We Can Do Better Than That!
Although this will limit the game's speed to an acceptable level, there is still more we can do. Drawing graphics to the screen is usually the slowest part in a game, so if the scene takes too long to render, it will cause the logic to run slowly and the whole game will slow down. To avoid this problem, we need to separate drawing the graphics from the rest of the logic, and then we need to make sure that the logic gets priority over rendering the scene. This means that if the logic is falling behind, we should not update the graphics during that loop, so we drop the frame.
This is the setup that we will be using to manage our game:
Timer MainTimer;
MainTimer.Interaval = 1000/60;
bool runGame = true;
volatile uint speedCounter = 0;
Main()
{
while(runGame)
{
if(speedCounter >0)
{
GetInput();
PerformLogic();
speedCounter--;
if(speedCounter == 0)
DrawGraphics();
}
}
}
Timer()
{
speedCounter++;
}
In this loop, the logic will only run if the counter is greater than zero. After all of the logic has been run, we decrease the counter by one, and if the counter is back at zero, we will update the scene. If the entire loop takes longer than one frame (the interval of the timer), then when we decrease the counter; it will still be greater than zero, so we drop the frame and see if we can get back some time.
Changes For A Form
If we use a while
loop on our Windows Form, then Windows will never update the screen, and the application will hang. For this reason, we will be putting the logic within the callback function of our timer, for now anyway - later on, we will put it in its own thread. Apart from this, everything will remain the same. The final code will look similar to this:
bool drawGraphics = false
void TimerElapsed()
{
speedCounter++;
GetInput();
PerformLogic();
speedCounter--;
if(speedCounter == 0)
drawGraphics = true;
}
We also need to override the OnPaint
and OnPaintBackground
methods so that Windows will not do any drawing itself and we can handle it ourselves. Remember, we will not be calling base.OnPaint
or base.OnPaintBackground
.
protected override void OnPaint(PaintEventArgs e)
{
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
GDI+
Since we will be using it to draw our graphics, I thought it best we go over how to use it. To start drawing, we need a few things:
- A graphics object (this will do the drawing).
- A surface to draw onto.
- A pen or brush to draw with - no, really.
If we override the OnPaint
method of our form, we can use the Graphics
object in the PaintEventArgs
which already has the form as the surface. You can then use the methods in the Graphics
object to draw onto the screen:
override void OnPaint(PaintEventArgs e)
{
Brush myBrush = new SolidBrush(Color.Red);
e.Graphics.FillRectangle(myBrush,0,0,100,100);
Pen myPen = new Pen(Color.Green);
e.Graphics.DrawLine(myPen, 50, 250, 65, 15);
}
The Demo
The demo contains the game loop we've been talking about, plus a little drawing code so we can see things on the screen.
What? Homework!
Before I write up the next article (or before you just go and read it), why not try to edit the demo so that the circle moves around on the screen? Oh come on, it's not that difficult, I know you can do it if you try.
Coming Up
Well, that's all I've got time for. But on the bright side, now that we have our basic game code, we can start to do stuff with it. Coming up in the next article:
- Double Buffering
- Getting Input
- Setting Up An FPS Counter
- Drawing And Animating A Sprite
What Do You Want?
Feel free to post about what you'd like to see in these articles. I am here trying to help, so if I know how to do what you are asking, I'll try and include it. These articles are heading through the world of 2D games, and up into the realms of 3D - though we might have to hop over to C++ and OpenGL for that one. I've never much liked DirectX, and I've never used XNA for 3D. Remember, I'm not trying to give you code to make a game, but showing you how to construct code that can be used to make a game - if you follow, the language shouldn't be so important.
History
- 8th May 2008 - Article submitted.