Description: A game tutorial using Bit Blit from David Brebner's Unlimited Realities website. You'll also be able to find many other excellent, more advanced animation and graphics tutorials. Well worth the visit!
Controls needed: Form, Command Button, Timer
Level: Beginner-Intermediate
This strategy I am going to present is best described as one step at a time. Where you think of your game as a whole lot of slightly more finished games. You complete bits of your game so that you can run it and test it, before proceeding to the next step.
I am going to list the whole non-sequential gory
process to give you some idea of how to go about it.
Ideas
Not very original I know (if you played sam n max
you have seen a version of this game) but the idea was to do something
simple. I was trying to come up with some ideas that were a bit more fun
than space invaders / noughts n crosses / pacman... I thought this could
be kinda cool. (keep a note book with you at all times, this was planned
in an airport lounge)
Preliminary Planning
Before I went any further I sketched out what the game should look like and what it should do.
Being the kinda guy I am, I tend to think about how things will look. However this is important as the look of your game will determine how you go about programming it.
I decided I would have 9 rat holes. Rats stick their
heads up to be whacked by your mallet. To make life easy I decided I would
ignore perspective, so the rats are all the same size.
Graphics
Rather than 3D rendering rats, or hand drawing each frame, I decided to use models. I have a video camera and capture card that make this easy to do. The background art was drawn in pen, then scanned and coloured on the computer.
p.s. these are a special mongolian breed of weird pink rats.
If you don't have this sort of gear, then you may need to find someone who does. I used to draw my graphics myself, directly on the PC using a mouse (even an analogue joystick on an Apple IIe, and using rubber keys on my old Spectrum, and even worse using HEX on my older ZX81!!) and trust me, a video camera and scanner are much easier!
When I had scanned in the backround pen drawing I coloured it in using photoimpact, you can choose whatever tool you prefer (paintshop pro is a good shareware tool). I the applied a blur to the background to give the image a feeling of depth.
In addition I made 3 holes of the right size for my rats, then copied and pasted them.
The next step is to create the sprites. (Check my tutorial on sprites)
Because this game is a simple BitBlt game, rather than using DirectDraw we need to mask the sprites ourselves. For simplicity I have created a second bitmap which contains the masks you will need.
Basically I have a hammer in 3 positions, upright, down, and hitting a rat. The rat pokes his head up, looks around to give the player a chance to whack him, and then him popping up and cheering (points for the rats). In addition, the all important squashed rat frames.
Sorry, but I have avoided the issue here slightly and processed this sprite sheet though my sprite editor (which is not yet ready for public consumption). Suffice to say, that it makes it easier to establish the position of each of your sprites within the sprite sheet, and creates a simple data file.
You can do this yourself, by writing down the coordinates
and dimensions of the sprites on your sprite sheet, or placing each sprite
in its own bitmap.
Version 1 (bobbing rats)
Right its time to pull out your trusty VB and start coding. I like to start with some tests to make sure my graphics look ok, and that things are going to work ok.
This also gives you a chance to test out some strategies for implementing your game.
Get those declares.
I usually use a standard BAS file I made up called blit.bas. This has all the normal stuff I use all the time like bitblt, strechblt, the constants and soundplaysnd.
Here's what we need for this project, declared at form level (in your general section),
| Private Declare Function BitBlt Lib "gdi32"
(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
Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long Const SRCCOPY = &HCC0020 ' (DWORD) dest = source Const SRCINVERT = &H660046 ' (DWORD) dest = source XOR dest Const SRCAND = &H8800C6 ' (DWORD) dest = source AND dest |
Time to stick some pictures and a timer on our form. We need 4 pictures,
You should also make sure that the PicBuf is as big as PicBak, as it has to buffer this image.
Next add a timer, enabled=true and interval (the speed of our game clock) equal to... say 30 (ms).
You also need to do some things to the form. Make scalemode=pixel, and create a blank icon - set the mouseicon property to your blank icon, and put mousecursor=99 (custom). The overall effect is to hide the mouse cursor when you move over the form at run-time. We will replace the cursor with our hammer graphics we have designed.
Lets start coding!
Lets see some graphics!! Add this line to your form_paint event.
| u& = BitBlt(hDC, 0, 0, Picbuf.ScaleWidth, Picbuf.ScaleHeight, Picbuf.hDC, 0, 0, SRCCOPY) |
Now for a hammer!
Right, time for some of that dreaded masking and double buffering business.
It all goes in the timer event. You can think of this as the heartbeat of the game.
First we need some form level variables (in your general section) to remember the mouse position, and some contants to define the different states of the hand. (remember the hammer can be up, down and hit)
| Dim handx%, handy% 'current hand position
Dim handpos% 'how far through a swing
|
| Private Sub Form_MouseMove(Button As Integer,
Shift As Integer, x As Single, y As Single)
handx% = x - 15 handy% = y - 40 'this offset is so the head of the hammer is at the same point as the mouse cursor End Sub Private Sub Form_MouseDown(Button As Integer,
Shift As Integer, X As Single, Y As Single)
Private Sub Form_MouseUp(Button As Integer,
Shift As Integer, X As Single, Y As Single)
Private Sub Timer1_Timer()
'place the sprite of the hand
'copy to screen
Sub drawhand()
|
The next subroutine, the timer, is the guts of it all. We initally copy the background into the buffer to start off afresh. We then do all our drawing, which currently consists of a mallet. After this we copy the whole buffer to screen so the animation is relatively smooth.
The drawing of the hand is handled in a slightly kludgy way. As I said earlier in the graphics section I have a tool to create coordinates for sprites, however just for you I have done it an old fashioned way, and hard coded the hand positions.
This involved using photoimpact, and selecting the appropriate hand, and writing down the x,y, width and height. I then simply type these values into the bitblt for each case. (see my tutorial on bitblting and sprites)
When you run the project now, you should see a hammer instead of a mouse cursor! p.s. if you still see an arrow as well, then check my instructions on hiding the mouse cursor earlier on.
I want some RATS!
Lets get some rats onscreen for our satisfaction. Once again lets start with some variable to hold our rats positions. I am going to use a 2 dimensional array (whoo, hoo) because even though its not necessary, I figure the rats are layed out in rows and columns.
| Dim ratx%(2, 2), raty%(2, 2), ratpos%(2, 2)
'hold the sprites
|
Heres a chunk of code to add to your timer event. You should add it after you clear the buffer and before you draw the hand (the hand should always be on-top of the rats).
| 'draw some rats For col% = 0 To 2
--For row% = 0 To 2 ----drawarat ratx%(row%, col%), raty%(row%, col%), ratpos%(row%, col%) ----ratpos%(row%, col%) = ratpos%(row%, col%) - 1 ----If ratpos%(row%, col%) < 1 Then ratpos%(row%, col%) = 12 --Next Next |
| Sub drawarat(X%, Y%, cell%)
If cell% > 0 Then --u& = BitBlt(Picbuf.hDC, X% + spnox%(cell%), Y% + spnoy%(cell%), spnw%(cell%), spnh%(cell%), Picmsk.hDC, spnx%(cell%), spny%(cell%), SRCAND) --u& = BitBlt(Picbuf.hDC, X% + spnox%(cell%), Y% + spnoy%(cell%), spnw%(cell%), spnh%(cell%), Picimg.hDC, spnx%(cell%), spny%(cell%), SRCINVERT) End If End Sub |
| Private Sub Form_Load()
'load up the sprites Open App.Path & "\whack_img.spr" For Random As #1 Len = 2 For a% = 0 To 14 --Get #1, a% * 6 + 1, spnox%(a% + 1) --Get #1, a% * 6 + 2, spnoy%(a% + 1) --Get #1, a% * 6 + 3, spnx%(a% + 1) --Get #1, a% * 6 + 4, spny%(a% + 1) --Get #1, a% * 6 + 5, spnw%(a% + 1) --Get #1, a% * 6 + 6, spnh%(a% + 1) Next Close #1 'position the rats over their holes
Sub stickrat(row%, col%, X%, Y%, pos%)
|
Fire it up and you should have 9 shifty rats
poking their heads up while you wander around with a mallet banging to
no effect. Time to proceed to version 2!
Version 2 (a game?)
Well its time we got ourselves a game. What do we want in our game, and what are the priorities?
Rats popping up
Here are some new variables we will need to add to the declare section.
| Dim ratspeed%(2, 2)
' game status
' current frame (will just increment)
|
We need to add the randomize statement to the form_load so that the random numbers we will be generating are different each time we run the game.
| (into form_load)
Randomize |
| Private Sub Timer1_Timer()
'copy from background u& = BitBlt(Picbuf.hDC, 0, 0, Picbuf.ScaleWidth, Picbuf.ScaleHeight, Picbak.hDC, 0, 0, SRCCOPY) frame = frame + 1
'draw some rats
'copy to screen
|
The other new subroutine, updateRat, takes the place of that bobbing code we have in version 1. This will check to see if there is currently a rat, and if there is, then to bob at the speed defined when popuprat was called.
See what I mean, heres the code for those two subroutines;
| Sub updateRat(row%, col%)
If ratpos%(row%, col%) <= 12 And ratpos%(row%, col%) > 0 Then --'if the rat has started popping up! --'check if time has elapsed sufficent for the speed --If frame Mod ratspeed%(row%, col%) = 0 Then ----'e.g. the bigger the speed, the less frequently it will ----'be an exact division (e.g. the slower the rat pops up) ----'change the frame ----ratpos%(row%, col%) = ratpos%(row%, col%) - 1 --End If End If End Sub Sub popuprats()
|
Run wac-a-rat and you should get some randomly bobbing rats, and still a very impotent hammer.
Lets get those smug, self satisfied rodents.
How do we go about whacking the rats on the head? Well I figured two options we could use initially. 1) check in the mousedown if we have clicked inside the mouse sprite. 2) check in the mousedown if we are a certain radius away from the centre of the hole.
Well as cool as #2 sounds, its probably not so effective. We could achieve like this, xdiff = abs(holex - handx), ydiff = abs(holey - handy). The distance is now srqt(xdiff^2 + ydiff^2).
Technique #1 is better as its quick, and the bigger the sprite, the easier to hit (i.e. slightly more realistic)
Lets have a go;
| Private Sub Form_MouseDown(Button As Integer,
Shift As Integer, X As Single, Y As Single)
If handpos% = hand_up Then handpos% = hand_down For col% = 0 To 2 --For row% = 0 To 2 ----sp% = ratpos%(row%, col%) ----If sp% > 0 And sp% <= 12 Then ------'this rat is trying to popup! ------If X > ratx%(row%, col%) + spnox%(sp%) And X < ratx%(row%, col%) + spnox%(sp%) + spnw%(sp%) Then --------'inside the left and right of the sprite --------If Y > raty%(row%, col%) + spnoy%(sp%) And Y < raty%(row%, col%) + spnoy%(sp%) + spnh%(sp%) Then ----------'inside the top & bottom too! ----------u& = sndPlaySound(App.Path & "\whack_pop.wav", 1) ----------ratpos%(row%, col%) = 13 ----------ratspeed%(row%, col%) = 10 '10 frames to fade away --------End If ------End If ----End If --Next Next End Sub |
Now that we can have rats in a new position, 13, we need to handle this in our updateRat routine.
| Sub updateRat(row%, col%)
--If ratpos%(row%, col%) <= 12 And ratpos%(row%, col%) > 0 Then ----'if the rat has started popping up! ----'check if time has elapsed sufficent for the speed ----If frame Mod ratspeed%(row%, col%) = 0 Then ------'e.g. the bigger the speed, the less frequently it will ------'be an exact division (e.g. the slower the rat pops up) ------'change the frame ------ratpos%(row%, col%) = ratpos%(row%, col%) - 1 ----End If --ElseIf ratpos%(row%, col%) > 12 Then ----If frame Mod ratspeed%(row%, col%) = 0 Then ------ratpos%(row%, col%) = ratpos%(row%, col%) + 1 ------If ratpos%(row%, col%) >= 15 Then --------'finished animation, get rid of it --------ratpos%(row%, col%) = 0 ------End If ----End If --End If End Sub |
Finishing it off
Well its getting late, so lets tidy it up - remember it is only an example.
We need a score, the rat position * 3 points for a hit, and -20 points per miss. Remember the rats position gets closer to 0 as it comes up out of the hole, e.g. the later you hit it, the less points you get.
We need a new game button which lets you choose a level.
I figure 1000 frames is enough rat whacking for anyone, so thats the time you have to whack as many as you can.
| (in general)
'score Dim score% '+/- 32000 should cover it (in form load)
|
The next two lines is so that until the game starts the timer is off. Because the timer is off we need to turn the default mousepointer on so we can see what we are doing (there is no hammer).
Now stick a button labeled New Game in the top left hand corner of the game window.
| Private Sub Command1_Click()
frame = 0 score% = 0 Level% = Val(InputBox("Choose a difficulty from 0 to 8" & Chr$(13) & "8 being the hardest.")) If Level% > 8 Then difficulty% = 8 Else difficulty% = Level% Timer1.Enabled = True MousePointer = 99 End Sub |
| (Add to the Form_Mousedown next to
the sndPlaysound)
score% = score% + ratpos%(row%, col%) * 3 (Add just after you decrement the rat positon
in updateRat sub) If ratpos%(row%, col%) = 0 Then
|
| (this routine goes into Timer1_timer
just after popuprats)
'update score and time Picbuf.CurrentX = 90 Picbuf.CurrentY = 10 Picbuf.Print "Score : " & score% Picbuf.CurrentX = 90 Picbuf.Print "Time : " & (1000 - frame) If frame = 1000 Then --'thats the end of this game --Timer1.Enabled = False --MousePointer = 0 End If |
The last bit is to check if time has expired. In which case the timer is disabled and the mousepointer turned on again.
Congratulations!
Download the source Code
Don't worry if you got out of step, you can view the complete source code - or download the VB project along with wave file images and the icon.
Play the game
Happy Programing
David
Brebner