Feb 25th, 2004, 09:28 AM
Tutorial [Game loop] NMs Game tutorial 0.2
This is a short tutorial that will teach how to implement a game loop that is the most used structure in games today. The code are only tested in VB6, but is probably running fine in VB5 and can probably be easy imported to VB.NET too. You should have some understanding about loops from before, and maybe some knowledge about APIs. But don't stop reading if you don't, because I will try to explain it in an easy way anyway.
The easiest form of a game loop that is perfectly suited for your first tests when you are starting in game programming is to use a timer control. But the timer control is slow, so it has to be a better way. And it is. We are going to use a tight while loop in this example, that will run as long as you want. I will also show you a simple way to pause the loop, and how to count Frames Per Second (FPS). So let’s dig into it.
Last edited by si_the_geek; Feb 27th, 2004 at 11:29 AM.
Feb 25th, 2004, 09:29 AM
The global declarations:
We are going to use the GetTickCount API for this loop, to make it a fixed frame count. The GetTickCount API gives us the time in ms after the computer was turned on. The resolution of the API is more then enough for newer OSes, but remember that if you use them on older OSes like Win95 and Win98, it can have as low resolution that your game will run really slow. But don't think about that right now. Another problem you might have to consider if you are making a big game is that the time is getting wrapped after 48 hours. So if the computer you are using the app on have been on for 2, 4, 6 or so on days it will suddenly mess up the whole thing. There are some work arounds about this, but we are not going to talk about them here. If you get to that point that you need a workaround, you will probably figure it out by your self.
So back to the declaration. It is more or less Copy Paste when you first know what API to use. The declaration that you need to use to be able to call the GetTickCount API in Vb looks like this. And goes in the General Section of your code.
Private Declare Function GetTickCount Lib "kernel32" () As Long
Doesn't look that bad, does it? It has no parameters, so you can use it just by writing GetTickCount(). But we need some more variables too. They have to be global too, so we can check them where ever we are in the game. We need one to check if the game is paused, and one too see if it is still running. So we just declare them as Booleans.
Dim Pause As Boolean 'check if game is paused
Dim Running As Boolean 'check if game is started
Last edited by si_the_geek; Feb 27th, 2004 at 11:30 AM.
Feb 25th, 2004, 09:29 AM
Starting the app:
We are setting all the variables that we need in the FormLoad event. In a real game, you would probably use your own Sub, for that. But for this small example we are not loading any big pictures and sound tracks. So we will just do the small initializing in the FormLoad event.
Me.Show 'show the window
Running = True 'no the game is on
Main 'start main loop
The first line makes the form visible. That is because if you have a lot of hard work here, your app will sometimes not show before all the work is done. The second line tells the app that it will be running right away. And the third line is calling a function that I have called Main. We haven’t made that function yet. But it is going to be where the game loop is located.
Last edited by si_the_geek; Feb 27th, 2004 at 11:31 AM.
Feb 25th, 2004, 09:30 AM
Now we can finally start on the actual code for the game loop. We are starting by setting up some variables.
Dim TempTime As Long 'to keep track of time
Dim Fps As Double 'current fps
Dim FrameCount As Long 'count frames between fps update
The first one will hold the time for the next loop to occur. We will use GetTickCount to update it. The second one will hold the last time we checked the frame rate. We will use that to update the frame rate every second. The third one will hold the actual frame rate that second, and will be updated every time the game loop is called.
Now we will start the actual game loop. We will use a Wend loop to do this. We are checking the Running variable that we made earlier to see if we should still call the game loop. We do that like this:
While Running 'is the game running?
This loop will run as ling as Running is true. And inside this loop the rest of the game loop will be coded. So now we have to test if it was really time to call the game loop. Or if the computer was to fast and we have to wait a bit. We can do that in an If test.
If TempTime <= GetTickCount Then 'is it time to calc next frame?
The TempTime variable holds the last time we executed the actual update of the game. The first time TimeTemp will be 0, so it this test will always be true the first time. The right side we are calling the GetTickCount API that gives us the time since the PC was turned on. since that must be more then 0, it will go through the first time. But after it has gone through this test we have to update the TempTime, so it will not be 0 the next time. We can do that by writing inside the if statement:
TempTime = GetTickCount + 10 'set next tick time
This will give the TempTime variable the time now plus 10ms. So the next time the If test will test if it has gone 10ms since last time it was called. If not the test will be done again later, to see if it has used enough time. We are doing this so it should work at the same speed on all PCs. If you are adding too little time it might be slower on old PCs then to your PC, so be sure to not add to little time.
When we first are in the game loop we can check if the game was paused. If it is, we will not draw more frames for now. We can do that too using a small loop like this:
While Pause 'is the game paused?
DoEvents 'infinite loop
Pause is set to be false by default. So the first time it will not enter the loop. We can change this later using keys. As you see this is a pretty tight loop. And will just go round and round if Pause is true. And do nothing more. I have did put a DoEvents in the loops, so the app has the time to check for other messages during the loop such as we are pressing the pause key again, so the pause loop can end.
After this you can write the game code as you want. Usually this is done by calling functions. So a big game won’t look messy. So it could look like something like this.
'And so on
But it the example project I have only written a small comment about it.
Now it is time too look at the frame count. We have to add one more to the FrameCount variable every time we get here. And we have to check how long we have been counting the frames. We want it to be FPS so that means that for every 1000ms we need to start at zero again and print the frame count. I am writing it in the caption of the form, so you can see it there in the example project.
FrameCount = FrameCount + 1 'fps counter
If Fps + 1000 <= GetTickCount Then
Me.Caption = "Fps: " & FrameCount 'Printing the actual frame count
Fps = GetTickCount
FrameCount = 0
The first line will add one more to the frame count. The second line will test to see if it has been more then 1000ms since the last frame count output. So the left side will hold the last time we did output it in the FPS variable, and then we are "adding" 1000ms to it, to check against the right side. The right side will return the actual time now. If it is more then a second since last FPS output the If test will be true, and if will go into the If statement. It will print "FPS:" and then the actual frame count. We could get a more accurate frame count by using this code in stead of just printing FrameCount, but it isn't that big a difference.
Round(1000 / ((GetTickCount - Fps + 0.00001) / FrameCount), 0)
Then after that is done, we are updating the FPS variable with the exact time now. So it will be 1sec to the next time we output the frame rate. And we have to remember to set the FrameCount to zero again, so we are not adding the FrameCount from the last output too.
Then we had finished our game if statement in the game loop. And are out in the outer loop again. We are adding one more DoEvents to it, so the app gets all the messages that it need to perform all the tasks like keypresses. And then the game loop is finished.
Last edited by si_the_geek; Feb 27th, 2004 at 11:34 AM.
Feb 25th, 2004, 09:31 AM
Since there is no other way to close this form now then pressing the X, we have to be sure that the game loop ends. We are therefore putting a Running = False in the Forms QueryUnload event. You will usually end the loop in another way in a real game. Like when you are heading out in the main menu, or after checking the Win/Loss condition of a game.
The last thing for this app now is to make sure it is possible to pause it. We can do that using the forms KeyDown event.
If KeyCode = vbKeyP Then 'pause code
If Pause = False Then
Pause = True
Me.Caption = "Paused"
ElseIf Pause = True Then
Pause = False
Just remember to have the KeyPreview property of the form to true to make this code work. If you are pressing the p button of the keyboard this if test will be true. And it is setting the pause variable to true. This will make it go into the tight loop we made in the game loop. It will then write Paused in the caption of the form, and nothing more will happen. At least until you press the P button again. Then the While condition in the game loop will be false. And the game loop is running again.
That was all. I hope you have learned something, and remember there is many ways to do this. This is one of them, and will work satisfying for most games. Some would probably use the Sleep API to give some CPU time to other programs running in the background. But that is things you can dael with your self later if you need it. The complete source code for this app you can download here in the attachment.
Thanks too Cyborg for the code. I was too lazy to write it my self, so it is actually his code. I just wrote the tutorial since it was so close to the game loop I used in my VB games.
Last edited by si_the_geek; Feb 27th, 2004 at 11:36 AM.
Feb 27th, 2004, 11:36 AM
The files within this thread (submitted: 25 Feb 2004) have been checked for malware by a moderator.
Disclaimer: This does not necessarily mean that any compiled files (DLL/EXE/OCX etc) are completely safe, but any supplied code does not contain any obvious malware. It also does not imply that code is error free, or that it performs exactly as described.
It is recommended that you manually check any code before running it, and/or use an automated tool such as Source Search by Minnow (available here or here).
If you find any serious issues (ie: the code causes damage or some sort), please contact a moderator of this forum.
Usage of any code/software posted on this forum is at your own risk.
The above posts have been edited (with NoteMe's permission) to correct spelling/grammar errors.
Click Here to Expand Forum to Full Width