Tools Links Login

Making games using the Win32 api

This is an complete tutorial on how to make a game using the Win32 api. The pictures wouldn't work so download the zip. Please leave a comment to tell me what you think of it.

Original Author: Dennis Meelker

Code

style='font-size:24.0pt;mso-bidi-font-size:12.0pt'>Making games using the Win32
api.


style='font-size:16.0pt;mso-bidi-font-size:12.0pt'>By Dennis Meelker


style='font-size:16.0pt;mso-bidi-font-size:12.0pt'>Meelkertje@hotmail.comclear=all style='mso-special-character:line-break;page-break-before:always'>


In this tutorial I will show you how to make a game that
runs fast using the Win32 Api. I will try to explain everything as good as
possible, so if you don't understand something read it over and over until you
get it, got it? If you really can't understand it you can always e-mail me.Now
lets get started.


 


So why should we use the Win32 API instead of using DirectX,
I personally think DirectX is way to hard to learn if you just want to make a
flat type game. I you want to make a 3D shooter with all effects like anti
alias and stuff, you will need to learn DirectX for sure, there is just no way
you can do this in VB using only API's. But if you want to make a flat game
like Pacman or some kind of Platform I prefer using the API. But this is just
my opinion so if you want to use DirectX go ahead.


 


First we will make a new project just create a standard exe,
now name the form frmMain or something like that. This will be the game form. Now,
the next two things are really important, set the AutoRedraw property to True
and set The Scalemode to Pixel.


When Autoredraw property is set to true the things that our
game drew on the form won't just disappear when the form is refreshed. We set
the Scalemode to pixel because the API's all need pixels as parameter and not
twips, if we hadn't changed it we had to turn the twips into pixels all the
time witch would cause trouble for sure.


 


Now that we have our form ready create a new module and call
it something like modInvaders orso. Now add these api Declaration's to the
module:


 







Public Declare Function BitBlt Lib
"gdi32" Alias "BitBlt" (ByVal hDestDC As Long, ByVal x As
Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal
hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long)
As Long


 


Public Const SRCAND = &H8800C6 style="mso-spacerun: yes">  ' (DWORD) dest = source AND dest


Public Const SRCCOPY = &HCC0020 ' (DWORD) dest
= source


Public Const SRCPAINT = &HEE0086 style="mso-spacerun: yes">        ' (DWORD) dest = source OR dest



 


As you can see, we just added the Bitblt api function to our
project, the bitlbt function will be the core of our game, it is used to draw
all the graphics on the screen. I will explain all the parameters here:


 


hDestDC          - This
is the DC of the form/control to draw to.


x                      -
This is the x position to draw to


y                      -
This is the y position to draw to


nWidth             -
This is the width of the picture or a part of a picture to copy


nheight              -
This is the height of the picture or a part of a picture to copy


hSrcDC            -
This is the DC of the form/control that containt the picture we want to copy


xSrc                 -
The source x coordinate


ySrc                 -
The source y coordinate


dwRop             -
This specifies how the graphic should be drawn


 


When you see this you will probably say, “Dennis? What is a
DC?”.


So I will explain that right now, DC stands for device
context, it's just a place inside the memory of your PC where the picture of a
form or any anther control is stored. Now if you didn't know what a DC was you
probably won't know what the dwRop property is, well, as I said above it tells
the BitBlt functions how to draw, “Are there different way's to draw??”, yes
there are, you noticed the three constants below the BitBlt function that we
added to our module those are three ways to draw, you can just set the dwRop to
SRCCOPY or any of them. The SRCAND and SRCPAINT are almost always used
together, I don't know what they do exactly but I do know that when you use the
SRCAND first and the SRCPAINT after that you can get a transparent picture, you
probably don't understand what I just said, never mind, I will explain this
later on. The SRCCOPY constant is nothing special, it just copies the part of a
picture you specified to the specified DC.


 


If we are making a good game, we want the area surrounding
our characters to be transparent, if it would not be transparent, below are two
examples of a character, the first one has a transparent background, the second
one doesn't.















src="./Making%20games%20using%20the%20Win32%20api_files/image003.jpg"
v:shapes="_x0000_s1027">
src="./Making%20games%20using%20the%20Win32%20api_files/image004.jpg"
v:shapes="_x0000_s1026">

style='mso-ignore:vglayout' clear=ALL>
 




src="./Making%20games%20using%20the%20Win32%20api_files/image006.jpg"
align=left hspace=12 v:shapes="_x0000_s1028">I think you will now
understand why we want the background of our character transparent. To make the
background of our pictures transparent we have to create a mask. On the right
you can see a picture that is ready to be drawn with a transparent background.
The first picture has a black background, the black will be transparent, now
you will probably think: “But his arms have black stripes, wont he get
transparent arms?”, to solve that problem I created a “Mask” a mask is a
picture with everything that should be transparent white, and the rest black.
With these two images and the BitBlt function we are ready to draw the
character with a transparent background.


 


Before we go on, make sure you have a picture like the one
above,  you can also use the one above
if you want to.


 


Now add a Command button and a picturebox to the form set
the picturebox's autoredraw property to true, the scalemode to Pixel, the
borderstyle to zero and set the Autosize property to true. Now load your
picture in the picturebox by setting the picture property.


 


Doubleclick on the button you just inserted and add the
following code:


 







BitBlt me.hDc, 0 ,
0 , 20, 20, picture1.hDc, 0, 0, SRCCOPY


Me.Refresh lang=EN-US style='mso-ansi-language:EN-US'>



If your
picture has other sizes you must change them, but watch out, you have to use
the sizes of one part of the picture, the picture I showed you has a width of
40 pixels and a height of 20, it consists of two pictures that are put together
so the sizes of one picture are 20x20.


 


If you
start the program and you press the button you will see you character with a
black background. Now that you know how to use the SRCCOPY constant we will go
on with the other two. Add another Command button and set it's code to:


 







BitBlt me.hDc, 0 ,
0 , 20, 20, picture1.hDc, 20, 0, SRCAND


BitBlt me.hDc, 0 ,
0 , 20, 20, picture1.hDc, 0, 0, SRCPAINT


Me.Refresh lang=EN-US style='mso-ansi-language:EN-US'>



 


When you
press this button you will see your character with a transparent background!!


 


 


The first thing you should now when making a game is that
you should use as less timers as possible , timers are really bad things to use
in a game. Now I hear you thinking things like: “But how can I move the bullet
my spaceship just fired without a timer?”, the solution is:… Loops!. “Loops??”,
yes loops, because a game usual needs to time a lot using timers will only make
your game run slow, and that is the most worst thing to have, imagine you made
a great looking game with killer graphics, but it runs soooooo slow because you
used about two dozen timers. We wont have this kind of trouble, cause we are
using loops!!


 


Well, with the most games you will have a main loop, a main
loop is a loop that runs your game, when the loop stops.. the game stops. In
this loop we will check for keypresses and we will move our characters, we will
move bullets, draw the players and powerups and so on. A simple loop would look
like this:


 







Do


   ‘Game
Stuff 


   DoEvents


Loop



 


But if we put all the stuff I mentioned above in here and
start our loop you will notice it goes way to fast, we will need to slow our
loop a bit down. Now I will change the loop like this:


 







Const
TickDifference as long = 10


Dim
LastTick


 


LastTick =
GetTickCount()


 


Do


style="mso-spacerun: yes">   Curtick = GetTickCount()


style="mso-spacerun: yes">   If 
Curtick – LastTick > TickDifference then


 


style="mso-spacerun: yes">      ‘Game Stuff


 


style="mso-spacerun: yes">   End if


style="mso-spacerun: yes">   DoEvents


Loop



 


The first thing you will probably see is the GetTickCount
function, if you never worked with the API before you will probably don't know
what it does. So I will tell you, the function GetTickCount returns the amount
of milliseconds that elapsed since windows has started. So if you look at the
rest of our loop you will see that we first get the current tick and store it
in the LastTick variable. Then we start the loop and we store the current tick
in the CurTick variable. Now comes the important part, we check if the
difference between CurTick and LastTick is ten, if it is the game stuff will be
executed. So if we make our loop this way, every ten milliseconds the game
stuff will be executed, this gives you game a speed of 100 Fps!! The
declaration of the GetTickCount is so:


 







Public
Declare Function GetTickCount Lib "kernel32" () As Long



 


For the people that don't know the DoEvents command, I will
explain it here. The DoEvents command is really important in our main loop, the
DoEvents command lets the pc do things like updating the screen, if we let it
out of our loop you would not see anything happen because the pc hasn't any
time to redraw the screen, so it stays empty.


I will now tell you how to obtain keypresses. If you ever
used the keydown event with a game you probably noticed that if you hold a key
down, your character first goes forward one step, then it pauses and then it
goes on. It's very simple…”We don't want that!” so we won't use any event, we
will use the GetKeyState API, it's declaration is as followed:


 







Public Declare Function GetKeyState Lib
"user32" (ByVal nVirtKey As Long) As Integer


 


Public Const KEY_DOWN As Integer = &H1000 style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>



 


The only parameter this functions has is the nVirtKey, this
is the key you want to check. I've included the KEY_DOWN constant witch is
needed to check for a keypress, if you want to check if the space bar is
pressed you simply use this code


 







If GetKeyState(vbKeySpace) and KEY_DOWN then


  
‘Statements


End If



 


When creating a game you should make a function called
something like: GetUserInput or something like that, it would also be handy if
you declared a Boolean variable for every key so you can use it inside your
whole game, the sub would look like this:


 







Public Function GetUserInput()


  
UpPressed = GetKeyState(vbKeyUp) And KEY_DOWN


  
DownPressed = GetKeyState(vbKeyDown) And KEY_DOWN


  
LeftPressed = GetKeyState(vbKeyLeft) And KEY_DOWN


  
RightPressed = GetKeyState(vbKeyRight) And KEY_DOWN


End Function



 


If you call the newly created function inside our main loop
we can check for keypresses everywhere in our game.


 


If you want the keys to customisable you could also declare
a long for every key, then you should also make something like a InitKeys sub
in witch the variables will be loaded with the right keycodes, you can then let
the user choose his own configuration, the code would look like this:


 







Public Sub InitKeys()


   UpKey =
vbKeyUp


   DownKey
= vbKeyDown


   LeftKey
= vbKeyLeft


   RightKey
= vbKeyRight


End Sub


 


Public Function GetUserInput()


  
UpPressed = GetKeyState(UpKey) And KEY_DOWN


  
DownPressed = GetKeyState(DownKey) And KEY_DOWN


  
LeftPressed = GetKeyState(LeftKey) And KEY_DOWN


  
RightPressed = GetKeyState(RightKey) And KEY_DOWN


End Function



 


On to the next subject, Sound, without sound, a game is
usual boring, so you need sound. You can download sounds from various websites,
you can also record the yourself using a microphone, if I need a simple sound
like a beng, I just put my mirophone near my desk and punch on the table. If
you have your sound saved as a .wav file you can play it two ways, you can use
the mci control Microsoft made. And you can use the sndPlaySound api, we will
use the sndPlaySound api, because using the control only makes your game slower
and bigger because you will need to include a ocx of 150 kb. De declaration of
the sndPlaySound stands below:


 







Public Declare Function sndPlaySound Lib
"winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As
String, ByVal uFlags As Long) As Long


 


Public Const SND_ASYNC = &H1


Public Const SND_LOOP = &H8


Public Const SND_NODEFAULT = &H2



 


As you can see the sndPlaySound function has two parameters,
ipszSoundName and uFlags. The ipszSoundName is the filename of the .wav file to
play, uFlags can be set to various settings. I explain the setting below:


 


SND_ASYC – The file is played and the program continues, if
you don't use this one the program waits until the sound is done.


 


SND_LOOP – The sound will be looped, if you want to stop the
sound just call the sndPlaySound function again, with no file specified.


 


SND_NODEFAULT – When this flag is not set, the system
default beep will sound if the given file can't be found. When you set it there
just won't be sound. It's smart to always use this flag when creating a game.


 


In most games you will only use the SND_ASYNC and the
SND_NODEFAULT flags, therefore I always create a function called PlaySound,
like this:


 







Public Function PlaySound(sFileName as string)


  
SndPlaySound sFileName, SND_ASYNC + SND_NODEFAULT


End Function



 


To play a sound just call the PlaySound function. Because
the sound is usual in the map of the game I always create a function to add a
to the app.path variable if necessary, I do so because else, if I always add a
the path can become something like c:\, and that wont work. The new code
will then be.


 







Public Function FixPath(sPath as string) as string


   If
Right(sPath,1) = “” then


     
FixPath = sPath


   Else


     
FixPath = sPath & “”


   End If


End Function


 


Public Function PlaySound(sFileName as string)


  
SndPlaySound FixPath(App.Path) & sFileName, SND_ASYNC +
SND_NODEFAULT


End Function



 


This way you only need to specify the filename, the program
will then automatically add the directory in witch the game is installed.


 


Okay, now that you now the basics ( if you don't know the
basics, just read it again ) we will make a small game in witch you can walk
around a character. Create a new project, make a button that says “new game” on
the first form. Add a new form, set up the form as we did earlier. Make a
module with all the declarations and constants we talked about, they are below:


 







Public Declare Function BitBlt Lib "gdi32"
Alias "BitBlt" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As
Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long,
ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long


 


Public Declare Function GetKeyState Lib
"user32" Alias "GetKeyState" (ByVal nVirtKey As Long) As
Integer


 


Public Declare Function sndPlaySound Lib
"winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As
String, ByVal uFlags As Long) As Long


 


Public Declare Function GetTickCount Lib
"kernel32" Alias "GetTickCount" () As Long


 


Public Const SRCAND = &H8800C6


Public Const SRCCOPY = &HCC0020


Public Const SRCPAINT = &HEE0086


Public Const KEY_DOWN As Integer = &H1000


Public Const SND_ASYNC = &H1


Public Const SND_NODEFAULT = &H2



 


Also add some declaration to the module, just put them at
the bottom:


 







Public UpKey as long


Public DownKey as long


Public LeftKey as long


Public RightKey as long


 


Public UpPressed as boolean


Public DownPressed as boolean


Public LeftPressed as boolean


Public RightPressed as Boolean


 


Public PlayerX as integer


Public PlayerY as integer


 


Public TimeToEnd as boolean



 





 


Now create a new sub in the module called MainLoop, you can
also copy it from below


 







Public Sub MainLoop()


   Const
TickDifference as long = 10


   Dim
LastTick


  


   PlayerX
= 0


   PlayerY
= 0


 


  
TimeToEnd = False


 


   InitKeys


  


   LastTick
= GetTickCount()


 


   Do until
TimeToEnd


      If
GetKeyState(vbKeyEsc) and KEY_DOWN Then TimeToEnd = True


     
Curtick = GetTickCount()


     
If  Curtick – LastTick >
TickDifference then


 


        
GetUserInput


 


         If
UpPressed Then


           
PlayerY = PlayerY -4


        
End if


 


         If
DownPressed Then


           
PlayerY = PlayerY + 4


        
End if


 


         If
LeftPressed Then


           
PlayerX = PlayerX - 4


        
End if


 


         If
RightPressed Then


          
PlayerX = PlayerX + 4


        
End if


        
‘Check if the player is still in the screen


         If
PlayerX < 0 Then PlayerX = 0


         If
PlayerX > 100 Then PlayerX = 400


        


         If
PlayerY < 0 Then PlayerY = 0


         If
PlayerY > 100 Then PlayerY = 400


        


        
‘Draw the player


        
Draw Player


      End
If


      ‘Let
the pc do its stuff


     
DoEvents


   Loop


End Sub



 





 


Add the two subs InitKeys and GetUserInput to the module:


 







Public Sub InitKeys()


   UpKey =
vbKeyUp


   DownKey
= vbKeyDown


   LeftKey
= vbKeyLeft


   RightKey
= vbKeyRight


End Sub


 


Public Function GetUserInput()


  
UpPressed = GetKeyState(UpKey) And KEY_DOWN


  
DownPressed = GetKeyState(DownKey) And KEY_DOWN


  
LeftPressed = GetKeyState(LeftKey) And KEY_DOWN


  
RightPressed = GetKeyState(RightKey) And KEY_DOWN


End Function



 


src="./Making%20games%20using%20the%20Win32%20api_files/image008.jpg"
align=left hspace=12 v:shapes="_x0000_s1029">Now, for the drawing
I've created a new sub, in the sub the BitBlt function is called twice, create
a Picturebox on the game-form, set the scalemode to true, borderstyle to zero,
autosize to true and autoredraw to true. Finally, set the visible property to
false. Now add your graphic. I used this one. Now copy the DrawPlayer sub into
the module.


 







Public Sub DrawPlayer()


   FrmMain.Cls


 


   BitBlt
frmMain.hDc, PlayerX, PlayerY, 20, 20, Picture1.hDC, 20, 0, SRCAND


   BitBlt
frmMain.hDc, PlayerX, PlayerY, 20, 20, Picture1.hDC, 0, 0, SRCPAINT


 


  
FrmMain.Refresh


End Sub



 


The last thing you need to do is add this to the click event
of the command button on the first form:


 







FrmMain.Show


 


DoEvents


 


MainLoop



 


That's it, you have created a small game in witch you can
move around a smile, off course this isn't a fun game, but this game includes
all the basics, now you can add things like a background, you can just set the
picture property of the form to any picture you like. You can turn this into an
RPG or into a pacman type game. You can add sound.


 


For more practise with this way of making games, search for
“My Pacman” at http://www.Planet-Source-Code.com , you will find a pacman game
I made, it uses the same techniques as I explained on this tutorial. I hope you
find this document , useful. You can send all questions and other things to
Meelkertje@hotmail.com.



About this post

Posted: 2002-06-01
By: ArchiveBot
Viewed: 103 times

Categories

Visual Basic 6

Attachments

Making_gam554672152002.zip
Posted: 9/3/2020 3:45:00 PM
Size: 23,428 bytes


Loading Comments ...

Comments

No comments have been added for this post.

You must be logged in to make a comment.