BASIC, prolific during the late Seventies and Eighties due to the popularity of the 8-bit BBC Micro, was the language that kickstarted much of the software industry we know today. Many programmers then moved on to more complex and powerful languages like C/+/++/Java etc, games consoles took over the home computer market and BASIC was all but forgotten. Fast-forward 30 years and it’s easy to see why the UK government is desperately trying to get kids coding again – resources are now very thin on the ground and we’re outsourcing our programming requirements like there’s no tomorrow. There’s really never been a better time to become a programmer. You’ll find no better introduction than learning to program a game, so we’ll start with the classic bat-and-ball genre, but with a twist or two, of course.
This is a slightly extended version of the tutorial in issue 157, on sale Monday 24th August, including some metal balloons, an intro screen and a few other tweaks. To get started you will need to install FUZE BASIC V3 for Linux and download the graphics required for the game from http://ift.tt/1JnjrWD and http://ift.tt/1MJLaI1.
01 Get started
Once you have installed FB3, you will have a directory on your desktop called fuze-basic – within is a programs folder. Unzip ‘spike.zip’ in here. Now start FUZE BASIC from your applications menu; if in Ubuntu then use the search tool and enter ‘fuze’. The first thing you’ll see is the retro C64 immediate mode – you can change this using Shift+Tab. There are only two skins right now but more will be added soon, so keep an eye on the FUZE Lair for updates. Right, let’s get started. Press F2 or type EDIT in immediate mode to get to the FB editor. Note: you can also get a list of immediate mode commands by entering HELP, and you can use the help icon at any time to view the help system. This is a great way explore the language and try out new commands.
Start typing in the code below, and remember to save your work regularly. Our first section calls the routines to set up the main graphics and variables. The initial CYCLE command starts the main loop, and this loop covers the gameplay to game over. The next CYCLE is for the level and the UNTIL hhLives check is to see if we have used up all our lives. Next, the procedures to display our character Spikey, the trampoline and the balloons are called. Finally, if we are out of lives then “GAME OVER” is displayed and it starts over.
REM Spikey POP
PROC setup
PROC sprites
CYCLE
PROC intro
level = 1
hhLives = 3
hhScore = 0
CYCLE
PROC newLevel
PROC getready
UNTIL hhLives <= 0 OR levComp CYCLE
PROC displayInfo
PROC hedgeHog
PROC balloon
plotSprite (tramp, MOUSEX, trampY, 0)
UPDATE
REPEAT
IF hhLives <= 0 THEN BREAK
level = level + 1
REPEAT
INK = Red
PROC hideSprites
CLS
text1$ = “GAME OVER!”
printAt (tWidth / 2 – LEN (text1$) / 2, tHeight / 2); text1$;
UPDATE
WAIT (2)
CLS
REPEAT
END
02 Reset a new level
It’s worth pointing out that the program will only generate errors until you have finished typing the whole thing in. It is a fairly large program at 350 or so lines, and as such is going to take a while to type in. You’ll most likely need to debug it a bit afterwards, in case you make a few errors. Be patient and stick with it to the end and you’ll be glad you did, as once it is working you’ll understand every command within it.
Next are the variables that we need to reset at the start of each new level to make sure everything starts in the right place:
DEF PROC newLevel
bals = maxB
metalCt = level – 1
IF metalCt >= 5 THEN metalCt = 5
PROC newLifeVariables
PROC hideSprites
plotImage (back1, 0, -20)
levComp = 0
PROC setupBals
ENDPROC
03 Reset variables
Now enter the variables to reset every time a life is lost, or at the beginning of a new level:
DEF PROC newLifeVariables
hhX = gWidth / 2
hhY = gHeight / 2.8
hhYspd = .1
hhAngle = 0
hXdiff = 0
hXpow = 0
hYpow = .51
hhGrv = 0
hhXdirection = .1
hhYdir = 0
trampX = gWidth / 2
ENDPROC
04 Check the hedgehog
This section is where the hedgehog action is at. The first few lines check the position, the size and if Spikey has hit anything – spriteCollidePP(hhID,1). PP means pixel perfect, and the two variables are for the ID of the sprite we are checking and the accuracy from 1 (close) to 16 (boundary box).
DEF PROC hedgeHog
hhGrv = (((gHeight – hhY / hYpow) / 80))
hhW = getSpriteW (hhID)
hhH = getSpriteH (hhID)
hCol = spriteCollidePP (hhID, 1)
IF hCol >= b(0, 0) AND hCol <= b(maxB, 0) THEN
IF NOT b(hCol, bPop) THEN
IF ABS (b(hCol, bX) – hhX) > 20 THEN
hXpow = (b(hCol, bX) – hhX) / 100 * RND (10)
hXpow = – hXpow
ENDIF
05 Check the balloons
Now we check to see which row of balloons has been popped – from the top row, we add 200 points, from the middle we add 100 and then the bottom is just 50. We also make a small speed adjustment so that each time a balloon is hit, the speed slowly increases. Next we check for a metal balloon and, if we hit one, then just reverse the direction as these cannot be popped.
hhYdir = NOT hhYdir
b(hCol, bPop) = 1
IF hCol >= 0 AND hCol <= 19 THEN
hhYspd = hhYspd + 0.03 + level / 100
hhScore = hhScore + 200
ENDIF
IF hCol >= 20 AND hCol <= 39 THEN
hhYspd = hhYspd + 0.01 + level / 200
hhScore = hhScore + 100
ENDIF
IF hCol >= 40 AND hCol <= 59 THEN
hhYspd = hhYspd + 0.005
hhScore = hhScore + 50
ENDIF
bals = bals – 1
IF bals <= 0 THEN levComp = 1
ENDIF
ENDIF
IF hCol >= mb(0, 0) AND hCol <= mb(metalCt, 0) THEN
hXpow = hXpow + RND (0) / 10
hXpow = – hXpow
hhYdir = NOT hhYdir
ENDIF
06 Calculate bounce angle
This section checks to see if we have hit the trampoline and adjusts our bounce (with hXdiff) accordingly. If Spikey goes below the bottom of the screen, that means he has missed the trampoline and so the lives are reduced. If the screen sides are hit, we reverse the bounce, adjust the sprite angle, then plot the sprite.
IF hhYdir = 0 THEN
hhY = hhY – hhGrv
IF hCol = tramp THEN
hhYdir = 1
hXdiff = (hhX – MOUSEX) / 3
IF ABS (hXdiff) > trampW / 6 THEN
hYpow = .51
hhYspd = .1
hXdiff = 0
hXpow = .1
ENDIF
hYpow = hYpow + .05
hXpow = hXpow + hXdiff / 20
hhAngle = hhAngle + hXdiff / 50
setSpriteAngle (hhID, hhAngle)
ENDIF
ENDIF
IF hhYdir = 1 THEN
hhY = hhY + hhGrv
IF hhGrv <= .5 OR hhY >= gHeight THEN hhYdir = 0
ENDIF
IF hhY <= 0 THEN
hhLives = hhLives – 1
IF hhLives > 0 THEN
PROC newLifeVariables
PROC getready
ELSE
gameOver = 1
ENDIF
ENDIF
IF hYpow >= 1.6 THEN hYpow = 1.6
hhAngle = hhAngle + hXdiff / 50
setSpriteAngle (hhID, hhAngle)
IF hhX <= hhW / 2 OR hhX >= gWidth – hhW / 2 THEN hXpow = – hXpow
hhX = hhX + hXpow
plotSprite (hhID, hhX, hhY, 0)
ENDPROC
07 Wait for player to start
Now we enter the code that flashes Spikey and waits for a mouse click before dropping him to the floor, allowing the player to position the trampoline.
DEF PROC getready
CYCLE
getMouse (a, b, mousebutton)
hideSprite (hhID)
UPDATE
plotSprite (hhID, hhX, hhY, 0)
UPDATE
plotSprite (tramp, MOUSEX, trampY, 0)
REPEAT UNTIL mousebutton
ENDPROC
08 Move the balloons
The balloon procedure sets up an animation loop that increases the animation sequence every four frames (IF bAnCtr > 4 THEN). Our balloons will each be stored in an array, which we’ll look at in the next step. It then checks to see if a balloon is in pop mode, and if so we rotate it, decrease its size and drop its Y position so that it falls down the screen, shrinking and spinning. If it is not due to be popped, we move each balloon in its direction and then check to see if it goes off the side of the screen, and if so prepare it for display on the opposite side of the screen. Finally, we plot any metal balloons if they are active.
DEF PROC balloon
bAnCtr = bAnCtr + 1
IF bAnCtr > 4 THEN
bAnID = bAnID + bAnDir
bAnCtr = 0
ENDIF
IF bAnID >= 6 OR bAnID <= 0 THEN bAnDir = – bAnDir
FOR I = 0 TO maxB CYCLE
IF b(I, bPop) = 1 THEN
b(I, bSize) = b(I, bSize) – .1
b(I, bY) = b(I, bY) – 2
b(I, bAngle) = b(I, bAngle) + 10
setSpriteSize (b(I, ID), b(I, bSize) * 16)
setSpriteAngle (b(I, ID), b(I, bAngle))
IF b(I, bSize) <= 0 THEN
hideSprite (b(I, ID))
b(I, bActive) = 0
ENDIF
ENDIF
IF b(I, bActive) THEN
plotSprite (b(I, ID), b(I, bX), b(I, bY), bAnID)
b(I, bX) = b(I, bX) + b(I, bSpd)
IF b(I, bSpd) < 0 THEN
IF b(I, bX) <= bMinX THEN b(I, bX) = bMaxX
ENDIF
IF b(I, bSpd) > 0 THEN
IF b(I, bX) >= bMaxX THEN b(I, bX) = bMinX
ENDIF
ENDIF
IF I <= metalCt THEN
IF mb(I, bActive) THEN plotSprite (mb(I, ID), mb(I, bX), mb(I, bY), bAnID)
REPEAT
ENDPROC
09 Display the score
This chunk of code will display the score, number of lives remaining and the level information:
DEF PROC displayInfo
INK = Yellow
printAt (0, 0); “Score “;
INK = Red
PRINT hhScore
INK = Yellow
printAt (tWidth – 10, 0); “Lives “;
INK = Red
PRINT hhLives
INK = Yellow
printAt (tWidth / 2 – 4, 0); “Level “;
INK = Red
PRINT level
ENDPROC
10 Set up the balloons
The initial balloon data is essential to get things running smoothly. Here we set up an array to store all the information needed about each balloon. In this case, we have the sprite ID, X position, Y position, speed, score value, size and then active and pop mode states. Finally the same is setup for the metal balloon variables.
DEF PROC setupBals
bI = 20
bMinX = – bW * 2
bMaxX = gWidth + bW * 2
bStep = (bMaxX – bMinX) / 20
ctr = 0
FOR n = bMinX TO bMaxX – bW STEP bStep CYCLE
b(ctr, bY) = gHeight – bH
b(ctr + 20, bY) = gHeight – bH * 2.5
b(ctr + 40, bY) = gHeight – bH * 4
b(ctr, bX) = n
b(ctr + 20, bX) = n
b(ctr + 40, bX) = n
b(ctr, bSpd) = .5
b(ctr + 20, bSpd) = -.2
b(ctr + 40, bSpd) = .1
b(ctr, bScore) = 200
b(ctr + 20, bScore) = 100
b(ctr + 40, bScore) = 50
b(ctr, bSize) = 10
b(ctr + 20, bSize) = 10
b(ctr + 40, bSize) = 10
b(ctr, bActive) = 1
b(ctr + 20, bActive) = 1
b(ctr + 40, bActive) = 1
b(ctr, bPop) = 0
b(ctr + 20, bPop) = 0
b(ctr + 40, bPop) = 0
plotSprite (b(ctr, ID), b(ctr, bX), b(ctr, bY), 0)
plotSprite (b(ctr + 20, ID), b(ctr + 20, bX), b(ctr + 20, bY), 0)
plotSprite (b(ctr + 40, ID), b(ctr + 40, bX), b(ctr + 40, bY), 0)
ctr = ctr + 1
UPDATE
REPEAT
IF level < 6 THEN
mBgap = gWidth / level
ELSE
mBgap = gWidth / 6
ENDIF
IF level > 1 THEN
FOR num = 0 TO metalCt CYCLE
mb(num, bY) = gHeight / 2
mb(num, bX) = mBgap * num
mb(num, bSpd) = 0
mb(num, bScore) = 1000
mb(num, bSize) = 10
mb(num, bPop) = 0
IF num > 0 THEN mb(num, bActive) = 1
REPEAT
ENDIF
ENDPROC
11 Set up the game itself
The main setup section configures the screen and update settings, loads the background image and defines the main variables, including the balloon arrays and indexes that we have already seen in some of the previous steps. It is worth revisiting this section once you have finished putting Spikey Pop together, because you can test yourself by changing the size of the array and modifying the other code accordingly.
DEF PROC setup
setMode (1024, 600)
FULLSCREEN = 0
updateMode = 0
mouseOff
maxB = 60
back1 = loadImage (“back1.png”)
back = loadImage (“back.png”)
DIM b(maxB, 10)
DIM mb(6, 10)
ID = 0
bActive = 1
bX = 2
bY = 3
bSpd = 4
bAngle = 5
bSize = 6
bScore = 7
bPop = 8
balloon = 0
bAnID = 0
bAnDir = 1
bAnCtr = 0
ENDPROC
12 Draw the sprites
The order that sprites are created in is the order in which they will be displayed on the screen. Therefore if you want a sprite to always be on top of every other sprite, create it last. You must set a transparent colour using setSpriteTrans (pop, 255, 0, 255); this is the one colour that will not be displayed on-screen. Setting the transparent colour means you can make sure that sprites don’t obscure others when they overlap. Understanding the setSpriteOrigin command is important. The default is bottom-left, so you need to use offsets if you want to control the sprite from its middle. Far more convenient is to set the origin using: setSpriteOrigin (pop, getSpriteW (pop) / 2 and then getSpriteH (pop) / 2), as this sets the origin at the absolute centre of the sprite. Other important sprite commands include setSpriteSize, setSpriteAngle and advanceSprite – more information can be found in the Programmer’s Reference Guide.
DEF PROC sprites
FOR n = 0 TO maxB CYCLE
b(n, ID) = newSprite (7)
FOR nn = 1 TO 7 CYCLE
num$ = STR$ (nn)
IF n >= 0 AND n <= 19 THEN loadSprite (“spe” + num$ + “.png”, b(n, ID), nn – 1)
IF n >= 20 AND n <= 39 THEN loadSprite (“spc” + num$ + “.png”, b(n, ID), nn – 1)
IF n >= 40 AND n <= 59 THEN loadSprite (“spd” + num$ + “.png”, b(n, ID), nn – 1)
REPEAT
setSpriteTrans (b(n, ID), 255, 0, 255)
setSpriteOrigin (b(n, ID), getSpriteW (b(n, ID)) / 2, getSpriteH (b(n, ID)) / 2)
REPEAT
FOR n = 0 TO 6 CYCLE
mb(n, ID) = newSprite (7)
FOR nn = 1 TO 7 CYCLE
num$ = STR$ (nn)
loadSprite (“spb” + num$ + “.png”, mb(n, ID), nn – 1)
REPEAT
setSpriteTrans (mb(n, ID), 255, 0, 255)
setSpriteOrigin (mb(n, ID), getSpriteW (mb(n, ID)) / 2, getSpriteH (mb(n, ID)) / 2)
REPEAT
bH = getSpriteH (b(0, 0))
bW = getSpriteH (b(0, 0))
tramp = newSprite (1)
loadSprite (“tramps.png”, tramp, 0)
setSpriteTrans (tramp, 255, 0, 255)
setSpriteOrigin (tramp, getSpriteW (tramp) / 2, 0)
trampY = -5
trampH = 20
trampW = getSpriteW (tramp)
trampL = trampW / 2
trampR = gWidth – trampW / 2
hhID = newSprite (1)
loadSprite (“hedge.png”, hhID, 0)
setSpriteTrans (hhID, 255, 0, 255)
setSpriteOrigin (hhID, getSpriteW (hhID) / 2, getSpriteH (hhID) / 2)
setSpriteSize (hhID, 110)
spikey = newSprite (3)
FOR n = 1 TO 3 CYCLE
num$ = STR$ (n)
loadSprite (“spikey” + num$ + “.png”, spikey, n – 1)
REPEAT
setSpriteTrans (spikey, 255, 0, 255)
setSpriteOrigin (spikey, getSpriteW (spikey) / 2, getSpriteH (spikey) / 2)
pop = newSprite (1)
loadSprite (“pop.png”, pop, 0)
setSpriteTrans (pop, 255, 0, 255)
setSpriteOrigin (pop, getSpriteW (pop) / 2, getSpriteH (pop) / 2)
ENDPROC
13 Clean up
This final routine clears and resets all the sprites, and is needed at the end of each level and at the start of a new life:
DEF PROC hideSprites
FOR n = 0 TO metalCt CYCLE
setSpriteSize (mb(n, ID), 100)
setSpriteAngle (mb(n, ID), 0)
hideSprite (mb(n, ID))
REPEAT
FOR n = 0 TO maxB CYCLE
setSpriteSize (b(n, ID), 100)
setSpriteAngle (b(n, ID), 0)
hideSprite (b(n, ID))
REPEAT
hideSprite (tramp)
hideSprite (hhID)
ENDPROC
14 Introduce the game
Finally, the intro section displays our title sprites with a simple animation and waits for a mouse click to continue:
DEF PROC intro
angle = 0
anglestep = .2
sID = 0
scale = 100
scaleDir = .1
text1$ = “Click mouse to play – use mouse to control”
timecount = 0
plotImage (back, 0, 20)
CYCLE
IF timecount > 100 THEN END
getMouse (a, b, mousebutton)
setSpriteAngle (spikey, angle – 30)
plotSprite (spikey, gWidth / 2, gHeight / 2, sID)
plotSprite (pop, gWidth / 2 + getSpriteW (pop) / 2, gHeight / 2 – getSpriteH (pop), 0)
sID = sID + 1
IF sID > 2 THEN sID = 0
scale = scale + scaleDir
IF scale <= 96 OR scale >= 104 THEN scaleDir = – scaleDir
setSpriteSize (pop, scale)
INK = RND (16)
printAt (tWidth / 2 – LEN (text1$) / 2, tHeight – 1); text1$;
angle = angle + anglestep
IF angle < 0 OR angle > 10 THEN anglestep = – anglestep
UPDATE
REPEAT UNTIL mousebutton
CLS
hideSprite (spikey)
hideSprite (pop)
ENDPROC
FUZE BASIC V3 for Linux and the Raspberry Pi, the Programmer’s Reference Guide and a Project Workbook are all freely available to download from www.fuze.co.uk.
from Linux User & Developer - the Linux and FOSS mag for a GNU generation http://ift.tt/1Kc1ZcW
via IFTTT
No comments:
Post a Comment