TDK's - 2D Shooting - A Basic Introduction

Started by kevin, April 21, 2008, 07:53:47 AM

Previous topic - Next topic

kevin


TDK's Play Basic Programming For Beginners

Back To Index

TDK's -  2D Shooting - A Basic Introduction

Many games in both 2D and 3D require the ability to shoot - something no more difficult than locating where a projectile is being fired from, where it is being aimed, and then plotting it's course between the two points.


First though, we'll cover the less realistic environment of the 2D shooting problem.



2D Shooting


 In a 2D program the bullet can be a sprite, an image, or even an ASCII character. Whatever the type of bullet, the process is as follows:

    1. Place the bullet in front of the gun barrel
    2. Calculate the next position of the bullet
    3. Replace the background for the old bullet position
    4. Place the bullet in the new position
    5. Check to see if hit the enemy or left the screen
    6. If 'no' to both questions at number 5, repeat from number 2
    7. End firing routine

 The actual X and Y positions of the bullet on the screen are held in variables so we can alter them and move the bullet. Let's try a very simple example of the type that most beginners usually make. To keep it simple, we will use the Text X,Y command and ASCII characters - remember the method is basically the same with images and sprites.

  Note: The example programs have been written to demonstrate the process as clearly as possible - not to demonstrate good programming practices. Once you understand how these programs work, you can optimize and improve on them.

+ Code Snippet

; Tell PB to limit this program to 30 frames per second
Setfps 30

; Our Objects X position
BasePosX = 400

Rem Main Program Loop
Do

 If Leftkey()=1 Then BasePosX=BasePosX-4
 If BasePosX<0 Then BasePosX=0
 If Rightkey()=1 Then BasePosX=BasePosX+4
 If BasePosX>788 Then BasePosX=788
 CLS Rgb(0,0,0)
 Text BasePosX,580,"M"
 If Spacekey()=1 Then Gosub MoveBullet
 Sync
Loop

MoveBullet:
 BulletPosX = BasePosX + 5
 BulletPosY = 580-16
 For N=BulletPosY To 0 Step -5
ink Rgb(0,0,0)
  Text BulletPosX,N+5,"|"
ink Rgb(255,255,255)
   Text BulletPosX,N,"|"
   Rem Check to see if we hit anything here and if we did
   Rem handle it.
Sync
 Next N
Return




 Essentially, BasePosX is integer variable used to store the horizontal position on the screen and when the left and right cursor keys are pressed, we decrement or increment this variable, clear the screen and print our base (the letter M) in the new position.

 When the space bar is pressed, we gosub the MoveBullet procedure.

 In that, we have a loop which calculates the bullet start position based on the current base position then goes around a loop decrementing the bullet's Y position, printing and erasing the bullet on the screen.

 But, if you run the above code, you'll see there's a major problem: When you fire, the base freezes while the bullet is on the screen.

That's because this little program isn't the correct way to do this. But because we learn from our mistakes, seeing the wrong way to do something makes the right way easier to understand.

 To solve this problem, we really need to get rid of the For N=BulletPosY To 0 Step -5 loop which 'traps' our program while the bullet is moved.

 This is done by calling the MoveBullet procedure EVERY time we go through the main program loop and move the bullet a bit each time. To do this we have to keep track of it's current position manually as we no longer have the variable N from the For...Next loop.

 At this point, the keen-minded of you may have a question: "If we do that, won't the bullet be firing all the time"?

 Well the answer initially is yes. So, we use something called a 'flag'. A flag simply conveys a True/false message and in programming terms is a variable which we use to store whether something has been done or not. Usually this would be by setting the variable to 0 (zero) if it hasn't and 1 if it has.  

 So, when the space bar is pressed, we set a flag to 1 to say a bullet has been fired and it remains 1 until the bullet no longer exists - at which point the flag is set back to 0 (zero).

 In our main loop, we gosub the MoveBullet sub routine only if this flag is set to 1. Easy eh?

 But another problem then creeps in. If we keep pressing the space key, each time we do, the bullet currently moving up the screen starts again from the beginning!

 Stopping this is easy. All we do is tell the program to ignore the space bar if a bullet is in flight. As we already have a flag which equals 1 when a bullet is fired, we just tell the program to ignore the space bar unless the bullet flag equals zero!

 So let's modify our above example...

+ Code Snippet

; Tell PB to limit this program to 30 frames per second
Setfps 30

BasePosX = 400
Rem Main Program Loop
Do
 If Leftkey()=1 Then BasePosX=BasePosX-4
 If BasePosX<0 Then BasePosX=0
 If Rightkey()=1 Then BasePosX=BasePosX+4
 If BasePosX>788 Then BasePosX=788
 CLS rgb(0,0,0)
 Text BasePosX,580,"M"
 If Spacekey()=1 and FiredBullet=0
   FiredBullet=1
   BulletPosX = BasePosX + 5
   BulletPosY = 580-16
 Endif
 If FiredBullet=1
   Gosub MoveBullet
 Endif
 Sync
Loop

MoveBullet:
 BulletPosY=BulletPosY-5
 Text BulletPosX,BulletPosY,"|"
 Rem Check to see if we hit anything here and if we did
 Rem handle it. Reset flag if bullet is done with
 If BulletPosY<=0 Then FiredBullet=0
Return


 In this example, when the space bar is pressed, so long as there isn't a bullet already in flight, we calculate the bullet start position and turn on the 'fired bullet' flag.

 Next, if the flag has been set, we gosub the MoveBullet procedure which moves the bullet a bit then immediately returns to allow the user to move the base.

 OK, that covers simple 2D firing, but "wait!" I hear you cry... "you can only fire a single bullet and I want more!"

Well, for multiple firing the process is just the same, but you need separate variables to store the X and Y screen positions of EVERY bullet.

 This task is exactly what arrays were designed for, so if you don't fully understand arrays, go and read the tutorial which covers them now, then come back when you know how they work. In this next section I have to assume that arrays are not a mystery to you as I make no attempt at explaining them.

 Arrays are covered in my Beginners Guide To Programming Part 1 tutorial.




Multiple Bullets


  In the last example it gets a little more complicated as we need to keep essentially the same method, but handle more than one bullet in the MoveBullet sub routine, when we don't know how many bullets there are - if any! This is done with arrays. We also add another procedure for the creation of a new bullet, but more on that after the example code.

  Before we start, we also need to decide on a maximum number of bullets to display at any one time. Also, our flag from the last example can no longer be a single flag - there has to be one for each possible bullet.

 So we create a variable for the maximum number of bullets and use this value to DIMension arrays to hold each bullets X and Y position and represent each bullet's 'fired or not' flag.

 In the MoveBullet sub routine, we go back to using a loop and use the flag array to decide if the respective bullet should be displayed. Let's see the code:


+ Code Snippet

; Tell PB to limit this program to 30 frames per second
Setfps 30

; Init ther Base's X position Variable to 400
BasePosX = 400

; init the Max Number of bullets Variable
MaxBullets = 20

; dimension arrays to hold the Fired stgatus and X & Y coordinate
; of each bullet
Dim FiredBullet(MaxBullets)
Dim BulletPosX(MaxBullets)
Dim BulletPosY(MaxBullets)

Rem Main Program Loop
Do
 If Leftkey()=1 Then BasePosX=BasePosX-8
 If BasePosX<0 Then BasePosX=0
 If Rightkey()=1 Then BasePosX= BasePosX+8
 If BasePosX>788 Then BasePosX=788
 CLS rgb(0,0,0)
 Text BasePosX,580,"M"
 If Spacekey()=1 Then Gosub AddBullet
 If BulletCount > 0 Then Gosub MoveBullet
 Text 0,0,"Bullets On Screen: "+Str$(BulletCount)+" "
 Sync
Loop

MoveBullet:
 For N = 1 To MaxBullets
   If FiredBullet(N)=1
     BulletPosY(N)=BulletPosY(N)-20
     Text BulletPosX(N),BulletPosY(N),"|"
     Rem Check For Bullet Going Off Top Of Screen Here And Deal With It
     If BulletPosY(N) <= 0
       FiredBullet(N) = 0
       Dec BulletCount
     Endif
     Rem Check For Hit Enemy Here And Deal With It
   Endif
 Next N
Return

AddBullet:
 For N = 1 To MaxBullets
   If FiredBullet(N)=0
     FiredBullet(N)=1
     BulletPosX(N) = BasePosX + 5
     BulletPosY(N) = 580-16
     Inc BulletCount
     Exit
   Endif
 Next N
Return




 You can alter the maximum number of bullets by changing the value of MaxBullets.   If this is set to 20, then when you have fired 20 bullets, you can't fire any more until at least one has hit something or gone off the top of the screen.

 There are also a couple of changes in the Main Loop worth mentioning...

  Note: Remember the machine guns in war movies where the ammunition is on a 'belt' and is fed into the weapon from a box on the floor next to it? Well, for this explanation, think of the array as one of those ammo belts, but as a continuous loop containing 'MaxBullets' bullets. Each bullet is in a slot and once a bullet has been fired, it's slot is empty and a new bullet can be clipped into the belt at that position.

  First of all, when the space key is pressed, we now Gosub AddBullet where we loop through every element of the FiredBullet() array looking for an empty slot. If you remember, when bullet 1 is in use, then FiredBullet(1) equals 1 and when it is not, it equals 0.

  When we press space, this routine finds the first empty slot in the array, turns the FiredBullet() flag on (sets it to 1), stores that bullet's X and Y start position and increments the variable which holds how many bullets are currently in use.

  The next line, the Exit, basically exits the For..Next loop at that point without unnecessarily running through the whole loop. For example, if it was the first bullet and there were a maximum of 10 bullets, the first slot (1) is used and continuing the loop testing the other 9 would be a waste of time. So, placing an Exit inside the block when an empty slot is found saves our program a lot of wasted testing.

  OK, at this point, BulletCount equals 1 so in the main loop, Gosub MoveBullet is called. This is another similar loop but this time, the array flag is used to say whether the bullet is drawn or not.

  If the bullet goes off the top of the screen or hits something (that's the bit you can write yourself), then we set the array flag back to 0 to show that the slot is available for use again and decrement the BulletCount variable.

  If the space is pressed again while another bullet is on screen, then as we are using arrays, another one is created and as this is done in a For..Next loop which counts from 1 to the maximum number of bullets you can have (MaxBullets), you can never create more than the required number of bullets, (or as we are using arrays, ever get the dreaded 'index out of bounds' error)!



TDK_Man

   



reno

Thanks for this Kevin ;)

Next step will be ennemy firing to the player position ?

Something I really want to know, is the maths to make a curve movement. Like in "NBA JAM" when you shoot the ball into the basket. Can you give us that ? Thank you !
More games ? Go to my website :)
http://www.thereeteam.com/

ATLUS