Isto está mesmo a começar a compor-se: temos uma estrutura facilmente extensível para criar padrões de movimento de extraterrestres, mas são um pouco inofensivos neste momento, já que não podem disparar armas próprias. Vamos remediar isso!
Primeiro vamos desenhar um sprite de míssil extraterrestre:

Vou usar o Sprite Type 3 para os mísseis extraterrestres, mas antes de começarmos a programar há algo que temos de ter em conta: a deteção de colisões do MPAGD. Como todos os sprites têm 16 px x 16 px, mas o nosso míssil extraterrestre só ocupa 3 pixels de largura, isto pode levar a colisões bastante injustas que visualmente não parecem um míssil a colidir com a nave espacial do jogador, mas que o motor regista como colisão. Por isso, teremos de incluir código que ajude a verificar se a parte gráfica do sprite colidiu realmente com a nave espacial.
Também temos de pensar em como viajam os mísseis: descem simplesmente em linha reta?, devem perseguir o jogador?, ou devem desviar-se para a esquerda ou para a direita consoante a direção em que o extraterrestre viajava quando lançou o míssil?
Esta última opção é a minha preferida e, como todas as direções dos extraterrestres são guardadas na variável DIRECTION, deverá ser bastante fácil programar a direção do míssil: se um extraterrestre viajava para a direita quando largou o míssil, o míssil deve desviar-se para a direita, e assim por diante.
Outras coisas que temos de ter em conta incluem:
Quando deve ser disparado um míssil?
Qual é a probabilidade de ser disparado um míssil?
Devem todos os extraterrestres disparar com a mesma frequência?
Devem todos os extraterrestres disparar mísseis ou apenas alguns?
A que altura do ecrã deve estar um extraterrestre para poder disparar mísseis? (convém evitar que disparem se estiverem a apenas alguns pixels acima do jogador, porque seria injusto.)
Até agora construímos uma estrutura bastante flexível, por isso estou a pensar em alargar ainda mais a flexibilidade para nos permitir variar a capacidade de disparo de mísseis de cada vaga de ataque de extraterrestres, o que nos ajudará a tornar as vagas de ataque progressivamente mais difíceis.
Muito bem, vamos ao código. Primeiro, vamos alargar o código do nosso Spawner para incluir algumas variáveis novas que controlem as capacidades de disparo dos extraterrestres. Abre o teu evento Spawner (Sprite Type 7) e acrescenta o código destacado:
EVENT SPRITETYPE7 ; SPAWNER ; ============ IF SETTINGA = 0 ; first time we run this LET JUMPSPEED = 0 ; reset the enemies spawned counter LET SETTINGB = 0 ; reset the timer RESTORE ; start from the beginning of the data table REPEAT L ; get the data for this level READ AIRBORNE ; get the spawner movement READ X ; get starting x position READ Y ; get starting y position READ J ; get the enemy sprite type READ K ; get the enemy image READ E ; how many enemies in this level? READ G ; delay between releasing each enemy READ P ; points for a kill READ C ; colour READ W ; weapon READ H ; fire weapon probability READ Q ; min height to release weapons ENDREPEAT LET D = E ; how many kills to get bonus? LET F = E ; enemies remaining ADD 1 TO SETTINGA ; prevent this running again ENDIF IF SETTINGB = G ; has timer reached the delay point? IF JUMPSPEED < E ; are there still enemies left to spawn? IF S < 9 ; are there 8 or less enemies already on screen? SPAWN J K ; spawn an enemy ADD 1 TO JUMPSPEED ; increment the number spawned SPAWNED ; switch to the enemy sprite LET DIRECTION = 0 ; set its initial direction to 0 ENDSPRITE ; retrun to the spawner ENDIF ELSE ; all enemies in this level have been spawned IF F = 0 ; all enemies are off screen ADD 1 TO L ; get ready for next level IF L = 7 ; have we reached the end of the levels? LET L = 1 ; yes, start again ENDIF LET SETTINGA = 0 ; get data for next level ENDIF ENDIF LET SETTINGB = 0 ; reset the timer ENDIF ADD 1 TO SETTINGB ; increment the timer DATA 0 132 8 5 2 8 8 5 67 1 10 100 DATA 0 100 8 5 2 8 8 10 68 1 10 100 DATA 0 232 16 5 5 8 6 15 69 1 8 100 DATA 0 8 16 5 5 8 6 15 70 1 8 100 DATA 0 232 146 5 2 16 6 20 71 1 4 100 DATA 0 8 146 5 2 16 6 20 66 1 4 100
Tudo o que acrescentámos aqui foram três variáveis novas que vamos ler da tabela de dados:
W (Weapon) - 0 significará que não podem disparar mísseis, 1 que podem
H (probabilidade de disparar uma arma): quanto maior for o valor, menor é a probabilidade de realmente dispararem um míssil
Q (altura de disparo): é o valor de Y abaixo do qual (ou seja, mais acima no ecrã, já que as coordenadas Y do Spectrum começam na parte superior do ecrã) um extraterrestre deve estar para lhe ser permitido disparar mísseis.
Acrescentei alguns valores para cada uma das 6 vagas de ataque definidas atualmente na tabela de dados no final.
A seguir, vamos precisar de acrescentar algumas linhas de código ao nosso evento do jogador para guardar os valores das coordenadas X & Y do jogador, já que vamos precisar delas no evento do extraterrestre para calcular a que distância horizontal está o extraterrestre do jogador. Abre o teu evento do jogador e acrescenta estas linhas no final do teu script:
LET M = X ; store player X in M LET N = Y ; store player Y in N
A seguir, vamos acrescentar algum código ao evento do extraterrestre (Sprite Type 5). Abre o teu e acrescenta/atualiza o código destacado:
EVENT SPRITETYPE5 ; FLEXI ALIENS ; ===================== SPRITEINK C ; colour the alien with C value IF SETTINGA = 0 ; Alien first appears RESTORE ; Restore the data table LET RND = L ; Store the Level number in RND ADD 50 TO RND ; and increase it by 50 WHILE DIRECTION <> RND ; While DIRECTION doesnt = RND READ DIRECTION ; Read value from DATA table READ SETTINGB ; Also read second value ENDWHILE ; Keep going until we find the value in RND LET SETTINGA = 1 ; When we find it change SA to 1 ENDIF IF SETTINGA = 1 ; Ready to get direction & reptitions READ DIRECTION ; Read the next movement into DIRECTION READ SETTINGB ; Read the repetitions into SettingB LET SETTINGA = 2 ; ENDIF IF COLLISION 1 ; if hit by a torpedo OTHER ; switch to torpedo LET DIRECTION = 99 ; run the explosion ENDSPRITE ; return to this Alien LET TYPE = 6 ; Switch it to explosion type LET IMAGE = 4 ; Explosion image LET FRAME = 0 ; first frame LET DIRECTION = 0 ; tell explosion its an alien exploding ENDIF IF COLLISION 0 ; if enemy and player collide LET TYPE = 6 ; switch it to the explosion type LET IMAGE = 4 ; and the explosion image LET FRAME = 0 ; reset it to first frame LET DIRECTION = 0 ; this is an alen exploding OTHER LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ENDIF IF DIRECTION = 0 ; Move N SPRITEUP SPRITEUP ENDIF IF DIRECTION = 1 ; Move NNE SPRITERIGHT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 2 ; Move NE SPRITERIGHT SPRITERIGHT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 3 ; Move ENE SPRITERIGHT SPRITERIGHT SPRITEUP ENDIF IF DIRECTION = 4 ; Move E SPRITERIGHT SPRITERIGHT ENDIF IF DIRECTION = 5 ; Move ESE SPRITERIGHT SPRITERIGHT SPRITEDOWN ENDIF IF DIRECTION = 6 ; MOVE SE SPRITERIGHT SPRITERIGHT SPRITEDOWN SPRITEDOWN ENDIF IF DIRECTION = 7 ; MOVE SSE SPRITEDOWN SPRITEDOWN SPRITERIGHT ENDIF IF DIRECTION = 8 ; MOVE S SPRITEDOWN SPRITEDOWN ENDIF IF DIRECTION = 9 ; MOVE SSW SPRITEDOWN SPRITEDOWN SPRITELEFT ENDIF IF DIRECTION = 10 ; MOVE SW SPRITEDOWN SPRITEDOWN SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 11 ; MOVE WSW SPRITEDOWN SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 12 ; MOVE W SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 13 ; MOVE WNW SPRITELEFT SPRITELEFT SPRITEUP ENDIF IF DIRECTION = 14 ; MOVE NW SPRITELEFT SPRITELEFT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 15 ; MOVE NNW SPRITELEFT SPRITEUP SPRITEUP ENDIF ; BOUNDARY CHECKING ; =================== IF X <= LEFTEDGE ; if alien reaches the left edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF IF X >= RIGHTEDGE ; if alien reaches the right edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF IF Y >= BOTTOMEDGE ; if alien reaches the right edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF ; SHALL WE SHOOT A MISSILE? ; =========================== IF Y < Q ; is alien above minimum height for releasing missiles? IF W > 0 ; yes, can alien release missiles? IF X < M ; yes, is Alien to the right of player? SUBTRACT X FROM M ; how far to the right of player is alien? IF M <= 8 ; 8 or less pixels? LET AIRBORNE = 1 ; run the chance of firing routine ENDIF ADD X TO M ; reset M ELSE IF M < X ; is alien to the left of player? SUBTRACT M FROM X ; yes, how far to the left IF X < = 8 ; 8 pixels or less? LET AIRBORNE = 1; yes, run the chance of firing routine ENDIF ADD M TO X ; reset X ELSE ; enemy must be directly above player LET AIRBORNE = 1 ; run the chance of firing routine ENDIF ENDIF ENDIF ENDIF IF AIRBORNE = 1 ; Chance of firing routine IF S < 9 ; are there 8 or less sprites on screen? GETRANDOM H ; yes, get a random number from 0 to the aliens missile probability # IF RND = 1 ; is it 1? LET RND = DIRECTION ; yes, store aliens direction in RND SPAWN 3 6 ; spawn a missile SPAWNED ; switch to the spawned missile LET DIRECTION = RND ; set its direction to RND ENDSPRITE ; switch back to this alien ENDIF ENDIF LET AIRBORNE = 0 ; end the chance of firing routine ENDIF SUBTRACT 1 FROM SETTINGB ; decrement the repetitions IF SETTINGB = 0 ; has alien moved all repetitions? LET SETTINGA = 1 ; get the next direction & # of repetitions ENDIF DATA 51 0 8 20 7 8 6 8 5 8 4 16 DATA 52 0 8 20 9 8 10 8 11 8 12 16 DATA 53 0 12 48 11 2 10 2 9 2 8 2 7 2 6 2 5 2 4 40 5 2 6 2 7 2 8 2 9 2 10 40 DATA 54 0 4 48 5 2 6 2 7 2 8 2 9 2 10 2 11 2 12 40 11 2 10 2 9 2 8 2 7 2 6 40 DATA 55 0 13 26 14 4 15 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 4 10 40 DATA 56 0 3 26 1 4 0 4 15 4 14 4 13 4 12 4 11 4 10 4 9 4 8 4 7 4 6 40
Lê os comentários do código destacado e, com sorte, vais perceber o que estamos a fazer:
Verifica se o extraterrestre está a menos de 8 pixels à esquerda e x pixels à direita do jogador
se estiver, há uma probabilidade aleatória de ser disparado um míssil (desde que também esteja acima da distância vertical mínima definida na variável Q) E se ainda não houver demasiados sprites no ecrã
A probabilidade de ser disparado um míssil é de 1 em H (onde H é definido na tabela de dados da vaga de ataque, no evento do spawner.
E se todos estes testes forem superados, é gerado um míssil (type 3, Image 6)
Depois, define o valor de DIRECTION do míssil gerado como sendo igual ao DIRECTION em que o alienígena se deslocava quando disparou o míssil.
Agora só falta escrever o código do míssil, por isso abre o Sprite Type 3 e cola isto:
EVENT SPRITETYPE3 ; ENEMY MISSILE SPRITEINK 71 ; bright white SPRITEDOWN ; go down fast (3 times SPRITEDOWN SPRITEDOWN IF DIRECTION = 0 ; was alien going down when fired? ELSE ; if so, do nothing IF DIRECTION < 8 ; was alien going right? SPRITERIGHT ; make missile veer right ELSE IF DIRECTION = 8 ; was alien going straight up? ELSE ; do nothing SPRITELEFT ; alien was going left, veer missile left ENDIF ENDIF ENDIF IF COLLISION PLAYER ; has missile collided with player? IF X > M ; is missile to the right of player? SUBTRACT M FROM X ; find out how far to the right IF RND <= 10 ; 10 px or less? its a hit! OTHER ; switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ; switch back to this event ENDIF ADD M TO X ; reset X ELSE ; not to the right IF X < M ; is missile to the left? SUBTRACT X FROM M ; yes, how far to left? IF M <= 8 ; 8 or less pixels? OTHER ;switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ; switch back to this event ENDIF ADD X TO M ; reset M ELSE ; must have been a direct hit OTHER ; switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ;switch back to this event ENDIF ENDIF ENDIF IF Y >= 188 ; has missile reached bottom of the screen REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter ENDIF
Assim, verificamos as colisões com o jogador e depois calculamos o quão direto é o impacto, criando na prática uma caixa de impacto rápida. Podes ajustar estes valores ao teu próprio jogo e aos teus gráficos.
Mais uma coisa para atualizar: repara que, quando registamos um impacto no jogador, enviamos uma instrução ao sprite do jogador para pôr a sua direção em 99, por isso vamos ter de adicionar uma cláusula ao nosso evento do jogador. Abre o teu evento do jogador e adiciona:
IF DIRECTION = 99 LET TYPE = 6 ; switch it to the explosion type LET IMAGE = 4 ; and the explosion image LET FRAME = 0 ; reset it to first frame LET DIRECTION = 1 ; this is the player exploding ENDIF
Fiz isto para poupar memória, já que pode haver vários sítios no código onde fazemos explodir o jogador (repara que antes substituí as definições de type, image, frame e direction na colisão Alien/Player no evento Alien, o sprite type 5). Cada byte conta!
Pronto, isso deve ser tudo o que precisamos para dar poder de fogo aos alienígenas... hora de testar!
Por enquanto parece-me bem, podemos sempre refiná-lo mais tarde, à medida que o jogo avança.
A seguir vamos fazer um pouco de arrumação e organizar algumas coisas.
SEGUINTE: PARTE 10: UM POUCO DE ARRUMAÇÃO
