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
ENDIFVamos 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
ENDIFO 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
ENDIFHora 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:
AIRBORNE para verificar se o jogador está no ar ou não
e criámos um temporizador de uso geral no ciclo principal. Vamos usá-lo para outras coisas mais tarde.
Por fim, aprendemos também como usar variáveis temporárias quando precisamos de uma variável rápida descartável, e porque é que RND é uma boa opção neste caso.
