Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Newcomers DBPro Corner / 2D Shooting - A Basic Introduction (DBC & DBP)

Author
Message
TDK
Retired Moderator
21
Years of Service
User Offline
Joined: 19th Nov 2002
Location: UK
Posted: 20th Dec 2006 22:35 Edited at: 14th May 2007 13:39
2D Shooting - A Basic Introduction (DBC & DBP)


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.

In 3D games, which I'll cover in a different tutorial, most beginners make the same mistake as I did - the fatal one of not thinking about what happens in real life.

I have to admit that when I started with DB I spent many hours trying to make a bullet move fast enough to look right. But, that's the problem...

When you fire a bullet from a gun or a sniper rifle, the bullet moves that fast you don't see it. So why waste time in your programs trying to show what you shouldn't be able to see anyway?

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.



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. (Some languages have Boolean variables which can be set to True or False, but as DB Classic does not, in this tutorial we'll just use a normal integer variable set to 0 or 1 for the same result).

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 procedure 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...



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.

You'll also notice that there is also an 'Else' section. This is because when you fire a bullet, a delay is used in the procedure (Sleep 1) so the bullet can be seen. This isn't good practice, but keeps the example nice and simple for the tutorial.

This also means that when there is no bullet being fired the program runs quicker. The result is that the base moves at two speeds - one when no bullet is visible and slower when one is.

So, this If..Else..Endif section basically says 'if there is a bullet on screen then don't use a delay - if there isn't then do use a delay'. In a nutshell it means that the base moves at roughly the same speed whether firing or not.

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 procedure, 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 procedure, 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:



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)!

There are many more tutorials and example code snippets on TGPF - my game programming forums. Click on the link below - it's free to join and everyone's welcome!

Visit TGPF

TDK_Man

Cave Man
17
Years of Service
User Offline
Joined: 22nd Aug 2006
Location: North Carolina, US
Posted: 20th Dec 2006 23:02
I swear i've seen this before. Where was it?

Btw, good tutorial. You've been making good ones alot. Good for the community
TDK
Retired Moderator
21
Years of Service
User Offline
Joined: 19th Nov 2002
Location: UK
Posted: 21st Dec 2006 01:29
Probably on my web site - some were written years ago - before DBPro was available.

So I've updated them and posted them here ready for all the new members that Father Christmas is going to make programmers in a few days!

TDK_Man

Crazy Ninja
18
Years of Service
User Offline
Joined: 27th Aug 2005
Location: Awesometon
Posted: 26th Dec 2006 09:32
I ran all the code and i ran into a few problems.

With the first and second code boxes, there was the M at the bottom of the screen but when i tried to fire the bullet, nothing happened.

With the third code box, the screen doesn't seem to refresh itself and you get bullets left on the screen everywhere and the trail of M is at the bottom.

I'm using DBP v1.63 if that helps at all. Hope you get this sorted out!

TDK
Retired Moderator
21
Years of Service
User Offline
Joined: 19th Nov 2002
Location: UK
Posted: 26th Dec 2006 17:58 Edited at: 26th Dec 2006 17:59
Crazy Ninja:

This tutorial was written using DBC - for DBC users.

I was told by another user that it worked fine for him using DBP too, so that's why I added DBP to the compatibility bit at the top.

It could be that the version of DBP you are using is different to the one he was using - or possily he made minor modifications to the code to make it work in DBP and didn't tell me.

I'll check it out and if necessary post DBP versions of the snippets over the next couple of days...

TDK_Man

TDK
Retired Moderator
21
Years of Service
User Offline
Joined: 19th Nov 2002
Location: UK
Posted: 31st Dec 2006 14:07 Edited at: 31st Dec 2006 14:21
OK, here are the code snippets updated for DBPro.

At first glance, the problem seems to be that DBPro just requires an extra Sync call in loops - whereas DBC doesn't.

I'll edit this post and add the other code snippets as I correct them...

Code Box 1:



Code Box 2:



Code Box 3:



TDK_Man

Login to post a reply

Server time is: 2024-03-29 10:47:00
Your offset time is: 2024-03-29 10:47:00