GLBasic User Manual

Main sections

10 The First Game

One More



About the Game


The original game (I think) was part of a game called LOGO by Starbyte for the Amiga500. The aim is to remove all the colored dots from a playfield. Whenever the player clicks a block, this block and the four blocks surrounding the block toggle their state.
Thus, clicking the center point of this playfield:
###
###
###

Would cause it to look like:
#+#
+++
#+#

This game is pretty easy to program and leaves a lot of space for your own ideas to finish it. It's also nice since we can have computer generated levels. We simply have the computer randomly click some points. It's rather difficult to play once the computer has clicked more than 10 times or so...

New Project


After starting GLBasic, select the button "new project" and give it a name and a path for the project. e.g. "..\GLBasic\Projects\OneMore"

The Main Program


The basic code will look like this (pseudo-code):

main:
IF level_complete THEN create_new_level
IF mouse_click THEN change_clicked_stones
show_playfield
GOTO main


First we need a playfield to store whether a block is activated or deactivated (cleared). We use the value 0 for deactivated since the DIM command always sets all values to 0 automatically. Thus, the first IF statement will be evaluated and a new level will be created for us.

// --------------------------------- //
// Project: OneMore

DIM playfield[10][10]
level = 0

Now, we have an array called "playfield" that is 10x10 numbers in size. We can address them with the indices 0-9. See the DIM command.

   // Main game
main:
MOUSESTATE mx, my, b1, b2
PRINT "<=", mx, my-8
SHOWSCREEN
GOTO main
END


Compile and run it. It looks nice, but it doesn't do much. We have a mouse pointer that we can move using the mouse. Take a look at the reference for all the commands we used here. The PRINT line is our mouse-pointer. You can load and use a nice sprite image for it later. The my-8 is because the font is 16 pixels high, and I subtracted half of it. Don't forget the SHOWSCREEN!

Showing the Playfield



Now we want to show the playfield data so we add a new subroutine (SUB). Press the SUB-button and enter the name: ShowPlayfield

// ------------------------------------------------------------- //
// -=# SHOWPLAYFIELD #=-
// ------------------------------------------------------------- //
SUB ShowPlayfield:
LOCAL x, y, color[]

DIM color[2]
color[0] = RGB( 50, 50, 255) // Not set
color[1] = RGB( 50, 255, 50) // Set

DRAWRECT 0,0, 320, 320, RGB(255,255,255)
FOR x=0 TO 9
FOR y=0 TO 9
DRAWRECT x*32+1, y*32+1, 30, 30, color[playfield[x][y]]
NEXT
NEXT
PRINT "Clicks: "+clicks, 360, 120
PRINT "Level: "+level, 360, 160
ENDSUB // SHOWPLAYFIELD


Here we create a local array called color and assign a blue color (50,50,255) to the first index [0] and a green color (50,255,50) to the second index [1]. Next, a white rectangle gets drawn that blanks out the playfield area.
We then do a double FOR loop, One for x and one for each y of each x.
In this loop we draw a filled rectangle for each cell of our playfield-array. We use color[ playfield[x][y] ] for the RGB value. Thus a zero in that array location causes a blue colored rectangle, a 1 causes a green colored rectangle.
Also we inform the user about the current level and the number of times they clicked for this level.

Add this line to the main game below the "MOUSESTATE" line:
   GOSUB ShowPlayfield

and run the game. Looks better now, but there's not much interactivity here, right?

Interactivity


Next, add a function with the FUNCTION button and give the name "Change" and the arguments: x, y
This is the point where we want to change the playfield array.
// ------------------------------------------------------------- //
// -=# CHANGE #=-
// ------------------------------------------------------------- //
FUNCTION Change: x, y
// These values are defined LOCAL:
// x, y
IF x>=0 AND x<10 AND y>=0 AND y<10
set = playfield[x][y] // Get old state

IF set = TRUE
set=FALSE
ELSE
set=TRUE
ENDIF
// or simply:
// set = 1-set
playfield[x][y]=set // Set new state
ENDIF
ENDFUNCTION

In this function we will try to make the playfield[x][y] be 1 if it was 0 and vice versa. Before we change it though, we check if the point (x, y) is out of the 0-9, 0-9 bounds. If it is out of this range, we don't change anything or we will get an error from the running program: DIM out of range. This error means we've tried to update a value in our array which doesn't exist (the position we tried to update is "out of (the) range" of the array.

Now add another function with the FUNCTION button. Give it the name "Click" and the argument list: x, y
This is the point where the player clicks will be processed.

// ------------------------------------------------------------- //
// -=# CLICK #=-
// ------------------------------------------------------------- //
FUNCTION Click: x, y
// These values are defined LOCAL:
// x, y
clicks=clicks+1
Change(x-1, y)
Change(x+1, y)
Change(x, y)
Change(x, y-1)
Change(x, y+1)
ENDFUNCTION

Here, we Change() the value of the clicked point, as well as the points surrounding it. Also we increase a variable (clicks) indicating how often the player has clicked in this level.
Now change the main program, so it looks like:

DIM playfield[10][10]

level = 0

// Main game
start:
MOUSESTATE mx, my, b1, b2
IF b1
IF mousefree THEN Click(mx/32, my/32)
mousefree=FALSE
ELSE
mousefree=TRUE
ENDIF

GOSUB ShowPlayfield
PRINT "<=", mx, my-8
SHOWSCREEN
GOTO start
END


The mousefree variable tell us whether the user is still holding the mouse button down from the last time we processed the Click() function. If the player is still holding the button down, we don't reprocess the click again and again, but wait until they have released the mouse and then re-clicked it.
Now run the program. It should work nicely now. Get a feeling for how the game works. Click randomly a few times and try to clear the board again. Is it fun?

Levels


Add a new SUB called "NewLevel":

// ------------------------------------------------------------- //
// -=# NEWLEVEL #=-
// ------------------------------------------------------------- //
SUB NewLevel:
LOCAL x, y, i

// Clear playfield - just to be sure
FOR x=0 TO 9
FOR y=0 TO 9
playfield[x][y]=FALSE
NEXT
NEXT

level=level+1
// Do random clicks
FOR i=0 TO level
Click(RND(9), RND(9))
NEXT
clicks=0
GOSUB ShowPlayfield
PRINT "Level: "+level + " - get ready", 0, 360
SHOWSCREEN
MOUSEWAIT
ENDSUB // NEWLEVEL

In this subroutine, we first clear the playfield to really make sure it's cleared. Next we increase the level variable. The computer then clicks some random points (more are clicked as the level being played increases) in the playfield and we reset the number of clicks occurred back to 0.
The computer can now create a level but it doesn't know when the level is cleared. We need a function that tells us when this has occurred.
Add a function called "IsLevelComplete":
// ------------------------------------------------------------- //
// -=# ISLEVELCOMPLETE #=-
// ------------------------------------------------------------- //
FUNCTION IsLevelComplete:
LOCAL x, y

FOR x=0 TO 9
FOR y=0 TO 9
IF playfield[x][y]=TRUE THEN RETURN FALSE
NEXT
NEXT
RETURN TRUE
ENDFUNCTION


Here we simply loop through all the locations in our playfield and return FALSE if any of the playfield's cells is set to 1 (=TRUE). If the loop finishes without returning from the function, there must be no more green dots left, thus the board is clear and we return TRUE at the end of the function.

In order to test the whole game insert this code:
      IF IsLevelComplete()
GOSUB NewLevel
mousefree=FALSE
ENDIF

to the main program after the main: jumpmark. Run the game. How does it feel to have written a complete computer game? If you add
BLENDSCREEN "images"+level".bmp"

to the NewLevel subroutine, the game is even better than the original Amiga game was back then! Doesn't that feel great? You are the master of the computer now - you tell it what to do and it follows your commands. You can create worlds with a few lines of code.

Here's the complete source code in case you might have missed something:

// --------------------------------- //
// Project: OneMore

DIM playfield[10][10]

level = 0

// Main game
start:
IF IsLevelComplete()
GOSUB NewLevel
mousefree=FALSE
ENDIF

MOUSESTATE mx, my, b1, b2
IF b1
IF mousefree THEN Click(mx/32, my/32)
mousefree=FALSE
ELSE
mousefree=TRUE
ENDIF

GOSUB ShowPlayfield
PRINT "<=", mx, my-8
SHOWSCREEN
GOTO start
END

// ------------------------------------------------------------- //
// -=# SHOWPLAYFIELD #=-
// ------------------------------------------------------------- //
SUB ShowPlayfield:
LOCAL x, y, color[]

DIM color[2]
color[0] = RGB( 50, 50, 255) // Not set
color[1] = RGB( 50, 255, 50) // Set

DRAWRECT 0,0, 320, 320, RGB(255,255,255)
FOR x=0 TO 9
FOR y=0 TO 9
DRAWRECT x*32+1, y*32+1, 30, 30, color[playfield[x][y]]
NEXT
NEXT
PRINT "Clicks: "+clicks, 360, 120
PRINT "Level: "+level, 360, 160
ENDSUB // SHOWPLAYFIELD


// ------------------------------------------------------------- //
// -=# CLICK #=-
// ------------------------------------------------------------- //
FUNCTION Click: x, y
// These values are defined LOCAL:
// x, y
clicks=clicks+1
Change(x-1, y)
Change(x+1, y)
Change(x, y)
Change(x, y-1)
Change(x, y+1)
ENDFUNCTION


// ------------------------------------------------------------- //
// -=# CHANGE #=-
// ------------------------------------------------------------- //
FUNCTION Change: x, y
// These values are defined LOCAL:
// x, y
IF x>=0 AND x<10 AND y>=0 AND y<10
set = playfield[x][y]

IF set = TRUE
set=FALSE
ELSE
set=TRUE
ENDIF
// or simply:
// set = 1-set
playfield[x][y]=set
ENDIF
ENDFUNCTION


// ------------------------------------------------------------- //
// -=# NEWLEVEL #=-
// ------------------------------------------------------------- //
SUB NewLevel:
LOCAL x, y, i

// Clear playfield - just to be sure
FOR x=0 TO 9
FOR y=0 TO 9
playfield[x][y]=FALSE
NEXT
NEXT

level=level+1

// Do random clicks
FOR i=0 TO level
Click(RND(9), RND(9))
NEXT
clicks=0
GOSUB ShowPlayfield
PRINT "Level: "+level + " - get ready", 0, 360
SHOWSCREEN
MOUSEWAIT
ENDSUB // NEWLEVEL


// ------------------------------------------------------------- //
// -=# ISLEVELCOMPLETE #=-
// ------------------------------------------------------------- //
FUNCTION IsLevelComplete:
LOCAL x, y

FOR x=0 TO 9
FOR y=0 TO 9
IF playfield[x][y]=TRUE THEN RETURN FALSE
NEXT
NEXT
RETURN TRUE
ENDFUNCTION

See also...