How to write games in Basic4GL...

15-May-2004
-Tom Mulgrew

Intro

This is a tutorial on how to write games in Basic4GL, which is a free programming language for Windows computers.
It's aimed at the complete beginner, and takes you from the very basics of writing simple programs, right through to writing scrolling 2D sprite based games.
So if you've never programmed a line of code before, I recommend you start at the beginning. Otherwise you may want to skip a few sections and jump in at the level that suits you.

This is part 1 of the tutorial, which introduces the basics of programming by using them to write a simple text based space invaders like game. Later parts will build onto what is taught here, introducing 2D sprite based animation and finally a full parallax scrolling 2D shooter.

Why start with Basic4GL?

Once upon a time if you bought a computer, it came with a free programming language called BASIC, which stands for "Beginner's All Purpose Symbolic Instruction Code" and was invented to make programming easier to learn for beginners.
Traditionally it wasn't the most powerful language for writing complex applications, but it did make it easy to write interesting programs - often with graphics and/or sound effects - without having to write pages of program code just to get started.

Basic4GL aims to continue in this spirit, as a free, beginner friendly language, based on today's technology like OpenGL 3D graphics. It's specifically designed to make the fun stuff easily accessible, like games and animations.
And while you're doing this, you're also learning basic programming concepts and skills that you can use if you decide to move on to more powerful languages that professional programmers use.

So, in that spirit, this tutorial will teach you to program using animations, games, and basically anything that I think looks interesting or cool. We will start with text based animation (because it's easiest to start with), move into sprite based 2D animation and work our way up to a full 2D parallax scrolling platform shooter.

Getting started

First you need a computer. It will need to be:

Next you need the Basic4GL compiler and development environment.
Fortunately this is available for free from the Basic4GL website: www.basic4gl.net.
You will need to download and install it.

My first program!

Okay! Let's assume you've installed Basic4GL and are ready to go. (If you haven't, there's not much that this tutorial can do for you :)

We're going to write our first program. It will be very simple.

First, run Basic4GL as follows:
Click the windows "Start" menu, then "Programs", then "Basic4GL", then "Basic4GL".

You should see the Basic4GL programming window.

This window looks fairly innocent. Infact it looks a bit like the Windows notepad.
It has an area where you can type into, and and a few buttons on the top.

This however is the Basic4GL editor and is all you need to create and test your own programs. You type them into the text area at the bottom, and run them by clicking the "Go" button on the toolbar.

Here's the first program.

print "Hello world!"

Type this in, then click the "Go" button on the toolbar:

Now one of two things will happen!
If it works, the screen will go black and you will see this:

In the top left hand corner.
Yes, it's the infamous Hello World program. Press a key to finish the program and switch back to the regular screen.

If it doesn't work, you will likely see something like this:

This means there is a syntax error in the program.
If this happens to you, do not panic! Syntax errors occur all the time. They are the programming equivalent of a typo. The computer is simply saying it does not understand what you are trying to do, so you just need to fix it up and try again.

The computer has positioned the cursor at the position of the problem, and has displayed a description of the problem on the status bar (the gray bar at the bottom). In the example, it reads "Unknown variable: prit. Must be declared with DIM."
Sometimes these 'error messages' can be helpful in figuring out the cause of the problem.
In this example, it isn't all that useful, so we need to have a close look ourselves and figure out what is wrong.

Here we have written: prit instead of print

All we need to do is correct the problem, then click the Go button again, and we should get the results we need.

Examining the "Hello World" program

Lets have a look at how "Hello World" worked. It's quite a simple program.
It has just one instruction, consisting of the command print and a parameter "Hello world!".

The print command as you might have guessed prints text on the screen.
You may have noticed that Basic4GL changed its colour to red as you typed it. This is Basic4GL's way of telling you that it has recognised the word "print" as one of its commands.

The "Hello world!" part is what's known as a quoted string. This is how we supply text to the program, so we can tell the print command what we want to print to the screen. The double quotes tell Basic4GL that anything between the quotes are to be treated as text data, so it doesn't try to read it as program code. Basic4GL displays quoted strings in green.

If we remove the quotes from the program and try to run it, it won't work! Because now it treats Hello World! as program instructions. There is no command called "Hello", so Basic4GL will stop and complain.

Saving your program

Once you have a working program that you want to keep, you can save it to disk.
It is also a good idea to save your changes as you go, so that your work isn't lost if something unexpected happens.

Basic4GL saves programs the same way as a text editor would.
You can click "Program|Save", press Control+S on your keyboard, or click the save button

Basic4GL as a calculator

Here's our second program.
Before typing it in, click the new program button on the toolbar , to tell Basic4GL we are working on a new and different program.

print "4 + 5 = "
print 4 + 5

Again, click on the "Go" button to run the program.

So now we can use Basic4GL as a calculator!
Infact we've just written our first expression!

You've just written your first expression: 4 + 5
And the computer has calculated the result and displayed it (with print).

Again let's break it down.
This program has two instructions. Each on a different line.

print "4 + 5 = "
This is just like the Hello World program. We have used the print command, and supplied it with the text "4 + 5 = " as a quoted string. We know that this means write the text "4 + 5 =" to the screen.

print 4 + 5
This looks much like the first line, except the 4 + 5 is not a quoted string anymore. We are using the print command again, but this time using 4 + 5 as the parameter.
So what does the computer do? It evaluates
4 + 5 first. It calculates that 4 + 5 equals 9, and then sends 9 through to print as the parameter.
So the second line prints out a 9 on the screen.

Try modifying the program to calculate other equations. Here are some examples:

4 + 5 + 6  
7 * 8 * means multiplication
14.2 - .5  
3 / 2 / means division
1 + 2 * 3  
(1 + 2) * 3  
sqrt (2)  
(1 + sqrt (2)) * 2  
"Cat" + "fish"  
"Five" + 5  
"1" + "3"  
"4 x 5 = " + 4 * 5  

Some notes to be aware of:

Variables

So now we can calculate mathematical equations and print the results on the screen.
Useful... But we are still some distance away from writing a parallax side scroller! Don't worry, we will get there! But first we need to introduce a few more concepts.

We will start with variables.

A variable is somewhere that you can store some data.
In Basic4GL, this data can be one of the following:

Almost all programs use variables. Even games use them to keep track of all sorts of things. They might store how many aliens there currently are, or how many bullets the player has, or what the score is, or what the players names are on the high score chart, or how long since each one fired a bullet, or any number of things.

So let's have an example.
Clear out the old program (save it first if you want to), and type in the following.

dim a, b
a = 5
b = 3
printr "a stores " + a
printr "b stores " + b

Then run the program (with the Go button, as usual).

It should tell us that a is storing 5, and b is storing 3.

a and b are variables.
We told the computer that we wanted to create two variables with line at the top:
dim a, b
dim is the command that creates variables. After it we list the names that we want to give to those variables, with commas in between (if there are more than one). In this case, we have two variables called a and b.

Next we tell the computer to assign values to them with:

a = 5
b = 3

This tells the computer to store the value 5 in the variable named a, and the value 3 in the variable named b.
Finally we print the results to screen:

printr "a stores " + a
printr "b stores " + b

This time we're using the Basic4GL printr command. This is exactly like the print command except that it returns the cursor to the start of the next line after it has finished.

Variables can be used anywhere that you would normally put a number - in expressions or as parameters for commands. For example, consider the following program.

dim a, b, c
a = 5 
b = 3 + 4
c = a + b
printr "a = " + a
printr "b = " + b
printr "c = " + c
printr "b + c = " + (b + c)

(Type it in and run it if you want.)

This program uses an expression 3 + 4 to calculate the value to store in b:

b = 3 + 4

It uses a + b to calculate the value to store in c:

c = a + b

It uses b + c to calculate a value to use in a parameter to printr:

printr "b + c = " + (b + c)

Choosing variable names

So far we have used a, b and c as variable names, but we don't have to stick with single letters if we don't want. In our program, the variables didn't really have any purpose, but in most programs they do, so it is a good idea to try to give them names that match what it is they are for. This makes the program easier to understand when you come back to it.
If we are storing the number of lives the player has and his/her score, we could write this:

dim a, b
a = 3
b = 1020

this works fine, but if we instead wrote:

dim lives, score
lives = 3
score = 1020

it makes it a lot easier to understand what the program is trying to do.

There are a few rules you have to follow when choosing the names for your variables, otherwise the compiler won't compile and run your program:

Some examples of acceptable variable names:
cat, a1, my_shoe_size, MaximumHealth, counter

Some examples of unacceptable variable names:
cat fish, 2many, health&armour

Variable types

All the variables we have seen so far are integer variables.
We can't use them to store text strings.

dim a
a = "my house"

Would not work.

We can't use them to store real numbers with fractions.

dim a
a = 3.443

would compile and run, however it would round 3.443 to 3 before storing it in a.

So how do we tell the computer we want to store a text string instead of an integer?
To store a text string, we have tell the computer to allocate a text string variable.
We do this by appending a dollar sign ($) to the variable name when we declare it.

Here's another example to try.

dim school$, schoolMotto$

school$ = "Scott Base elementary"
schoolMotto$ = "If it moves, don't eat it"

printr "Welcome to " + school$
printr "Our motto is"
printr schoolMotto$

We've created two text string variables:

dim school$, schoolMotto$

Because they end in a dollar sign, the computer knows that we want to store text string data inside them.
We've stored some text inside them:

school$ = "Scott Base elementary"
schoolMotto$ = "If it moves, don't eat it"

And we've used them in expressions and as parameters to printr

printr "Welcome to " + school$
printr "Our motto is"
printr schoolMotto$

So that's how we store text in variables.

If we want to tell the computer to store a real number in a variable, we have to append a hash sign (#) to the variable name..

For example:

dim l#, w#, diameter#

l# = 10
w# = 14
diameter# = sqrt (l# * l# + w# * w#)

printr "A " + w# + " by " + l# + " inch screen"
printr "Is " + diameter# + " inches in diameter"

Drawing text on the screen

We've covered a fair bit of theory so far, and you may now be scratching your head and wondering how you're going to hold it all in. Again, don't worry! The best way to pick these things up is from examples, and we'll have plenty of those.

For now, let's move on to something a little more visual, where we can see what's going on.
We're going to draw a scene from a space invaders like game.
Just be a single snapshot for now as we will need to learn a few more things before we can turn it into something playable.

Here's the program:

color (255, 255, 255)
locate 0, 0: print "Score=1040"
locate 30, 0: print "Lives=3"
color (255, 50, 50)
locate 11, 12: print ">O<"
color (150, 150, 150)
locate 17, 23: print "<!>"
color (255, 255, 50)
locate 15, 17: print "!"

When you run this, you should see something that looks a bit like a scene from a space invaders type of game.
It has the score and lives displayed at the top, some sort of red space invader thing in the middle, the player's gun turret down the bottom in gray, and a yellow bullet presumably halfway through flying up the screen.
Okay, it's all just coloured text characters for now, but it will serve until we learn about sprites and other forms of animation.

We have used three different commands:

You can see that we've used the colon (:) to separate the locate command from the print command.
We must use a colon if we want to place more than one command on one line, so that Basic4GL knows where the first one stops, and the second one begins.

Adding animation

Believe it or not, we are going to turn the previous program into a full working game. So don't forget to save it before moving on.
First though, we are going to learn how animate.
The basic principles of computer animation are the same as those of cartoon animation. It can be broken down into steps:

  1. You display a picture.
  2. Leave it on the screen for a short amount of time
  3. Then replace it with another picture which has changed slightly from the first.
  4. Go back to step 2

The brain interprets the continually changing still images as movement, and we have animation.

Let's illustrate with an example:

Sleep (2000)
cls:locate 10, 12: print "->": Sleep (200)
cls:locate 11, 12: print "->": Sleep (200)
cls:locate 12, 12: print "->": Sleep (200)
cls:locate 13, 12: print "->": Sleep (200)
cls:locate 14, 12: print "->": Sleep (200)

This is a very short animation. It simply moves "->" along the screen a few times and stops.
The new commands are:

As you can see, we display a picture (the "->") on the screen, leave it for a short amout of time, then replace it with another picture (the "->" slightly to the right of where it was). We do this five times, each time changing the position of the "->" by changing the column number in the locate command. This gives us our very simple animation.

Note: I put the "Sleep (2000)" line at the top to add a 2 second delay to the program before it started. This gives the monitor time to switch resolutions and get setup before the animation starts.

Improving the animation

The biggest problem with that animation is that we had to write a line of code for each "frame" of the animation.
In this case, it wasn't so bad, as we only had 5 frames, and each frame is very simple. But if you consider that even a simple 2D computer game can display several thousand frames, with dozens of objects moving around in each, then it becomes clear that we need a better way of doing things than writing one line of code for each frame!

One better approach is to use an "animation loop".
We will demonstrate this by extending the animation to move the "->" all the way from the left hand side of the screen to the right hand side.

Here's the program:

Sleep (2000)
dim x
for x = 0 to 38
    cls
    locate x, 12
    print "->"
    Sleep (75)
next

As you can see, this program is a lot shorter than 39 lines, but it still displays 39 frames of animation!
Let's examine what it's doing:

We've introduced a variable x, which we are using to store the column of the "->"s position. Notice that we are now positioning the cursor with "locate x, 12". That is we are calling the locate command, and passing x as the first parameter (the column number) and 12 as the second (the row number).

Next, we've created a for..next "loop".
A "loop" is a set of commands that get run more than once. In our case, this is all the commands between the for line and the next line.
The for..next loop is a particular type of loop. It has a loop variable, which counts from one number to another number:

for x = 0 to 38

In this case, the loop variable is x. It will count from 0 to 38 (inclusive). And for each time, it will run the contents of the loop.

So the first time it is effectively running:

x = 0
cls
locate x, 12
print "->"
Sleep (75)

And the second time:

x = 1
cls
locate x, 12
print "->"
Sleep (75)

And so on, up until the last time, where it runs:

x = 38
cls
locate x, 12
print "->"
Sleep (75)

And then it's finished.
So each time it runs the locate command, the cursor will end up one to the right of where it was last time, and the arrow will be drawn in a slightly different place.

As you can see, the for..next loop is very useful, and makes what would otherwise have been a long repeditive program very small and compact.
Here's some more for..next examples:

dim i
for i = 1 to 10
    printr i
next

 

dim i 
for i = 0 to 9
    printr 10 - i
    Sleep (1000)
next
print "Blastoff!"

 

dim i
printr "12 times table"
for i = 1 to 12
    printr i + " x 12 = " + (i * 12)
next

Back to space invaders

So let's take what we've learned and extend the space invaders program. We will be adding to the program bit by bit, so each example will show the new pieces in red.

First, we will start by storing the score and lives in some variables:

dim score, lives
lives = 3

color (255, 255, 255)
locate 0, 0: print "Score=" + score
locate 30, 0: print "Lives=" + lives
color (255, 50, 50)
locate 11, 12: print ">O<"
color (150, 150, 150)
locate 17, 23: print "<!>"
color (255, 255, 50)
locate 15, 17: print "!"

Notice that we set "lives" to it's initial value at the start of the program. The initial value for "score" is 0, so we don't have to change it.

Next we will take all the drawing code, and put it into an animation loop. The program will keep on drawing new animation frames until the user quits it by pressing escape. So remember to press escape when you want to get out of the program!

dim score, lives
lives = 3

while true
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    locate 11, 12: print ">O<"
    color (150, 150, 150)
    locate 17, 23: print "<!>"
    color (255, 255, 50)
    locate 15, 17: print "!"
    Sleep (75)
wend

This time we've used a while..wend loop, which is another sort of loop. Like the for..next loop, the instructions inside the loop are run multiple times. In this case the computer will keep on running the instructions forever, until the user quits the program by pressing escape.
(Not all while..wend loops run forever, there are many different ways to use them. The while..wend loop will be explained in more detail later.)

You may have noticed a couple of things:

We will fix the flickering later. For now we need to concentrate on making things move, and the obvious place to start is the player's turret, which will slide along the bottom of the screen.

To make the player's turret move, the computer needs to store information about the turret in variables. We need to decide exactly what information it needs to remember so that it can draw the turret on the screen and move it around when the player pressed the right keys.
We will make the turret moves as follows:

But first, we need to take another detour to introduce some more new concepts.

Constants, functions, keyboard input, and conditional statements

Before we can continue with the space invaders game we need to take a detour to learn a few new techniques. Don't forget to save the space invaders program away to disk first.

Constants

A constant is much like a variable.
It has a name (like true, TEXT_BUFFERED, VK_LEFT). It also has a value, which can be either a number or a string.
Unlike variables, the value of a constant never changes.

Basic4GL has a number of pre-defined "constants". These are constants that Basic4GL already knows about, without you having to tell it first.

Here are some pre-defined constants:

printr "m_pi = " + m_pi
printr "TEXT_BUFFERED = " + TEXT_BUFFERED
printr "VK_LEFT = " + VK_LEFT

Sometimes constants are used as an easy way to remember a commonly used number, such as Pi (stored in the constant m_pi).

Sometimes constants are used to associate a more meaningful name with a number. For example VK_LEFT stores the scan code of the left arrow key, which happens to be 37.
The computer knows that 37 means the left arrow key, but I'm less likely to remember that. But if I use the VK_LEFT constant in a program, I can easily see the program is trying to do.

We will be using the following constants to find out which keys are being pressed, so we can move the gun turret accordingly:

Functions

A function is like a command. It has a name which Basic4GL recognises, and sometimes has one or more parameters.
The main difference is that a function evaluates to a number or a string. We call this the "return value". We can use this value anywhere we would normally use a value, e.g. in an expression, as a parameter to another command or function. We can also assign it to a variable.

Some examples of functions:

printr "Half the square root of 2 is " + sqrt (2) / 2
printr "A random number: " + rnd ()                      
dim a$, helloLength
a$ = "Hello"
helloLength = len (a$)
print a$ + " has " + helloLength + " letters"

sqrt, rnd, and len are all functions.
As you can see, they all evaluate to a number or string that can be used like any other value.
Basic4GL has a large number of functions that calculate a number of different things.

Keyboard input

Right! Now we've had a very quick crash course on functions and constants, we can introduce the ScanKeyDown function.
We are going to use this function to find out whether keys are being pressed or not. The function takes a single parameter which is the "scan code" of the key that we are interested in. Because I don't know all the keyboard scan key codes off the top of my head, we're going to use special predefined constants: VK_LEFT, VK_RIGHT and VK_SPACE.

Here's how it all fits together:

while true
    cls
    print ScanKeyDown (VK_SPACE)
wend

(Tap the spacebar while the program is running to see it in effect.)

ScanKeyDown (VK_SPACE) evaluates to -1 when the spacebar is being pressed, and 0 when it isn't.
(-1 and 0 are actually special "boolean" values true and false. But we will worry about boolean values later.)

Here's another example:

while true
    cls
    if ScanKeyDown (VK_UP) then
        locate 19, 4: print "Up"
    endif
    if ScanKeyDown (VK_DOWN) then
        locate 18, 19: print "Down"
    endif
    if ScanKeyDown (VK_LEFT) then 
        locate 8, 12: print "Left"
    endif
    if ScanKeyDown (VK_RIGHT) then 
        locate 28, 12: print "Right"
    endif
wend

(In this one, you need to press the arrow keys while it's running to see what it does.)

This example uses the if..then..endif statement, which will be explained very soon.

Space invaders with keyboard input

Okay, load up the space invaders program, and modify it to look like the following (new bits are in red.)

dim score, lives, turretx
lives = 3
turretx = 19

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        
        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    locate 11, 12: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    color (255, 255, 50)
    locate 15, 17: print "!"
    Sleep (75)
wend

(Don't forget to update the locate command where it draws the turret!)

Okay, so now we can move the turret left and right by pressing the left and right arrow keys. This is our first interactive program! We're well on our way!

But first we have to understand this new if..then..endif statement.
This is called a conditional statement, because the computer sometimes runs the instructions between then and endif and sometimes doesn't. This all depends on the part between if and then, i.e. the "condition".

Let's examine the first one:

if ScanKeyDown (VK_LEFT) and turretx > 0 then

The "condition" here is: "ScanKeyDown (VK_LEFT) and turretx > 0"
This is like an expression (e.g. 1+2/3), except it evaluates to either true or false.

Conditional statements often read a lot like english. In english you might say "If it is raining then take your umbrella to work".
In this one we are telling the computer "if the player is pressing the left arrow key and the turret hasn't already moved as far as it will go, then move it slightly to the left".
Of course the computer doesn't speak english, so we need to put it in computer terms. So "the player is pressing the left arrow key" becomes "ScanKeyDown (VK_LEFT)", and "the turret hasn't already moved as far as it will go" becomes "turretx > 0".

When the computer runs this instruction and sees that the condition is true (i.e. the player is pressing the key and the turret can move further), then it will run the instructions between then and endif:

turretx = turretx - 1

Which will move our turret one column to the left (by subtracting one from it's column number).

Of course if the computer runs the if ... then instruction and the player is not pressing the left arrow key (or the turret has already reached the edge of the screen) then the computer will see that the condition is false, and it will not run the instructions between then and endif. Instead it will skip to just after the endif and keep running the program from there.

The second if..then..endif line is used to move the turret right. It works much the same way as moving left so I'll leave you to examine it and see how it works.

Buffered text output

It's time to deal with the flickering, as it is quite painful to watch!

The reason for the flickering is that the screen is updated every time we:

So each time a frame is drawn, everything dissapears, and then is drawn back in, one object at a time. So the ugly flicker is due to everything constantly dissapearing and re-appearing.

What we want is a way to prevent the user seeing our drawing until we have everything setup and ready to display. So the user never has to see a half drawn frame.
We can do this with the TextMode and DrawText commands.

dim score, lives, turretx
lives = 3
turretx = 19

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        
        
    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    locate 11, 12: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    color (255, 255, 50)
    locate 15, 17: print "!"
    DrawText ()
    Sleep (75)
wend

Flicker problem solved!

TextMode (TEXT_BUFFERED) switches the text drawing mode to buffered mode. This means that any changes to text on the screen will not be shown until we say so. And we tell the computer we're ready to display the changes with the DrawText () command.
Sorted!

Moving the alien

Next we'll add some code to move the space alien.
It will move from left to right. When it reaches the right hand side of the screen, we will start it again from the left.

dim score, lives, turretx, alienx
lives = 3
turretx = 19
alienx = 0

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
    alienx = alienx + 1
    if alienx > 37 then
        alienx = 0
    endif

    cls
    color (255, 255, 255)
    locate 0, 0: print "Score=" + score
    locate 30, 0: print "Lives=" + lives
    color (255, 50, 50)
    locate alienx, 12: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    color (255, 255, 50)
    locate 15, 17: print "!"
    DrawText ()
    Sleep (75)
wend

(Again, don't forget to update the locate line where we draw the alien.)

It should be getting easier to see how this works.
We've created a new variable to store the alien's position, called alienx. Like with the turret, this stores the column that the alien is currently positioned at.
We set it to zero at the start. We didn't really have to (as zero is the default anyway), but it doesn't hurt to be specific.
We move the alien one column to the right by adding one to it's column position.

alienx = alienx + 1.

Then we check whether the alien has reached the right hand side of the screen:

if alienx > 37 then

And if it has, we move it back to the left hand side:

alienx = 0

And that's all there is to it.

Moving the bullet

The object of the game is to shoot the alien, so we need to have a moving bullet.
This is a little bit more complicated than the other moving objects because it isn't fixed at a single height. Also, sometimes the bullet isn't on the screen at all.

Again, we need to decide what the computer needs to store in order to handle the bullet.
We will store the bullet in three variables:

So when the user presses space, we want to "fire" the bullet, by placing it on the screen just above the turret.
While the bullet is on the screen, it will fly up until it reaches the top, then it will be taken off the screen ready to be fired again.
When it comes time to draw the bullet, we only draw it if it's on the screen.

The modified program looks like this.

dim score, lives, turretx, alienx
dim bulletx, bullety, bulletOnScreen
lives = 3
turretx = 19
alienx = 0
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
    alienx = alienx + 1
    if alienx > 37 then
        alienx = 0
    endif
    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)
    locate alienx, 12: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif
    DrawText ()
    Sleep (75)
wend

(You may notice that while we can now shoot at the alien, we can't actually hit it! The bullet passes straight through. This is because we haven't told the computer to do anything when the bullet hits the alien.)

We're getting very close to a complete game now! A very simple one, but a complete one all the same.

The new code should be starting to make sense now, but you may be wondering what true and false mean.
You may remember we learnt earlier that conditional expressions evaluate to true or false. And that if..then..endif uses the condition between if and then to determine whether to run the instructions between then and endif.
Well we can actually store a condition result in a variable. So we set bulletOnScreen to true to say that the bullet is on the screen, and false to say that it isn't. We can then use it as the condition part of our if..then..endif statement.

We've used some if..then..endif statements inside other if..then..endif statements.
This is okay. The second if..then..endif simply become and instruction inside the first one.

We've also used an if..then..else..endif statement. So you may be wondering how this new else bit works.
It's actually just a simple extension of the standard if..then..endif statement. If the condition (between if and then) evaluates to true, the computer runs the instructions between then and else, otherwise it runs the instructions between else and endif instead.

To look at what it actually does:

If the bullet is on the screen "if bulletOnScreen then", we move it up the screen by subtracting one from the row position "bullety = bullety - 1". We also check whether the bullet has reached the top of the screen "if bullety < 1 then", and if so take it off the screen. "bulletOnScreen = false".

Inside the else..endif section we say what to do if the bullet is not on the screen. In this case the player can shoot it by pressing the spacebar, so we check whether the user is pressing space "if ScanKeyDown (VK_SPACE) then" and if so, place the bullet on the screen and position it above the gun turret:

bulletOnScreen = true
bullety = 22
bulletx = turretx + 1

Shooting the alien

To complete the game we need to make the bullet hit the alien.
So we have to tell the computer how to check when the bullet has hit the alien, and what to do when it happens.

We're going to keep it simple, and do the following:

So let's do it:

dim score, lives, turretx, alienx
dim bulletx, bullety, bulletOnScreen, i
lives = 3
turretx = 19
alienx = 0
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
    alienx = alienx + 1
    if alienx > 37 then
        alienx = 0
    endif
    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)
    locate alienx, 12: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif

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

To hit the alien, the bullet has to be on the screen, at the same row as the alien, and on a column between alienx and alienx + 2 (because the alien is 3 columns wide).
We animate the exploding alien by repeatedly drawing diagonal lines over the top, using a for..next loop to repeat the process 10 times, and a Sleep (50) delay for timing.

We placed all this code after the regular drawing code, so that everything is already on the screen when we perform our animation.

After our animation, we remove the bullet from the screen, reset the alien to the left side, add 100 hundred points and pause for 1 second.

Program complete (well, sortof!)

And that's it. That's our text based space alien game.

Currently it has a lot of restrictions, such as there's only one alien, and it doesn't shoot back (so there's not much point in having 3 lives...). But it's a playable game all the same.

Along the way we've learnt a number of programming concepts and techniques, including:

Notice how we built up the program in stages.
At each stage we had a working program that we could run and examine the results of. This allowed us to check each stage, and make sure that the code was doing what we expected before moving on. It also kept things interesting (I hope!), as we were able to see the difference that each of our changes made.

As a final touch we will add a little variety and make the alien fly past at different heights.
We can use the rnd() function, which returns a random number between 0 and about 32,000. A bit like throwing a 32,000 sided dice I guess.
With a bit of maths we can turn this into a number between 1 and 22 like so:

alieny = rnd () % 22 + 1

Here's the final version:

dim score, lives, turretx, alienx, alieny
dim bulletx, bullety, bulletOnScreen, i
lives = 3
turretx = 19
alienx = 0
alieny = 12
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
    alienx = alienx + 1
    if alienx > 37 then
        alienx = 0
        alieny = rnd () % 22 + 1
    endif
    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)
    locate alienx, alieny: print ">O<"
    color (150, 150, 150)
    locate turretx, 23: print "<!>"
    if bulletOnScreen then
        color (255, 255, 50)
        locate bulletx, bullety: print "!"
    endif

    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
    
    DrawText ()
    Sleep (75)
wend

Where to now?

Don't worry too much if this all seems like a lot to take in in one setting. It's not important to absorb it all the first time through. The best way to learn is by example and experimentation, so don't be afraid to play around with the examples or try and invent some of your own. You won't break anything!

You may want to try skimming through the Programmer's guide (choose "Programmer's guide" from the "Help" menu in Basic4GL), which lists a number of functions and commands used in Basic4GL for things like joystick and mouse input, resizing the text area or changing the font.

You may also want to play with the demo programs that are distributed with Basic4GL (click "Open..." from the "Program" menu in Basic4GL). Just keep in mind that some of the demo programs use programming features that we haven't learnt yet, and many of them use OpenGL which is a huge topic in it's own right.)

We will finish off this first part of the tutorial with some more example programs/games to try out and experiment with. See if you can figure out how they work, or modify/extend them into your own customised versions.

Bricks

dim score, balls
dim ballx#, bally#, ballxd#, ballyd#
dim paddlex
dim col, line, a$

paddlex = 18
ballx# = 20
bally# = 10
ballxd# = 0
ballyd# = 1
score = 0
balls = 3

color (255, 255, 255)
locate 0, 0: print "Score: 0"
locate 30, 0: print "Balls: 3"
locate 0, 2
color (255, 100, 100)
for line = 1 to 7
    for col = 1 to 20
        print "[]"
    next
next

TextMode (TEXT_BUFFERED)

while true
    color (255, 255, 255)
    locate 0, 0: print "Score: " + score
    locate 30, 0: print "Balls: " + balls
    locate paddlex, 23: print"     "
    if ScanKeyDown (VK_LEFT) and paddlex > 0 then
        paddlex = paddlex - 1
    endif
    if ScanKeyDown (VK_RIGHT) and paddlex < 35 then
        paddlex = paddlex + 1
    endif
    Color (200, 200, 200)
    locate paddlex, 23: print"-----"
    
    Color (255, 255, 255)
    locate ballx#, bally#: print " "
    a$ = CharAt$ (ballx# + ballxd#, bally# + ballyd#)
    if a$ = "-" then 
        ballyd# = -ballyd#
        ballxd# = (int (ballx#) - paddlex - 2) / 2.0
        a$ = " "
    endif
    ballx# = ballx# + ballxd#
    bally# = bally# + ballyd#
    if ballx# <= 0 or ballx# > 39 then ballxd# = -ballxd# endif
    if bally# <= 0 then ballyd# = -ballyd# endif
    if bally# > 23 then
        DrawText ()
        Sleep (1000)
        balls = balls - 1
        if balls > 0 then
            locate paddlex, 23: print"     "
            paddlex = 18
            ballx# = 20
            bally# = 10
            ballxd# = 0
            ballyd# = 1
        else
            Color (255, 255, 255)
            locate 15, 12: print "Game Over!"
            DrawText ()
            end
        endif
    endif           

    locate ballx#, bally#: print "o"
    if a$ = "[" or a$ = "]" then 
        ballyd# = -ballyd# 
        score = score + 1    
    endif
    DrawText ()
    Sleep (50)
wend

Road

dim roadx, carx, score
dim delay#
dim i

roadx = 13
carx = 20
delay# = 100

Color (200, 200, 200)
for i = 1 to 23
    locate roadx, i: print "**          **"
next

TextMode (TEXT_BUFFERED)

while true
    roadx = roadx + rnd () % 3 - 1
    if roadx < 0 then roadx = 0 endif
    if roadx > 40 - 14 then roadx = 40 - 14 endif
    Color (200, 200, 200)
    locate 0, 24: printr
    locate roadx, 23: print "**          **"
    
    score = score + 1
    Color (255, 255, 0)
    locate 0, 0: print "Score = " + score

    if ScanKeyDown (VK_LEFT) then
        carx = carx - 1
    endif
    if ScanKeyDown (VK_RIGHT) then 
        carx = carx + 1
    endif
    if CharAt$ (carx, 12) <> " " then
        Color (255, 100, 10)
        locate carx - 1, 11: print "X X"
        locate carx - 1, 12: print " X "
        locate carx - 1, 13: print "X X"
        Color (255, 255, 255)
        locate 15, 12: print "Game Over!"
        DrawText ()        
        end
    endif
    Color (255, 255, 255)
    locate carx, 12: print "O"
    
    DrawText ()
    Sleep (delay#)
    delay# = delay# - 0.25
wend

Guess a number

dim guesses, number, guess
number = rnd () % 100 + 1

Color (255, 255, 255)
printr "I've chosen a number between 1 and 100"
printr "See if you can guess it in 10 guesses"
printr "To make a guess, type in the number and"
printr "press Enter"
printr

for guesses = 1 to 10
    print "Guess " + guesses + ": "
    guess = val (Input$ ())
    if guess = number then
        printr
        printr "Correct!"
        end
    endif
    if number > guess then 
        printr "The number is greater than " + guess
    else
        printr "The number is less than " + guess
    endif
next

printr
printr "Out of guesses!"
printr "The number was " + number

Worm

dim x, y, xd, yd, key, score
x = 20: y = 12
xd = 0: yd = -1
Color (0, 255, 0)
while true
    key = InScanKey ()
    if key = VK_LEFT and xd <> 1 then
        xd = -1: yd = 0
    endif
    if key = VK_RIGHT and xd <> -1 then
        xd = 1: yd = 0
    endif
    if key = VK_UP and yd <> 1 then 
        xd = 0: yd = -1
    endif
    if key = VK_DOWN and yd <> -1 then
        xd = 0: yd = 1
    endif
    x = x + xd
    y = y + yd
    score = score + 1
    if CharAt$ (x, y) <> " " then
        Color (255, 255, 255)
        locate 15, 11: print "Game Over!"
        locate 15, 13: print "You scored " + score
        end
    endif
    locate x, y: print "X"
    Sleep (75)
wend