top of page
Writer's pictureBruce

Let's create an MPAGD game - Part 23: Adding basic sounds with BEEP and CRASH

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


Want to support my work?....Buy my games!

aboutME

Hello, I'm Bruce and I write games for old 8bit computers using Jonathan Cauldwell's excellent Multi-Platform Arcade Games Designer (MPAGD)

I've written a few successful* games for the Sinclair ZX Spectrum and MSX platforms that have been (largely) well received including Twenty Four Hour Parsley People scoring a 10 out of 10 on Planeta Sinclair.

In my blog I am sharing lots of the code that I wrote for my games, in a way that you can use in your own games.   I've commented it so that you'll learn some of the techniques I use to create interesting new mechanics and help your games stand out from the pack.

MPAGD includes lots of standard scripts, they're great to get you started, but if you're new (or just rusty) when it comes to writing code, hopefully my tutorials will help you get started and  turn your imagination into awesome 8 bit games!

All my code is free to use and do with as you please, but if you find them useful please feel free to buy me a coffee ...or better still - buy or download my games :)

*successful is a very relative term in 8bit computer games

bottom of page