As we're working on a classic ZX Spectrum game, it would be remiss of us to not include a few nostalgic speccy beeper sounds. Although we could add AY sounds for 128K machines, that's not really something for the beginner. So we'll stick to the beeper and use two MPAGD commands that can make noises!.
BEEP
The BEEP command expects a number which will control how long the sound is made for, note that we cant directly control the pitch, MPAGD Beeps start low and steadily get higher over the duration of the Beep...basically it sounds like a squelch! It will accept a value up to (I think 125 after which it will start cycling through (so BEEP 130 sounds the same as BEEP 5)
Here's a bit of code you can try out, drop this into the bottom of your Game Initialisation event:
LET A = 5 ; store the value 5 in A
WHILE A < 255 ; do the following as long as A is less than 255
WAITKEY ; Wait for a key to be pressed
AT 10 10 ; start at line 10 column 10
PRINT "BEEP " ; display "BEEP"
AT 10 15
DISPLAY TRIPLEDIGITS A ; ...and the current value of A
BEEP A ; make the BEEP sound for current value of A
ADD 5 TO A ; increase A by 5 and repeat the process
ENDWHILE
save your event and build the game:
As you can hear BEEP 5 is little more than a tapping sound whereas BEEP 125 is a long squelchy sound.
Clearly not much for us to play with here!.
CRASH
The other sound we can use is CRASH, this makes a white noise sound. Lets replace our BEEPs in our code above with CRASH and see what it sounds like:
OK, this is going to take some work!
That short BEEP 5, the tapping sound, might work nicely when Stinky Dog is moving. Let's see what happens if we add it into to his movement code. Open up your PLAYER event and find the two statements that control left and right movement and we'll drop BEEP 5 into them:
IF KEY LEFT ; Is the left key pressed?
LET DIRECTION = LEFT ; if so, remember I am going LEFT
LET IMAGE = 1 ; display the left facing image
IF X <= LEFTEDGE ; are we at the left edge of the screen?
SCREENLEFT ; if so, go to the screen to left on the map
LET X = RIGHTEDGE ; and put me at the right edge of that screen
EXIT ; and stop processing this event
ELSE ; if I'm not at the left edge
IF CANGOLEFT ; is there space to the left of me?
SPRITELEFT ; if so move me left
BEEP 5 ; make a tap sound
ANIMATE ; and animate me
ELSE ; if I can;t go left
DIG LEFT ; dig away any fodderblocks there
ENDIF
ENDIF
ENDIF
IF KEY RIGHT ; Is the right key pressed?
LET DIRECTION = RIGHT ; if so, remember I am going RIGHT
LET IMAGE = 0 ; display the right facing image
IF X >= RIGHTEDGE ; Am I at the right edge of the screen?
SCREENRIGHT ; if so go to the screen to right on the map
LET X = LEFTEDGE ; and put me at the left edge
EXIT ; and stop processing this event
ELSE ; if I'm not at the right edge
IF CANGORIGHT ; is there space to the right of me?
SPRITERIGHT ; if so, move me right
BEEP 5 ; make a tap sound
ANIMATE ; and animate me
ELSE ; if I cant go right
DIG RIGHT ; dig away any fodderblocks there
ENDIF
ENDIF
ENDIF
Let's give it a go...
OK, well it works, but there's a couple of things I think need improving. First, I think it is tapping to much, we could do with reducing the number of taps. Second, the tapping continues when Stinky Dog jumps to the left or right.
Both of these issues are caused by the BEEP command being inside the IF KEY LEFT/RIGHT statements. But both can be solved.
First, let's look at how we can reduce the number of taps. The way we will achieve this is by introducing a general purpose timer. We'll use a variable, A, and write a little bit of code that will ensure half the time A contains 0 and the other half it contains 1. Then we will make the BEEP conditional on A containing 0.
As we always want this timer running, the best place to put it is in the MAIN LOOP 1 event. MPAGD runs this event every cycle before all of our sprite events (it then runs MAIN LOOP 2.
Open up MAIN LOOP 1, it may already contain some code (don't worry about it) and add in the highlighted code below:
ADD 1 TO A ; increment A
IF A = 2 ; does A contain 2?
LET A = 0 ; if so make A contain 0
ENDIF
OK, so now in our Player code we just need to wrap the BEEP 5's inside a condition of A being 0, i.e.
IF A = 0
BEEP 5
ENDIF
So now, the tap sound will only occur half of the time (every other cycle)
The second issue, the tap sound occuring when Stinky Dog is in the air, is also easily solved. For every sprite, MPAGD also stores a special variable that tells us if the sprite is 'in the air' or standing on solid ground, it's called AIRBORNE. Like any variable, it contains a value from 0-255. If it is 0, it means the sprite is NOT in the air. Again, we need to wrap our BEEP 5's inside a condition that checks Stinky Dog is on the ground. And we can do this inside the IF A = 0 condition:
IF A = 0 ; if A contains 0
IF AIRBORNE = 0 ; and Stinky Dog is not in the air
BEEP 5 ; make the tap sound
ENDIF
ENDIF
Your movement code in the Player Event should now look like this:
IF KEY LEFT ; Is the left key pressed?
LET DIRECTION = LEFT ; if so, remember I am going LEFT
LET IMAGE = 1 ; display the left facing image
IF X <= LEFTEDGE ; are we at the left edge of the screen?
SCREENLEFT ; if so, go to the screen to left on the map
LET X = RIGHTEDGE ; and put me at the right edge of that screen
EXIT ; and stop processing this event
ELSE ; if I'm not at the left edge
IF CANGOLEFT ; is there space to the left of me?
SPRITELEFT ; if so move me left
IF A = 0 ; if A contains 0
IF AIRBORNE = 0 ; and Stinky Dog is not in the air
BEEP 5 ; make a tap sound
ENDIF
ENDIF
ELSE ; if I can't move left
DIG LEFT ; dig any fodder blocks to the left
ENDIF
ENDIF
ENDIF
IF KEY RIGHT ; Is the right key pressed?
LET DIRECTION = RIGHT ; if so, remember I am going RIGHT
LET IMAGE = 0 ; display the right facing image
IF X >= RIGHTEDGE ; Am I at the right edge of the screen?
SCREENRIGHT ; if so go to the screen to right on the map
LET X = LEFTEDGE ; and put me at the left edge
EXIT ; and stop processing this event
ELSE ; if I'm not at the right edge
IF CANGORIGHT ; is there space to the right of me?
SPRITERIGHT ; if so, move me right
IF A = 0 ; if A contains 0
IF AIRBORNE = 0 ; and stinky dog is not in the air
BEEP 5 ; make a tap sound
ENDIF
ENDIF
ANIMATE ; and animate me
ELSE ; if I can't move right
DIG RIGHT ; remove any fodderblcoks on the right
ENDIF
ENDIF
ENDIF
Time for a test...
That'll do!
Next, I want to try something a bit different, I want a special sound for when Stinky Dog is killed
What we could do is repeat some sounds in succession, maybe use a variable to control the BEEP value... Something like this:
LET RND = 5 ; use RND as a temporary variable staring value of 5
REPEAT 20 ; do the following 20 times
BEEP RND ; make sound based on current RND
DELAY 2 ; a very brief pause
ADD 5 TO RND ; increase the BEEP value by 5
ENDREPEAT ;
Why am I using RND as the variable? Simply put, variables are precious things in MPAGD, we only have so many to use, and if we only, briefly, need to use a variable, I tend to use RND.
Normally the value stored in RND will be whatever the last random number generated was by our code if we had uses the RANDOMISE command.
Having said that, in the code above you could use whatever variable you like, but in my experience, use your main MPAGD variables (ABC etc.) for 'global variables' - things that you need to store and work with throughout the game.
I've also put a very small DELAY in. DELAY will pause your game for an amount of time, dependent on the number you put after it, 2 is so low that it probably wont be noticeable.
I'm thinking the sound this code generates might be a good one to use when Stinky Dog is killed, so let's drop it into the KILL PLAYER event...
EVENT KILLPLAYER
LET RND = 5 ; use RND as a temporary variable staring value of 5
REPEAT 20 ; do the following 20 times
BEEP RND ; make sound based on current RND
DELAY 2 ; a very brief pause
ADD 5 TO RND ; increase the BEEP value by 5
ENDREPEAT
SUBTRACT 1 FROM LIVES ; reduce the number of lives by one
COLOUR 66 ; bright red on black
AT 0 27 ; line 0 column 27
DISPLAY DOUBLEDIGITS LIVES ; display lives in xx format
ZEROBONUS ; remove any accrued fart bonus
Let's give it a test...
We'll come back and make more sounds another time, but hopefully this tutorial has given you a good idea of what you can achieve with basic Speccy Beeper sounds in MPAGD. We've also learned to use:
AIRBORNE to test if the player is in the air or not
and we've made a general purpose timer in the main loop., we'll be using that for other things later.
Finally, we've also learnt about how we can use temporary variables when we need a quick throwaway variable, and why RND is a good option in this case.
Comments