1690 IF y<56 AND x>basex-12 AND x<basex+64 playeralive=FALSE:bombdead=TRUE

1700 ENDIF

1710 IF bombdead THEN

1720 bomb(a,0)=FALSE:bombptr$+=STR$(a)

1730 ELSE

1740 GCOL 0,5:MOVE bomb(a,1),bomb(a,2):VDU 5,234,4

1750 ENDIF

1760 ENDIF

1770 NEXT

1780 ENDPROC

1790

1800 DEF PROCexplode

1810 LOCAL a:REM Destruction of player's base

1820 IF funeral=0 THEN

1830 FOR a=0 TO 9

1840 bomb(a,1)=basex+16:bomb(a,2)=40

1850 bomb(a,3)=RND(128)-64:bomb(a,4)=RND(64)

1860 NEXT

1870 ENDIF

1880 FOR a=0 TO 9

1890 GCOL 0,0:MOVE bomb(a,1),bomb(a,2):VDU 5,233,4

1900 bomb(a,1)+=bomb(a,3)

1910 bomb(a,2)+=bomb(a,4)

1920 GCOL 0,7:MOVE bomb(a,1),bomb(a,2):VDU 5,233,4

1930 NEXT

1940 IF funeral=50 restart=TRUE

1950 funeral+=1

1960 ENDPROC

Invaders From Space: Code explained...

10 MODE 9:OFF

20 ORIGIN 640,0

30 DIM alien(69,5),bomb(9,4)

We're using MODE 9 again and shifting the graphics origin to the horizontal centre of the window, but leaving the vertical origin at the bottom. This arrangement is handy because the game graphics begin with a degree of vertical symmetry.

This game requires two tables. The first holds information on each alien. The second holds details of any bombs they drop.

50 VDU 23,224,60,78,78,60,126,255,255,126:REM Alien look left

60 VDU 23,225,60,114,114,60,126,255,255,126:REM Alien look right

70 VDU 23,226,1,65,109,127,255,255,238,68:REM Base left half

80 VDU 23,227,128,130,182,254,255,255,119,34:REM Base right half

90 VDU 23,230,192,192,192,192,192,192,0,0:REM Bullet

100 VDU 23,231,60,126,126,60,126,255,255,126:REM Alien solid

110 VDU 23,232,120,252,252,252,252,252,252,120:REM Bullet hole

120 VDU 23,233,252,252,252,252,252,252,0,0:REM Debris

130 VDU 23,234,96,240,240,96,0,0,0,0:REM Bomb

Note that any of these VDU 23 commands can be replaced with your own design of graphics. We're using characters 224, 225, 226, 227, 230, 231, 232, 233 & 234.

150 hiscore=0

160 REPEAT

170 lives=3:score=0

This is another program that doesn't self-terminate. The condition of the related UNTIL command is never met. This is the outer-loop allowing a new game to be started once the current game has finished.

'hiscore' is the variable used to store the player's highest score.

'score' will hold the current score for the current game.

180 REPEAT

190 restart=FALSE

200 playeralive=TRUE

210 firing=FALSE

220 aliensalive=0

230 funeral=0

240 bombptr$="0123"

This is the middle-loop, cycling once per life of the player.

'restart' will be used to indicate that a new life needs to be started.

'playeralive' indicates whether the player's base is active or has been destroyed. When the base has been destroyed the game animation doesn't reset immediately. Before the player obtains another base, we show the old base exploding. While the explosion is being animated, the player cannot interact with the game.

'firing' indicates the existence of a laser bullet. The game is designed to allow one active bullet only. This limit introduces a degree of skill to the game.

'aliensalive' keeps track of the number of aliens still alive.

'funeral' is used as a counter when animating the base explosion.

'bombptr$' limits the number of alien bombs active simultaneously. Change this string to increase or decrease the number of bombs, up to a maximum of 10 e.g. “0123456789”

250 FOR b=0 TO 9:bomb(b,0)=FALSE:NEXT

The first element in the bomb array is set to FALSE (for each bomb). This deactivates any bombs left active from a previous game.

On the commencement of each new life or game, we clear the screen of any leftover graphics.

Line 280 draws the player's base.

Line 290 calls a procedure 4 times to draw 4 yellow barriers. Note that PROCbarrier includes brackets containing values. This is an example of parameters being passed to a procedure. See line 510. The parameters are the x and y co-ordinates of the barrier's position.

300 REPEAT

310 TIME=0

This is the inner-loop, cycling once per animation frame. We use the computer's internal clock to time how long it takes to draw each animation frame.

320 IF aliensalive=0 PROCnewaliens

When the player has destroyed all of the aliens, this triggers the creation of a new wave.

330 PROCmovesomealiens(aliencolumn):aliencolumn+=1

340 IF aliencolumn=10 THEN

350 aliencolumn=0

360 IF allchange wavedirection*=-1:dropcount+=1:allchange=FALSE

370 ENDIF

PROCmovesomealiens() moves one column of aliens per animation frame. This method minimises the total graphics needing to be redrawn each animation cycle. A secondary effect is to limit the speed of the alien advance.

'aliencolumn' counts from zero to 9, incrementing once per animation frame.

When every alien in the wave has been moved, we check for the edge of the display. If any alien has reached one side of the window, or another, then the whole wave changes direction, as well as dropping down a line.

380 IF playeralive PROCdroppingbomb:PROCplayer ELSE PROCexplode

If the player is alive then we call the PROCplayer procedure for user input. We're also cheating slightly because we're going to use the alien bomb array to process the destruction of the player's base. This means that when the player is alive we want to process any active alien bombs, but if the player is dead then we process the explosion of the base instead.

390 PROCbullet

The player's laser bullet is processed independently of the player.

400 WAIT 4-TIME

410 *FX21

420 UNTIL restart

Line 400 limits the speed of the game to 25 animation frames per second (i.e. 4 one hundredths of a second per frame).

The end of the inner-loop. This ends with the end of a player's life.

430 lives-=1

440 UNTIL lives=0

Line 430 reduces the player's lives by one.

The end of the middle-loop. This ends when the player has run out of lives.

450 PRINTTAB(7,15);"Press SPACE to play again"

460 REPEATUNTILGET=32

470 IF score>hiscore hiscore=score

480 UNTIL FALSE

490 END

The end of the outer-loop and the end of the main part of the program.

510 DEF PROCbarrier(x,y)

520 RECTANGLEFILL x-80,y-64,160,120

530 ENDPROC

Recall that PROCbarrier is called 4 times, to draw 4 yellow rectangles that act as partial barriers to alien bombs and laser bullets.

The LOCAL command instructs BB4W to treat the listed variables as 'local' to this procedure. This means that these variables don't exist as far as the main part of the program is concerned. It is generally good practice to use local variables. It allows the reuse of variable names in different procedures for different purposes. You will note that many variables are given useful names like 'aliensalive', but often we need a variable for a simple loop, using 'a', 'b', 'x', 'y' etc for convenience. Problems arise when the same variable is used in more than one place in a conflicting manner. Making variables 'local' to procedures can reduce the risk of creating such conflicts.

'wavedirection' controls the direction of alien movement and is reversed when any alien reaches one side of the window or another.

'dropcount' and 'allchange' are used to synchronise alien movement downwards.

580 gap=64:offsetx=5*gap

'gap' and 'offsetx' control the spacing of the alien wave.

590 alf=0

600 FOR y=0 TO 6

610 FOR x=0 TO 9

620 alien(alf,0)=TRUE:REM alive

630 alien(alf,1)=y+1:REM colour

640 alien(alf,2)=x*gap-offsetx:REM x

650 alien(alf,3)=y*gap+512:REM y

660 alien(alf,4)=224+x MOD 2:REM graphic

670 alien(alf,5)=0:REM dropcount

680 WAIT 1

690 GCOL 0,alien(alf,1):PROCdrawalien(alf,alien(alf,4))

700 alf+=1

710 NEXT

720 NEXT

730 ENDPROC

The remainder of the routine initialises individual alien variables and draws each alien. Each column of the 'alien' array holds a specific value e.g. the colour of the alien is related to the row. Array columns 2 and 3 hold the position (x,y co-ordinates) of the alien.

Line 690 includes a call to PROCdrawalien. This is another procedure that requires parameters. Note that the second parameter is an array value, passed to the equivalent PROCdrawalien variable called 'gfx' on line 750. This is the user-defined character to be drawn.

750 DEF PROCdrawalien(alf,gfx)

760 MOVE alien(alf,2),alien(alf,3):VDU 5,gfx,4

770 ENDPROC

PROCdrawalien draws the graphics for one alien only. Note that the colour of the alien is set elsewhere. If drawn in black then the image of the alien is effectively deleted from the window.

790 DEF PROCmovesomealiens(c)

800 LOCAL a,b:REM Animation of aliens

810 FOR a=0 TO 69

820 IF a MOD 10=c AND alien(a,0) THEN

As mentioned above, we process one column of aliens per animation frame. The FOR... NEXT loop and the IF statement within this procedure select the relevant aliens for processing.

830 GCOL 0,0:PROCdrawalien(a,231)

Deletes the current alien image from the window by drawing an image in black.

840 alien(a,4)EOR=1

The 8 x 8 characters, used for animating an alien, face to the left and to the right. This line 'toggles' or 'flip-flops' between the two directions.

850 alien(a,2)+=wavedirection

Moves one alien in the current direction of the wave.

860 IF alien(a,2)<-600 OR alien(a,2)>568 allchange=TRUE

Tests to see if this alien has reached the edge of the window.

870 IF alien(a,5)<dropcount alien(a,5)+=1:alien(a,3)-=32

Does this alien have to drop down a line because another alien has already reached the edge of the window?

880 GCOL 0,alien(a,1):PROCdrawalien(a,alien(a,4))

Draws the new image of the alien.

890 IF alien(a,3)<=32 playeralive=FALSE

Player loses when an alien touches the ground.

900 IF playeralive AND RND(10)=1 AND bombptr$<>"" THEN

This section processes a possible alien bomb-release, but only if the player is still alive.

RND(10) returns a random value between 1 and 10, inclusive.

If 'bombptr$' is empty then this indicates that there are no free spaces in the 'bomb' array.

This use of 'bombptr$' is one method to control and limit the number of alien bombs. When the program is run you will notice that alien bombs have varying life spans. One bomb may exist for longer than another, depending on how fast it drops and whether it hits a barrier before reaching the ground or the player.

So whenever we want an alien to drop a bomb, we have to check that we can accommodate the alien's request. One way is to loop through the 'bomb' array, looking for spaces i.e. an empty bomb row. Our bomb array is very small, having only 10 spaces available, so any method would be okay. However, some programs may control hundreds or even thousands of objects, in which case it would be extremely inefficient to read though the entire list. One alternative method is to create a 'stack' of 'pointers', just like the 'bombptr$' string. The stack is a separate array (or string) that holds the number of every empty array row. Whenever we need to find an empty space we simply take the next number off the top of the stack. When the object is no longer needed then we then return its 'pointer' (row number) to the stack.

VAL(RIGHT$(bombptr$,1)) returns a numeric value, equal to the rightmost digit in the bombptr$ string.

Just like the alien array, the bomb array holds the details of every bomb.

RND(2)*12 randomly returns the value 12 or 24. This will be the speed that the bomb drops.

(Note that RND(1) returns a fractional value between zero and 1.)

1010 DEF PROCplayer

1020 LOCAL moving,xd:REM Animation of player's base

1030 moving=FALSE

Our method of animation is very simple and we only want to delete a particular graphic if the object is being moved. If it hasn't moved then there's no point in deleting the image and redrawing it.

1040 IF INKEY(-98) AND basex>-600 moving=TRUE:xd=-12

1050 IF INKEY(-67) AND basex<536 moving=TRUE:xd=12

INKEY(-98) tests the status of the 'Z' key and returns TRUE if currently depressed. This method of testing keyboard input is preferable over others when quick responses are required.

1060 IF moving THEN

1070 GCOL 0,0:MOVE basex,40:VDU 5,226,227,4

1080 basex+=xd

1090 GCOL 0,7:MOVE basex,40:VDU 5,226,227,4

1100 ENDIF

If the player has moved the base left or right, then the program deletes the old image (at the old co-ordinates), updates the position of the base and redraws the graphics.

'xd' can hold a positive or negative value. Adding 'xd' to 'basex' will increase or decrease the value of 'basex'.

VDU 5,226,227,4 is an instruction to draw text at the graphics cursor and print character 226 immediately followed by character 227. VDU 4 returns text positioning to the control of the text cursor.

1110 IF INKEY(-74) AND NOT firing THEN

1120 bulletx=basex+28:bullety=64:firing=TRUE

1130 ENDIF

1140 ENDPROC

Firing a laser bullet can only occur if a bullet is not already active.

'bulletx', 'bullety' and 'firing' hold the details of the current laser bullet.

1160 DEF PROCbullet

1170 REM Animation of player's bullet

1180 IF firing THEN

1190 GCOL 0,0:MOVE bulletx,bullety:VDU 5,230,4

1200 bullety+=24

A Laser bullet is always moving, so we always want to update its position and redraw the bullet, deleting the old image first.

1210 IF FNimpacted OR bullety>960 THEN

FNimpacted is the first appearance of a 'function'. Functions are almost identical to procedures, except they always return a single value. In this instance FNimpacted returns either TRUE or FALSE depending on a specific test.

Many computer languages do not discriminate between procedures and functions.

1220 GCOL 0,0:MOVE bulletx-8,bullety:VDU 5,232,4

This line draws a bullet hole. In the event a bullet hits one of the 4 barriers, it will cause significant damage to the relevant barrier.

1230 IF bullety<928 PROCtestaliens

1240 firing=FALSE

1250 ELSE

1260 GCOL 0,1:MOVE bulletx,bullety:VDU 5,230,4

1270 ENDIF

1280 ENDIF

1290 ENDPROC

Below a specific altitude, the bullet will be tested for collision with an alien, but only if the bullet is found to have hit 'something' (see FNimpacted).

If no impact was detected then the routine draws the bullet in its updated position.

1310 DEF FNimpacted

1320 LOCAL y:REM Player's bullet impacted anything?

1330 hit=FALSE:y=0

1340 WHILE NOT hit AND y<6

1350 IF POINT(bulletx,bullety-y*4)<>0 hit=TRUE

1360 y+=1

1370 ENDWHILE

1380 =hit

FNimpacted looks at the next 6 pixels in the immediate path of the bullet. If any non-black pixels are detected then the function returns a value of TRUE.

POINT(x,y) returns the colour of a pixel at co-ordinates x,y

WHILE... ENDWHILE is another form of loop structure. A WHILE loop will only execute if its defining criteria are met. In this instance the loop executes until 6 pixels are tested or a non-black colour is detected.

This specific example could be replaced by REPEAT... UNTIL hit OR y=6

Note the '=' at then end of the function. This is the main difference between a function and a procedure.

1400 DEF PROCtestaliens

1410 LOCAL hit,a:REM Player's bullet hit an alien?

1420 hit=FALSE:a=0

1430 REPEAT

1440 IF alien(a,0) THEN

1450 IF bulletx>=alien(a,2) AND bulletx<alien(a,2)+32 THEN

1460 IF bullety>alien(a,3)-32 AND bullety<alien(a,3)+24 THEN

PROCtestaliens is only called when the program thinks a laser bullet may have collided with an alien.

This routine tests every alien, checking that the alien is still active/alive.

Next we check to see if the bullet falls within the rectangular area of the alien, i.e. between its top and bottom and between its left and right sides.

1470 alien(a,0)=FALSE:aliensalive-=1:hit=TRUE

1480 score+=10:IF aliensalive=0 score+=400

1490 PRINTTAB(16,0);score

1500 GCOL 0,0:PROCdrawalien(a,231)

1510 ENDIF

1520 ENDIF

1530 ENDIF

1540 a+=1

1550 UNTIL hit OR a>69

1560 ENDPROC

If a collision is detected then the alien is deactivated, and its image within the window deleted. We also increase the player's score, with a bonus for the destruction of an entire wave.

1580 DEF PROCdroppingbomb

1590 LOCAL a,bombdead,x,y:REM Alien bombs

1600 FOR a=0 TO 9

PROCdroppingbomb processes the movement of up to 10 bombs currently in free-fall.

1610 bombdead=FALSE

1620 IF bomb(a,0) THEN

1630 GCOL 0,0:MOVE bomb(a,1),bomb(a,2):VDU 5,234,4

1640 bomb(a,2)-=bomb(a,4)

Just like laser bullets, bombs are always in motion and require constant redrawing. While laser bullets move upwards at a fixed rate, bombs can drop at different speeds.

1650 x=bomb(a,1):y=bomb(a,2)

The use of variables 'x' and 'y' in this instance is for convenience only, because it makes the code easier to read than repeated references to bomb(a,1) and bomb(a,2).

1660 IF y<16 OR y<316 AND y>196 AND POINT(x,y)=3 THEN

Note the complexity of this IF statement. The statement will be TRUE if y is less than 16. It will also be TRUE if y is less than 316 AND y is greater than 196 AND the colour of the pixel at x,y is yellow.

This test destroys a bomb if it hits the ground or falls onto a yellow barrier.

1670 bombdead=TRUE:GCOL 0,0:MOVE x,y+2/3*bomb(a,4):VDU 5,234,4

Bombs erode barriers. Note the inclusion of '2/3*' which relates to two thirds of the bomb's downward velocity. This is a 'fudge-factor', a minor adjustment of an inconsequential calculation, to make the pattern of erosion appear more realistic.

1680 ELSE

1690 IF y<56 AND x>basex-12 AND x<basex+64 playeralive=FALSE:bombdead=TRUE

This IF statement tests for a bomb colliding with the player's base, destroying the base and the bomb.

1700 ENDIF

1710 IF bombdead THEN

1720 bomb(a,0)=FALSE:bombptr$+=STR$(a)

Upon the demise of a bomb the pointer to the row it occupied, in the 'bomb' array, is returned to the stack (bombptr$), ready for reassignment.

1730 ELSE

1740 GCOL 0,5:MOVE bomb(a,1),bomb(a,2):VDU 5,234,4

1750 ENDIF

1760 ENDIF

1770 NEXT

1780 ENDPROC

If the bomb remains active, then it needs to be redrawn in its updated position.

1800 DEF PROCexplode

1810 LOCAL a:REM Destruction of player's base

1820 IF funeral=0 THEN

1830 FOR a=0 TO 9

1840 bomb(a,1)=basex+16:bomb(a,2)=40

1850 bomb(a,3)=RND(128)-64:bomb(a,4)=RND(64)

1860 NEXT

1870 ENDIF

1880 FOR a=0 TO 9

1890 GCOL 0,0:MOVE bomb(a,1),bomb(a,2):VDU 5,233,4

1900 bomb(a,1)+=bomb(a,3)

1910 bomb(a,2)+=bomb(a,4)

1920 GCOL 0,7:MOVE bomb(a,1),bomb(a,2):VDU 5,233,4

1930 NEXT

1940 IF funeral=50 restart=TRUE

1950 funeral+=1

1960 ENDPROC

PROCexplode usurps control of the 'bomb' array. Ten blocks of debris, with varying velocities, are entered into this array.

For the next 50 animation frames this routine draws the exploding blocks, moving them away from the remains of the player's base.

When 50 frames have been drawn, 'restart' is set to TRUE to trigger a new life, or the end of the game.

Pixel Size:

It will be apparent that there is a discrepancy between the size of the pixels, as they appear in user-defined characters, and the accuracy to which they can be plotted. In MODE 9, one pixel is equal to 4 graphical 'units' in width and height.