Minilop Retroware

MPAGD Iniciantes · Parte 23 de 29

Vamos criar um jogo com o MPAGD - Parte 23: Acrescentar sons básicos com BEEP e CRASH

30 de outubro de 2021

Como estamos a trabalhar num clássico jogo de ZX Spectrum, seria um descuido não incluir alguns sons nostálgicos do altifalante do Speccy. Embora pudéssemos acrescentar sons AY para máquinas de 128K, isso não é realmente algo para iniciantes. Por isso vamos ficar com o altifalante e usar dois comandos do MPAGD que conseguem fazer barulho.

BEEP

O comando BEEP espera um número que controla durante quanto tempo o som é produzido. Repara que não podemos controlar diretamente o tom: os BEEP do MPAGD começam graves e vão subindo aos poucos ao longo da duração do BEEP... basicamente soa a um chape. Aceita valores até (acho que 125, depois do qual começa a dar a volta (por isso BEEP 130 soa igual a BEEP 5)

Aqui tens um pedaço de código que podes experimentar. Coloca-o no fim do teu evento Game Initialisation:

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

guarda o teu evento e compila o jogo:

Como podes ouvir, BEEP 5 é pouco mais do que uma pancadinha, enquanto BEEP 125 é um som longo e de chape.

Claramente não temos muito com que brincar aqui!

CRASH

O outro som que podemos usar é CRASH, que produz um som de ruído branco. Vamos substituir os BEEP do nosso código acima por CRASH e ver como soa:

Ok, isto vai dar trabalho!

Aquele BEEP 5 curto, a pancadinha, pode ficar bem quando o Stinky Dog se move. Vejamos o que acontece se o acrescentarmos ao código de movimento dele. Abre o teu evento PLAYER e encontra as duas instruções que controlam o movimento para a esquerda e para a direita e vamos colocar BEEP 5 nelas:

   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

Vamos experimentar...

Ok, funciona, mas acho que há duas coisas a melhorar. Primeiro, acho que dá pancadinhas a mais, devíamos reduzir o seu número. Segundo, as pancadinhas continuam quando o Stinky Dog salta para a esquerda ou para a direita.

Ambos os problemas são causados por o comando BEEP estar dentro das instruções IF KEY LEFT/RIGHT. Mas ambos se podem resolver.

Primeiro, vejamos como podemos reduzir o número de pancadinhas. Vamos consegui-lo introduzindo um temporizador de uso geral. Vamos usar uma variável, A, e escrever um pouco de código que garanta que metade do tempo A contém 0 e a outra metade contém 1. Depois vamos fazer com que o BEEP dependa de A conter 0.

Como queremos sempre este temporizador a funcionar, o melhor sítio para o pôr é o evento MAIN LOOP 1. O MPAGD executa este evento em cada ciclo antes de todos os eventos dos nossos sprites (depois executa MAIN LOOP 2.

Abre MAIN LOOP 1, pode já conter algum código (não te preocupes com ele) e acrescenta o código realçado abaixo:

ADD 1 TO A         ; increment A
IF A = 2           ; does A contain 2?
   LET A = 0       ; if so make A contain 0
ENDIF

Ok, agora no nosso código do jogador só temos de envolver os BEEP 5 numa condição de A ser 0, ou seja:

IF A = 0
   BEEP 5
ENDIF

Assim, agora o som de pancadinha só ocorrerá metade das vezes (um ciclo sim, outro não)

O segundo problema, o som de pancadinha ocorrer quando o Stinky Dog está no ar, também se resolve facilmente. Para cada sprite, o MPAGD também guarda uma variável especial que nos diz se o sprite está 'no ar' ou em chão firme, chama-se AIRBORNE. Como qualquer variável, contém um valor de 0 a 255. Se for 0, significa que o sprite NÃO está no ar. Mais uma vez, precisamos de envolver os nossos BEEP 5 numa condição que verifique que o Stinky Dog está no chão. E podemos fazê-lo dentro da condição IF A = 0:

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

O teu código de movimento no evento Player deve agora ficar assim:

   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

Hora de testar...

Já está bom!

A seguir, quero experimentar algo um pouco diferente: quero um som especial para quando o Stinky Dog for morto

O que podíamos fazer era repetir alguns sons em sucessão, talvez usando uma variável para controlar o valor do BEEP... Algo assim:

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           ; 

Porque é que estou a usar RND como variável? Em poucas palavras, as variáveis são coisas preciosas no MPAGD, só temos algumas para usar, e se só precisarmos de usar uma variável brevemente, costumo usar RND.

Normalmente o valor guardado em RND será o último número aleatório gerado pelo nosso código se tivermos usado o comando RANDOMISE.

Dito isto, no código acima podias usar a variável que quisesses, mas na minha experiência, usa as tuas variáveis principais do MPAGD (ABC, etc.) para 'variáveis globais': coisas que precisas de guardar e com as quais precisas de trabalhar ao longo de todo o jogo.

Também coloquei um DELAY muito pequeno. DELAY vai pausar o teu jogo durante um período de tempo que depende do número que colocares a seguir. 2 é tão baixo que provavelmente não se vai notar.

Penso que o som que este código gera pode ser bom para quando o Stinky Dog for morto, por isso vamos colocá-lo no evento KILL PLAYER...

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

Vamos testar...

Voltaremos para fazer mais sons noutra altura, mas espero que este tutorial te tenha dado uma boa ideia do que se pode conseguir com os sons básicos do altifalante do Speccy no MPAGD. Também aprendemos a usar:

Todo o MPAGD Iniciantes