How to write games using Basic4GL - Part 2

Right then!

This is the second part of the game programming tutorials series for Basic4GL. It's pretty much going to carry on from where part 1 left off, so obviously I advise reading part 1 first. It all fits together!

This tutorial will introduce just one concept: "arrays".
It turns out that arrays, and how to use them effectively gives us enough material for an entire tutorial, so I wont try to squeeze anything else in (it was hard enough to squeeze it down to its current length, and it's still pretty long.).

Arrays although simple in concept are actually extremely powerful. They can be used to apply the same lines of code to different data, which we will put to use to show that the same lines of code that move 1 alien can move 50 of them!

Back to the space alien game

The challenge is thus:

Take the simple space alien game from the previous tutorial and extend it to handle multiple space aliens.
For completeness, we might as well make them shoot back too.

--->

So...
...How would we approach this?

Based on what we've learned so far, we might consider doing this:

This would give us a complete working set for a second space alien.

This would work fine. Infact there's nothing wrong with this approach, so long as you have a small number of space aliens. But if we wanted say 50 aliens, we would have to do a lot of cutting and pasting and the program would get very long very quickly.
Also if we later wanted to change the alien code (which we will later when we make them shoot back) we would have to make the same change 50 times!

So we're going to take another approach, which (surprise, surprise) involves introducing our new concept, called "arrays".

Arrays

An array is a special kind of variable. You remember that a variable is used to store a number or a string, like in this (fairly meaningless) program:

dim count, temperature#
dim model$
count = 5
temperature# = 33.44
model$ = "Plastico 12B"

However, instead of just storing 1 number or string, an array can store many numbers or strings.

To illustrate, let's suppose we wanted to store three numbers.
We could simply use 3 different variables:

dim n1, n2, n3
n1 = 4
n2 = 3
n3 = n1 + n2

But we could also store all three numbers in a single array variable, like this:

dim n(3)
n(1) = 4
n(2) = 3
n(3) = n(1) + n(2)

This looks pretty similar to the first program. The biggest difference being that there are brackets around the numbers.
The dim n(3) statement tells the computer that we want an array of 3 elements called n. This is the array variable name, and follows the same rules as normal variables (for example, we know that the array stores integers because there is no special symbol after the n. Had we called it n$ as in dim n$(3), we would have been given an array of three text strings).

So is this better?
Admittedly it's slightly longer and doesn't do anything particularly better. But bear with me, because there are things we will learn to do with arrays that we can't do with regular variables.
One situation is if we want to store a large number of variables, say 1000. Obviously it would take us a long time to write out code to "dim" 1000 different variables.

dim n1, n2, n3, n4, n5, n6, n7, n8, n9, n10
...!

But with an array we just do this:

dim n(1000)

And there are other advantages which we will see soon.

Using array variables

Each number (or text string) that is stored in an array is called an "array element". Each element behaves like a regular variable. That is, we can read or write to it, use it in expressions, as function parameters or whatever.
We tell the computer which element we want to access by putting the number in brackets after the array variable name. So in the previous examples, n(2) accesses the seond element of the array.

Here's some more examples of using array elements.

dim name$(3), numbers(4)
name$(1) = "Tom"
name$(2) = "Dick"
name$(3) = "Harry"
numbers(1) = 3
numbers(2) = numbers(1)
numbers(3) = numbers(1) + numbers(2) * 2
numbers(4) = sqrt (numbers (3))

As you can see, we are using them just like normal variables.

The Array Index

The number inside the brackets is called the "array index".
This is the number used to 'lookup' the variable in the array. You can think of this like a mailbox number. It tells the postman where the letter is supposed to go.
So for an instruction like:

numbers(3) = 5

We're delivering the number 5 to mailbox number 3.
We could then look inside mailbox number 3 like this:

print numbers(3)

And we would find the number 5 which we just delivered there.
Of course if we used a different array index like 2:

print numbers(2)

We wouldn't find our number 5! We've looked in the wrong mailbox.
(We would infact find a number 0 there, because that is the default for integer variables if we haven't changed them to anything else yet.)

Other ways to specify the array index

We put a 3 in the brackets to specify array index 3.
But what if we had used something like 1+1+1 instead?

dim numbers(4)
numbers(1+1+1) = 5
print numbers(3)

Would this compile?
Would it have the same effect?

The answer is: yes, it does exactly the same thing. Type it in an run it, and you'll see that it does indeed print a 5 on the screen.

The reason this works is that the array index doesn't have to be just a single number. It will also accept any expression that sums up to a single number.

So we could have used 2 + 1 or 7 - 4 or sqrt (9) all to access mailbox number 3.
The computer is quite happy to calculate the result of this sum and go to the appropriate "mailbox".
(Unlike the postal service who turn their nose up at any address that requires long division... Don't ask...)

Using a variable for the array index

Here's another way to access the 3rd element (mailbox) of the numbers array:

dim numbers(4), i
i = 3
numbers(i) = 5
print numbers(3)

This is actually a very powerful technique!

What we've done is declared a variable i, and then stored the number 3 in it.
Then we've used i as the array index!
Remember that we can use any expression we want to calculate the array index number, and that includes other variables like i.
Again the computer calculates the expression, and sees that it comes out as 3. So off to index 3 it goes and stores in the number 5.

Arrays and loops

Right! Now we can bring it all together and show exactly why arrays are very powerful for dealing with large amounts of data (like space alien positions :).
The technique involves using a for..next loop, and combining it with the using-a-variable-for-the-array-index we just saw above.
Now that sentence probably sounded like Mongolian Swahili to you, so we'll illustrate with another example:

Let's say we have an array of 10 numbers, and we want to set all the elements to the number 7. (Think of it like delivering a copy of 7 to all the mailboxes in the street).

One way would be to do this:

dim numbers(10)
numbers(1) = 7
numbers(2) = 7
numbers(3) = 7
numbers(4) = 7
numbers(5) = 7
numbers(6) = 7
numbers(7) = 7
numbers(8) = 7
numbers(9) = 7
numbers(10) = 7

That works fine.

Another way would be to do this:

dim numbers(10), i
i = 1
numbers(i) = 7
i = 2
numbers(i) = 7
i = 3
numbers(i) = 7
i = 4
numbers(i) = 7
i = 5
numbers(i) = 7
i = 6
numbers(i) = 7
i = 7
numbers(i) = 7
i = 8
numbers(i) = 7
i = 9
numbers(i) = 7
i = 10
numbers(i) = 7

This works fine too. But it's almost twice as long.
But! Here's the trick!
Notice that we are simply running the same instruction ten times:

numbers(i) = 7

Only we're putting an i = 1 infront of the first one then an i = 2 in front of the next one, then an i = 3, and so on...

If you remember back to the for..next loop, you may realise that this is exactly the same as saying:

for i = 1 to 10
    numbers (i) = 7
next

(Or here's the full program:)

dim numbers(10), i
for i = 1 to 10
    numbers(i) = 7
next

Now this does everything we needed it to do, and it's quite a bit shorter than the first program.

And it has other advantages.
Say you wanted to store an 8 in each array element (mailbox), it would be very easy to change the program to do this.
Or, if you decided you wanted to have 100 array elements (or 1000), it's still very easy to update the program to do this.

Putting it together

That was quite a bit of theory, and not much about space aliens. It must be about time to put it to use on something practical.

But before going back to the space alien game though, we're going to start with a simple bouncing ball program.
This is because we want to demonstrate the inbetween steps for converting a one-object program into a many-object program, and the space alien game has become rather large. We want something smaller so we can show each little step without making the tutorial longer than it already is!

1 bouncing ball

Okay, so firstup we need a bouncing ball program with one ball.
Because we're still using text mode, we will simply draw a letter 'o' for the ball. The ball will simply move diagonally around the screen. Whenever it reaches the edge of the screen it will bounce away.

As before, we need to decide what data the computer needs to store to remember where the ball is, and keep it moving.

We need to store the ball's current position on the screen. So we will use ballx and bally as the current column and row.
Now at any one time, the ball will be moving in a direction, so we need to store that too.
We will use two variables called ballxd and ballyd, and will store the horizontal (left and right) and vertical (up and down) directions of the ball, which we will store as a number. Each time around the main loop we will update the ball's position like this:

ballx = ballx + ballxd
bally = bally + ballyd

So when ballx is 1, it will be the same as doing a ballx = ballx + 1 and the ball will move to the right. And when ballx is -1, it will be doing the same as ballx = ballx + -1 (in other words ballx = ballx - 1) and the ball will move to the left.
Likewise with ballyd. 1 will make the ball move down, and -1 will make it move up.

Here's the first version with 1 ball.

dim ballx, bally
dim ballxd, ballyd

ballx = rnd () % 38 + 1
bally = rnd () % 23 + 1
ballxd = 1: ballyd = 1

TextMode (TEXT_BUFFERED)
SetTextScroll (false)

while true
    ballx = ballx + ballxd
    bally = bally + ballyd
    if ballx <= 0 or ballx >= 39 then 
        ballxd = -ballxd
    endif
    if bally <= 0 or bally >= 24 then
        ballyd = -ballyd
    endif

    cls
    locate ballx, bally: print "o"
    DrawText ()
    
    Sleep (75)
wend

Most of this should look familiar from the space aliens game. We have declared our variables with dim. Then we've stored some initial data in them, using the rnd () function and the remainder operator (%) to generate numbers from 1-38 and 1-23 for the column and row screen position. This makes the ball start from a random position.

In the main loop, we move the ball as we said we would.
The only tricky bit is the if..then instructions:

if ballx <= 0 or ballx >= 39 then

detects when the ball has reached the left hand side of the screen (column 0) or the right hand side (column 39).
If so we reverse the left-right direction with:

ballxd = -ballxd

So if ballxd was 1 (ball moving right), it will be changed to -1 (ball moving left).
And if ballxd was -1 (ball moving left), it will be changed to 1 (ball moving right).

And the ball's up-down direction is handled in the same way.

The rest of the code simply draws the ball on the screen, and delays for 0.075 seconds, to regulate the speed.

3 bouncing balls

So now we're going to change the program to handle 3 bouncing balls.
As we said before, we're going to do this in stages, so we're not going to jump to the final solution straight away.

For now we're going to ignore arrays and simply use more variables.
So instead of ballx, we're going to have ballx1, ballx2 and ballx3. And likewise for bally, ballxd and ballyd all the other variables.
And we're simply going to copy the code that we wrote to move and draw the first ball over to the second and third.

The code for this one is a little bit repeditive and easy to make mistakes, so you might just want to cut and paste it rather than type it all in.

dim ballx1, bally1
dim ballx2, bally2
dim ballx3, bally3
dim ballxd1, ballyd1
dim ballxd2, ballyd2
dim ballxd3, ballyd3

ballx1 = rnd () % 38 + 1
bally1 = rnd () % 22 + 1
ballxd1 = 1: ballyd1 = 1
ballx2 = rnd () % 38 + 1
bally2 = rnd () % 22 + 1
ballxd2 = 1: ballyd2 = 1
ballx3 = rnd () % 38 + 1
bally3 = rnd () % 22 + 1
ballxd3 = 1: ballyd3 = 1

TextMode (TEXT_BUFFERED)

while true
    ballx1 = ballx1 + ballxd1
    bally1 = bally1 + ballyd1
    if ballx1 <= 0 or ballx1 >= 39 then 
        ballxd1 = -ballxd1
    endif
    if bally1 <= 0 or bally1 >= 23 then
        ballyd1 = -ballyd1
    endif
    ballx2 = ballx2 + ballxd2
    bally2 = bally2 + ballyd2
    if ballx2 <= 0 or ballx2 >= 39 then 
        ballxd2 = -ballxd2
    endif
    if bally2 <= 0 or bally2 >= 23 then
        ballyd2 = -ballyd2
    endif
    ballx3 = ballx3 + ballxd3
    bally3 = bally3 + ballyd3
    if ballx3 <= 0 or ballx3 >= 39 then 
        ballxd3 = -ballxd3
    endif
    if bally3 <= 0 or bally3 >= 23 then
        ballyd3 = -ballyd3
    endif

    cls
    locate ballx1, bally1: print "o"
    locate ballx2, bally2: print "o"
    locate ballx3, bally3: print "o"
    DrawText ()
    
    Sleep (75)
wend

As before I've used bold red to highlight the new bits.
So the program is almost 3 times as long now, but as you can see when you run it, it does work fine. There are 3 balls bouncing around the screen like they were told.

Okay, from now on, each version is going to do the same thing, just in slightly different ways each time!
So please try not to be discouraged when you don't see any improvement. You will see in the end that it was worth it, especially when we see how easy it is to change the final version to bounce 50 balls around the screen.

So let's soldier on and replace these variables with arrays.
Still gradual steps remember!

dim ballx(3), bally(3)
dim ballxd(3), ballyd(3)

ballx(1) = rnd () % 38 + 1
bally(1) = rnd () % 22 + 1
ballxd(1) = 1: ballyd(1) = 1
ballx(2) = rnd () % 38 + 1
bally(2) = rnd () % 22 + 1
ballxd(2) = 1: ballyd(2) = 1
ballx(3) = rnd () % 38 + 1
bally(3) = rnd () % 22 + 1
ballxd(3) = 1: ballyd(3) = 1

TextMode (TEXT_BUFFERED)

while true
    ballx(1) = ballx(1) + ballxd(1)
    bally(1) = bally(1) + ballyd(1)
    if ballx(1) <= 0 or ballx(1) >= 39 then 
        ballxd(1) = -ballxd(1)
    endif
    if bally(1) <= 0 or bally(1) >= 23 then
        ballyd(1) = -ballyd(1)
    endif
    ballx(2) = ballx(2) + ballxd(2)
    bally(2) = bally(2) + ballyd(2)
    if ballx(2) <= 0 or ballx(2) >= 39 then 
        ballxd(2) = -ballxd(2)
    endif
    if bally(2) <= 0 or bally(2) >= 23 then
        ballyd(2) = -ballyd(2)
    endif
    ballx(3) = ballx(3) + ballxd(3)
    bally(3) = bally(3) + ballyd(3)
    if ballx(3) <= 0 or ballx(3) >= 39 then 
        ballxd(3) = -ballxd(3)
    endif
    if bally(3) <= 0 or bally(3) >= 23 then
        ballyd(3) = -ballyd(3)
    endif

    cls
    locate ballx(1), bally(1): print "o"
    locate ballx(2), bally(2): print "o"
    locate ballx(3), bally(3): print "o"
    DrawText ()
    
    Sleep (75)
wend

So the program hasn't improved a great amount, but we are using arrays. And we still get 3 bouncing balls when we run it.
This still isn't the best way to use arrays though.
In the next step we are going to replace code like:

ballx(1) = ballx(1) + ballxd(1)

With code like this:

i = 1
ballx(i) = ballx(i) + ballxd(i)

Where i is a new variable that we are using for the "array index".

Here's the new code:

dim ballx(3), bally(3)
dim ballxd(3), ballyd(3)
dim i

i = 1
ballx(i) = rnd () % 38 + 1
bally(i) = rnd () % 22 + 1
ballxd(i) = 1: ballyd(i) = 1
i = 2
ballx(i) = rnd () % 38 + 1
bally(i) = rnd () % 22 + 1
ballxd(i) = 1: ballyd(i) = 1
i = 3
ballx(i) = rnd () % 38 + 1
bally(i) = rnd () % 22 + 1
ballxd(i) = 1: ballyd(i) = 1

TextMode (TEXT_BUFFERED)

while true
    i = 1
    ballx(i) = ballx(i) + ballxd(i)
    bally(i) = bally(i) + ballyd(i)
    if ballx(i) <= 0 or ballx(i) >= 39 then 
        ballxd(i) = -ballxd(i)
    endif
    if bally(i) <= 0 or bally(i) >= 23 then
        ballyd(i) = -ballyd(i)
    endif
    i = 2
    ballx(i) = ballx(i) + ballxd(i)
    bally(i) = bally(i) + ballyd(i)
    if ballx(i) <= 0 or ballx(i) >= 39 then 
        ballxd(i) = -ballxd(i)
    endif
    if bally(i) <= 0 or bally(i) >= 23 then
        ballyd(i) = -ballyd(i)
    endif
    i = 3
    ballx(i) = ballx(i) + ballxd(i)
    bally(i) = bally(i) + ballyd(i)
    if ballx(i) <= 0 or ballx(i) >= 39 then 
        ballxd(i) = -ballxd(i)
    endif
    if bally(i) <= 0 or bally(i) >= 23 then
        ballyd(i) = -ballyd(i)
    endif

    cls
    i = 1
    locate ballx(i), bally(i): print "o"
    i = 2
    locate ballx(i), bally(i): print "o"
    i = 3
    locate ballx(i), bally(i): print "o"
    DrawText ()
    
    Sleep (75)
wend

Now again the program still produces 3 bouncing balls. And it hasn't become any simpler or shorter. Infact all these i = ... statements have made it longer still.
But there is one very important feature here that you may have noticed!

The positioning code is exactly the same for each ball.
The movement code is exactly the same for each ball.
The drawing code is exactly the same for each ball.

All that differs is the i = 1, i = 2, or i = 3 that is infront.

So we can use our for..next trick (from before) so that the same instructions can be used on all the balls!
So instead of:

i = 1
locate ballx(i), bally(i): print "o"
i = 2
locate ballx(i), bally(i): print "o"
i = 3
locate ballx(i), bally(i): print "o"

we can write:

for i = 1 to 3
    locate ballx(i), bally(i): print "o"
next

So let's do it! Here's the new program:

dim ballx(3), bally(3)
dim ballxd(3), ballyd(3)
dim i

for i = 1 to 3
    ballx(i) = rnd () % 38 + 1
    bally(i) = rnd () % 22 + 1
    ballxd(i) = 1: ballyd(i) = 1
next

TextMode (TEXT_BUFFERED)

while true
    for i = 1 to 3
        ballx(i) = ballx(i) + ballxd(i)
        bally(i) = bally(i) + ballyd(i)
        if ballx(i) <= 0 or ballx(i) >= 39 then 
            ballxd(i) = -ballxd(i)
        endif
        if bally(i) <= 0 or bally(i) >= 23 then
            ballyd(i) = -ballyd(i)
        endif
    next

    cls
    for i = 1 to 3
        locate ballx(i), bally(i): print "o"
    next
    DrawText ()
    
    Sleep (75)
wend

Now there's an improvement! It's only a little bit longer than the code to bounce one ball around the screen!

But let's take this a little bit further.
We keep on writing for i = 1 to 3. This is obviously because there are 3 balls on the screen. But instead of saying 3 everytime, let's use a constant instead!
Why would we do this? We'll see in a minute :-)

We will call the constant ballCount and give it the value 3 at the start of the program like this:

const ballCount = 3

Then everytime we want to refer to the number of balls (i.e 3) we will use ballCount instead.
So instead of saying for i =1 to 3, we are instead going to say for i = 1 to ballCount.

Let's see the changed program.

const ballCount = 3

dim ballx(ballCount), bally(ballCount)
dim ballxd(ballCount), ballyd(ballCount)
dim i

for i = 1 to ballCount
    ballx(i) = rnd () % 38 + 1
    bally(i) = rnd () % 22 + 1
    ballxd(i) = 1: ballyd(i) = 1
next

TextMode (TEXT_BUFFERED)

while true
    for i = 1 to ballCount
        ballx(i) = ballx(i) + ballxd(i)
        bally(i) = bally(i) + ballyd(i)
        if ballx(i) <= 0 or ballx(i) >= 39 then 
            ballxd(i) = -ballxd(i)
        endif
        if bally(i) <= 0 or bally(i) >= 23 then
            ballyd(i) = -ballyd(i)
        endif
    next

    cls
    for i = 1 to ballCount
        locate ballx(i), bally(i): print "o"
    next
    DrawText ()
    
    Sleep (75)
wend

Again, the program works exactly the same way as before. 3 bouncing balls.

So why is this better?
Well consider this.. How difficult would it be to change this program to have 50 balls bouncing around the screen.
How many lines of code do you think we would have to change?

The answer - believe it or not - is just one line of code!

Try it! Change the top line from:

const ballCount = 3

to read:

const ballCount = 50

And re-run the program.

That's right! Changing just one line of the program can make it into a 50 ball program. And you can just as easily change it to 1, 10, 100 or 1000 balls, or any other number for that matter (although when the number gets really big the computer will start to slow down. If it gets too big the computer will say it doesn't have enough memory to store all these balls and won't be able to run the program.)

So that is - in a nutshell - the power of using arrays that I've been harping on about all this time...

Back to the space aliens!

Now I promised that we were going to have multiple space aliens, and now we have the tools to do it.

So load up the space alien game from the first tutorial (or cut and paste it from the tutorial if necessary).

More of 'em

We're going to use the same techniques from above to create multiple space aliens.
But first we have to think a little bit about how this will affect the game rules. Because the game rules for the 1 alien version aren't quite suited for a multiple space alien version.
In the current version, whenever we shoot the alien it reappears on the left side of the screen. But now we would really prefer them to dissapear and stay gone, so that the player can clear the screen of all aliens and win the game (or move on to the next level etc).

So the computer also needs to remember which aliens are still on screen, and which aren't, so it knows which ones to move around and draw.

We'll start by changing the variables declared to arrays. That is we will replace (at the top of the program):

dim score, lives, turretx, alienx, alieny

With :

const alienCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i

This will stop the program from working for now, but that doesn't matter (we still have some more changes to make before it is ready).
Notice that we've used a constant for the number of aliens, instead of simply writing 10 everywhere. This will make it easy to change the number of aliens later (e.g. if we want to make the game easier or harder.)
We've added the alienOnScreen array to store whether or not each alien is still on the screen.

Next we have to change the code that puts the aliens in their starting position. Now that we have more than one, we can't have them both start from the same position, so we will use random numbers to set their starting position.

Replace the lines (near the top):

alienx = 0
alieny = 12

With:

for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next

This should put the aliens in random start positions (rnd () % 37 makes a random number between 0 and 36, and rnd () % 22 + 1 makes a random number between 1 and 22). We obviously couldn't set them all to column 0, row 12, as otherwise they would all be drawn over the top of each other, and it would look like there is only one alien!
We still have a couple of changes to make before the program will compile and run though.

Next we will change the alien movement code.
Find the section of the program that reads:

    alienx = alienx + 1
    if alienx > 37 then
        alienx = 0
        alieny = rnd () % 22 + 1
    endif

And replace it with:

    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
        endif
    next

Notice that we've used our for..next loop to make sure the instructions get repeated multiple times - once to move each alien. We've also used an if..then..endif statement to tell the computer to only run the instructions between then and endif if the alien is on the screen. (There's no reason to move the alien if it isn't.)

We're almost there. Next we're going to update the code that draws the alien on the screen.
Find this line in the program

    locate alienx, alieny: print ">O<"

And replace it with this:

    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next

Again we tell the computer to run the same lines of code once for each alien.
And again we tell the computer only to draw the alien if it's still on the screen.

We've made quite a few changes, and the program still won't run. It's normally a good idea to try and keep the program working as we make the changes. This means that we can test each of the steps as we go along, and keeps us from getting disheartened.
So what we're going to do now is completely remove the collision detection code so that we can test the program! (Don't worry, we will add it back in again very soon.)

So find the section of the program that reads:

    if bulletOnScreen and bullety = alieny and bulletx >= alienx and bulletx <= alienx + 2 then
        color (255, 255, 100)
        for i = 1 to 10
            locate alienx, alieny: print "///"
            DrawText ()
            Sleep (50)
            locate alienx, alieny: print "\\\"
            DrawText ()
            Sleep (50)
        next
        bulletOnScreen = false
        alienx = 0
        alieny = rnd () % 22 + 1
        score = score + 100
        Sleep (1000)
    endif

And delete it!

Now run the program, and you should see a whole screenful of space aliens flying up above the turret.

If it doesn't work, then you will need to check over the program for mistakes.
To make it easier, here's the complete program at this point, with the changes in red as usual.

const alienCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i
lives = 3
turretx = 19
for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next
bulletOnScreen = false

TextMode (TEXT_BUFFERED)
while true
    if ScanKeyDown (VK_LEFT) and turretx > 0 then
        turretx = turretx - 1
    endif
    if ScanKeyDown (VK_RIGHT) and turretx < 37 then 
        turretx = turretx + 1
    endif
    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
        endif
    next
    if bulletOnScreen then 
        bullety = bullety - 1
        if bullety < 1 then 
            bulletOnScreen = false
        endif
    else
        if ScanKeyDown (VK_SPACE) then
            bulletOnScreen = true
            bullety = 22
            bulletx = turretx + 1
        endif
    endif
        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif
 
    DrawText ()
    Sleep (75)
wend

Shooting the space aliens

So we can see that the game indeed runs, and that there are many aliens.

This looks a bit more exciting! But if you're like me, you'll be wanting get that collision code back in so we can start blasting aliens! (It's a male thing...)

So we'll simplify what happens when the alien gets hit a little bit and just do the following:

  1. Remove the alien from the screen.
  2. Remove the bullet from the screen.
  3. Increase the score by 100

Because there are 10 aliens, we need to tell the computer to check whether the bullet has hit each one individually. So we will need another for..next loop, to check the bullet's position against all of the aliens.

Here's the updated code:

const alienCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i
lives = 3
turretx = 19
for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next
bulletOnScreen = false

TextMode (TEXT_BUFFERED)
while true
    if ScanKeyDown (VK_LEFT) and turretx > 0 then
        turretx = turretx - 1
    endif
    if ScanKeyDown (VK_RIGHT) and turretx < 37 then 
        turretx = turretx + 1
    endif
    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
        endif
    next
    if bulletOnScreen then 
        bullety = bullety - 1
        if bullety < 1 then 
            bulletOnScreen = false
        endif
    else
        if ScanKeyDown (VK_SPACE) then
            bulletOnScreen = true
            bullety = 22
            bulletx = turretx + 1
        endif
    endif
        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif 
        
    if bulletOnScreen then
        for i = 1 to alienCount
            if alienOnScreen (i) then
                if bullety = alieny (i) and bulletx >= alienx (i) and bulletx <= alienx (i) + 2 then
                    alienOnScreen (i) = false
                    bulletOnScreen = false
                    score = score + 100
                endif
            endif
        next
    endif       
 
    DrawText ()
    Sleep (75)
wend

Now this is probably more if..thens and for..nexts than we've used in combination before, but we should be ready to cope with it now :) It's the same as what we've seen before, just in a few more layers.

First we tell the computer that if the bullet is not on the screen then don't worry about checking any of the aliens:

if bulletOnScreen then

Next we tell it to repeat the instructions multiple times, once for each alien.

for i = 1 to alienCount

Then we say to only to check the alien if it's on the screen.

if alienOnScreen (i) then

And then we have the actual check to see if the alien and the bullet are at the same position on the screen.

if bullety = alieny (i) and bulletx >= alienx (i) and bulletx <= alienx (i) + 2 then

And finally, if it finds an alien at the same position as the bullet, it runs the code to remove the alien and the bullet from the screen and update the score.

alienOnScreen (i) = false
bulletOnScreen = false
score = score + 100

And that's about all there is too it.

Shooting back

There's still a few things missing from our game, and the most obvious is that the aliens don't shoot back So let's make them.

As usual we first need to stop and think about how the aliens are going to shoot back, and how that can be made into specific rules that we can enter into the computer.
The rules will be quite simple.

From this we can see that we will need arrays (because we will have multiple bullets).
We need to store the position of each bullet (we will use x and y array variables) on the screen.
The bullets won't always all be on the screen at once, so we also need to store which ones are on screen and which aren't.

So we will probably store them something like this:

const bombCount = 10
dim bombx (bombCount), bomby (bombCount), bombOnScreen (bombCount)

(I've called them "bombs" because they will fall down the screen, and because I can't be bothered typing "alienBullet" each time!)

Next we need to make the aliens drop bombs randomly each time they move.
We've said they will have a 1 in 10 chance each time. We can make this work by making picking a random number from 1 to 10 each time and then checking if it equals 1. If it does we drop the bomb.

In order to drop a bomb, we need to find one that isn't already on the screen. So we need to search through all the bombs for the first one where bombOnScreen (bombCount) is false. When we find one, we will place it on the screen where the alien is.
We need something like this (in the move alien loop):

if rnd () % 10 + 1 = 1 then
    for j = 1 to bombCount
        if not bombOnScreen (j) then
            bombOnScreen (j) = true
            bombx (j) = alienx (i) + 1
            bomby (j) = alieny (i)
            j = bombCount
        endif
    next
endif

We need the new variable j because we are already using i as the alien array index.
You may be wondering about the j = bombCount line at the end, after we have put a bomb on the screen. This simply tells the computer that we don't want to repeat the instructions in the for..next anymore (because we've already found our bomb and put it on the screen). Otherwise the computer would keep on looping through the remaining bombs and putting them all underneath the current alien! That's not what we want!

The next thing we need is some code to draw the bombs on the screen.
We will put this just after the code that draws the player bullet:

color (175, 175, 175)
for i = 1 to bombCount
    if bombOnScreen (i) then
        locate bombx (i), bomby (i): print "O"
    endif
next

Here's what the program currently looks like:

const alienCount = 10
const bombCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i, j
dim bombx (bombCount), bomby (bombCount), bombOnScreen (bombCount)
lives = 3
turretx = 19
for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next
bulletOnScreen = false

TextMode (TEXT_BUFFERED)
while true
    if ScanKeyDown (VK_LEFT) and turretx > 0 then
        turretx = turretx - 1
    endif
    if ScanKeyDown (VK_RIGHT) and turretx < 37 then 
        turretx = turretx + 1
    endif
    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
            if rnd () % 10 + 1 = 1 then
                for j = 1 to bombCount
                    if not bombOnScreen (j) then
                        bombOnScreen (j) = true
                        bombx (j) = alienx (i) + 1
                        bomby (j) = alieny (i)
                        j = bombCount
                    endif
                next
            endif
        endif
    next
    if bulletOnScreen then 
        bullety = bullety - 1
        if bullety < 1 then 
            bulletOnScreen = false
        endif
    else
        if ScanKeyDown (VK_SPACE) then
            bulletOnScreen = true
            bullety = 22
            bulletx = turretx + 1
        endif
    endif
        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif 
    color (175, 175, 175)
    for i = 1 to bombCount
        if bombOnScreen (i) then
            locate bombx (i), bomby (i): print "O"
        endif
    next        
    if bulletOnScreen then
        for i = 1 to alienCount
            if alienOnScreen (i) then
                if bullety = alieny (i) and bulletx >= alienx (i) and bulletx <= alienx (i) + 2 then
                    alienOnScreen (i) = false
                    bulletOnScreen = false
                    score = score + 100
                endif
            endif
        next
    endif
    DrawText ()
    Sleep (75)
wend

(Don't forget to add the j to the dim instruction at the top!)

You can see that the program actually runs, and that the aliens do make bombs.
We just need to make them fall down the screen, like this:

for i = 1 to bombCount
    if bombOnScreen (i) then
        bomby (i) = bomby (i) + 1
        if bomby (i) > 23 then
            bombOnScreen (i) = false
        endif
    endif
next

So the bomby (i) = bomby (i) is there to move the bombs down the screen.
The if bomby (i) > 23 then will check if the bomb has fallen off the bottom of the screen, and if so the bombOnScreen (i) = false will take it off the screen (so it's ready to be dropped again by another alien).

We can place this code after the code that moves the aliens. Like this:

const alienCount = 10
const bombCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i, j
dim bombx (bombCount), bomby (bombCount), bombOnScreen (bombCount)
lives = 3
turretx = 19
for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next
bulletOnScreen = false

TextMode (TEXT_BUFFERED)
while true
    if ScanKeyDown (VK_LEFT) and turretx > 0 then
        turretx = turretx - 1
    endif
    if ScanKeyDown (VK_RIGHT) and turretx < 37 then 
        turretx = turretx + 1
    endif
    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
            if rnd () % 10 + 1 = 1 then
                for j = 1 to bombCount
                    if not bombOnScreen (j) then
                        bombOnScreen (j) = true
                        bombx (j) = alienx (i) + 1
                        bomby (j) = alieny (i)
                        j = bombCount
                    endif
                next
            endif
        endif
    next
    for i = 1 to bombCount
        if bombOnScreen (i) then
            bomby (i) = bomby (i) + 1
            if bomby (i) > 23 then
                bombOnScreen (i) = false
            endif
        endif
    next
    if bulletOnScreen then 
        bullety = bullety - 1
        if bullety < 1 then 
            bulletOnScreen = false
        endif
    else
        if ScanKeyDown (VK_SPACE) then
            bulletOnScreen = true
            bullety = 22
            bulletx = turretx + 1
        endif
    endif        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif 
    color (175, 175, 175)
    for i = 1 to bombCount
        if bombOnScreen (i) then
            locate bombx (i), bomby (i): print "O"
        endif
    next        
    if bulletOnScreen then
        for i = 1 to alienCount
            if alienOnScreen (i) then
                if bullety = alieny (i) and bulletx >= alienx (i) and bulletx <= alienx (i) + 2 then
                    alienOnScreen (i) = false
                    bulletOnScreen = false
                    score = score + 100
                endif
            endif
        next
    endif      
 
    DrawText ()
    Sleep (75)
wend

And the last step is to tell the computer how to check whether a bomb has hit the turret, and what to do if it has.
We want to:

We also want to remove all the bombs from the screen. This gives the player a chance to react when the game starts again.
We want to put this after we've drawn the rest of the screen.

So here's the final version.

const alienCount = 10
const bombCount = 10
dim score, lives, turretx
dim alienx (alienCount), alieny (alienCount), alienOnScreen (alienCount)
dim bulletx, bullety, bulletOnScreen, i, j
dim bombx (bombCount), bomby (bombCount), bombOnScreen (bombCount)
lives = 3
turretx = 19
for i = 1 to alienCount
    alienx(i) = rnd () % 37
    alieny(i) = rnd () % 22 + 1
    alienOnScreen(i) = true
next
bulletOnScreen = false

TextMode (TEXT_BUFFERED)
while true
    if ScanKeyDown (VK_LEFT) and turretx > 0 then
        turretx = turretx - 1
    endif
    if ScanKeyDown (VK_RIGHT) and turretx < 37 then 
        turretx = turretx + 1
    endif
    for i = 1 to alienCount
        if alienOnScreen (i) then 
            alienx (i) = alienx (i) + 1
            if alienx (i) > 37 then
                alienx (i) = 0
                alieny (i) = rnd () % 22 + 1
            endif
            if rnd () % 10 + 1 = 1 then
                for j = 1 to bombCount
                    if not bombOnScreen (j) then
                        bombOnScreen (j) = true
                        bombx (j) = alienx (i) + 1
                        bomby (j) = alieny (i)
                        j = bombCount
                    endif
                next
            endif
        endif
    next
    for i = 1 to bombCount
        if bombOnScreen (i) then
            bomby (i) = bomby (i) + 1
            if bomby (i) > 23 then
                bombOnScreen (i) = false
            endif
        endif
    next
    if bulletOnScreen then 
        bullety = bullety - 1
        if bullety < 1 then 
            bulletOnScreen = false
        endif
    else
        if ScanKeyDown (VK_SPACE) then
            bulletOnScreen = true
            bullety = 22
            bulletx = turretx + 1
        endif
    endif        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    for i = 1 to alienCount
        if alienOnScreen (i) then
            locate alienx (i), alieny (i): print ">O<"
        endif
    next
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif 
    color (175, 175, 175)
    for i = 1 to bombCount
        if bombOnScreen (i) then
            locate bombx (i), bomby (i): print "O"
        endif
    next        
    if bulletOnScreen then
        for i = 1 to alienCount
            if alienOnScreen (i) then
                if bullety = alieny (i) and bulletx >= alienx (i) and bulletx <= alienx (i) + 2 then
                    alienOnScreen (i) = false
                    bulletOnScreen = false
                    score = score + 100
                endif
            endif
        next
    endif
    for i = 1 to bombCount
        if bombOnScreen (i) and bomby (i) = 23 and bombx (i) >= turretx and bombx (i) <= turretx + 2 then
            color (255, 255, 0)
            for j = 1 to 10
                locate turretx, 23: print "\\\"
                DrawText ()
                Sleep (75)
                locate turretx, 23: print "///"
                DrawText ()
                Sleep (75)
            next
            turretx = 19
            lives = lives - 1
            if lives = 0 then
                color (255, 255, 255)
                locate 15, 12: print "Game Over!"
                DrawText ()
                end
            endif
            for j = 1 to bombCount
                bombOnScreen (j) = false
            next
        endif
    next 
    DrawText ()
    Sleep (75)
wend

Game complete ("completer"? :-)

So now we have something that looks a bit more like a complete playable game.
There are still a lot of things we could do to improve it, such as automatically ending the game once all the aliens are shot (or moving onto the next level..?). Or letting the player shoot more than one bullet at a time. I'll leave these up to you.

Too hard? (Or too easy?)

You may also have noticed that the game is quite difficult at the start. This is no big deal, it just means we need to fine-tune some of the parameters. I'll leave this up to you also, but here are some ideas:

Lower the frequency that the aliens shoot

For example, changing the line that reads:

if rnd () % 10 + 1 = 1 then

To

if rnd () % 30 + 1 = 1 then

Will give the aliens a 1 in 30 chance of shooting each time instead of 1 in 10.
(Or try some other numbers.)

Change how high the aliens fly overhead

Replace this line:

alieny(i) = rnd () % 22 + 1

With

alieny(i) = rnd () % 20 + 1

To stop the aliens coming in quite so low.
(There are two places in the program with this line, you'll need to replace both.)

Adjust the number of aliens and bombs

Change the 10 in line:

const alienCount = 10

To the number of aliens you want to have on the screen.

Change the 10 in line:

const bombCount = 10

To the number of bombs you want on the screen.

Speed-up/slow-down the game

By changing the line:

Sleep (75)

To a different number. (Higher numbers slow the game down. Lower numbers speed it up.)

Adjust the number of lives

By changing the line:

lives = 3

 

And that about wraps it up for this tutorial! The next one will introduce a few more programming techniques for writing cleaner and easier to read code.