Para começar, vou criar um sprite de nave extraterrestre e depois escrever algum código para o seu movimento.

Na primeira vaga de ataque quero que uma sequência destes tipos apareça na parte superior do ecrã, desça em linha reta e depois se desvie para um dos lados do ecrã.
Quero que a sequência de extraterrestres apareça em dois pontos da parte superior do ecrã, um à direita do centro e outro à esquerda.
A sequência da direita deve desviar-se para o limite direito, e a da esquerda para o limite esquerdo.
Mas, para começar, vamos resolver o movimento básico. Vamos usar o Sprite Type 2 para este extraterrestre:
MENU: EVENTS > SPRITE TYPE 2
IF IMAGE 2 ; ALIEN 1 image SPRITEINK 4 ; green IF DIRECTION = 0 ; initial direction (setup) IF X <= 128 ; is alien on left side of screen? LET DIRECTION = 1 ; yes, set its direction to 1 ELSE LET DIRECTION = 2 ; no, set its direction to 2 ENDIF ENDIF IF Y < 80 ; is alien above the 80px y line? SPRITEDOWN ; move down SPRITEDOWN ; and again (faster) ELSE ; alien is below the 80 px y line IF Y < 120 ; is it betwwwn 80 and 119 y? IF DIRECTION = 1 ; yes, is it on the left side? SPRITEDOWN ; yes, go down SPRITELEFT ; and go left ELSE ; no, its on the right side SPRITEDOWN ; so go down SPRITERIGHT ; and go right ENDIF ELSE ; alien is below the 120 y line IF DIRECTION = 1 ; is it on the left side? SPRITELEFT ; yes, go left SPRITELEFT ; and left again SPRITEDOWN ; and go down ELSE ; if its on the right side SPRITERIGHT ; go right SPRITERIGHT ; go right again SPRITEDOWN ; and go down ENDIF ENDIF ENDIF ; BOUNDARY CHECKING ; =================== IF X <= LEFTEDGE ; if alien reaches the left edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter ENDIF IF X >= RIGHTEDGE ; if alien reaches the right edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter ENDIF ENDIF
Agora vamos acrescentar dois destes tipos ao nosso ecrã: vamos pôr um à esquerda do centro, no topo, e outro à direita do centro, e atribuir a cada um o Sprite Type 2. O código acima deverá descobrir onde está cada um e definir o seu movimento em conformidade.
MENU: EDITOR > SPRITE POSITIONS

Vamos compilar o jogo e ver o que acontece
{INSERT VIDEO}
Está bem, não está mau, podemos refiná-los e melhorá-los mais tarde.
Mas primeiro temos um problema maior para resolver. Como fazemos para que apareça uma sequência destes tipos?
Podia simplesmente criar um ecrã para cada nível, desenhar alguns sprites, escrever código de movimento e usar as posições dos sprites para definir os seus pontos de partida, mas não me parece que isso fosse a utilização mais eficiente da memória disponível.
Vou adotar uma abordagem diferente: vou definir um sprite Spawner, um sprite invisível cuja função é colocar inimigos no ecrã no momento certo.
O Spawner precisa de ter propriedades próprias, como:
Se está numa posição fixa ou se se move
Que extraterrestres gera (a sua imagem e o seu tipo de sprite)
Quanto tempo deve esperar entre cada aparição?
Quantos extraterrestres deve gerar em cada vaga de ataque / nível?
Este é um cenário ideal para guardar cada uma destas propriedades numa tabela DATA e depois lê-las com READ conforme necessário.
Mas primeiro, vamos criar o sprite Spawner, criando um sprite novo com uma imagem em branco.

Antes de começarmos a programar o nosso Spawner vamos criar uma variável nova, 'L', que vai conter o nível do jogo: começará em 1, o primeiro nível, e avançará à medida que o jogador derrotar cada vaga de ataque.
MENU: EVENTS > GAME INITIALISATION
EVENT GAMEINIT LET L = 1 ; Level (starts at 1)
A seguir vamos definir o Spawner no Sprite Type 7 e escrever algum código:
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 ENDREPEAT 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 <= 8 ; are there 10 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 ADD 1 TO L ; get ready for next level IF L = 2 ; have we reached the end of the levels? LET L = 1 ; yes, start again ENDIF LET SETTINGA = 0 ; GET DATA FOR NEXT LEVEL ENDIF LET SETTINGB = 0 ; reset the timer ENDIF ADD 1 TO SETTINGB ; increment the timer DATA 0 165 8 2 2 5 12
Muito bem, é aqui que a coisa se complica um pouco, mas tem paciência comigo, vou tentar explicar da forma mais simples possível. Vamos usar algumas das funcionalidades menos conhecidas do MPAGD que não estão disponíveis no AGD normal nem sequer no AGDx, mas quando as perceberes garanto-te que vais começar a criar melhores jogos.
Estou a falar de READ e DATA. Usei-os extensivamente em todos os jogos que lancei: ajudaram-me a poupar memória, a escrever melhor código, a criar mecânicas novas e interessantes e muito mais. Na minha opinião, são as melhores funcionalidades do MPAGD.
O que o código acima faz, essencialmente, é:
Quando o spawner aparece pela primeira vez, lê o primeiro número da tabela DATA e guarda-o na variável de sprite AIRBORNE (já explico!)
Lê o segundo número da tabela de dados: será a posição X do Spawner
Lê o terceiro número da tabela de dados: será a posição Y do Spawner
Lê o quarto número da tabela de dados: dir-nos-á o número de Sprite Type do extraterrestre que será gerado
Lê o quinto número da tabela de dados: dir-nos-á o número de imagem de sprite do extraterrestre que será gerado
Lê o sexto número da tabela de dados: dir-nos-á quantos inimigos é preciso gerar nesta vaga de ataque / nível
Lê o sétimo número da tabela de dados: dir-nos-á quanto tempo o spawner deve esperar antes de gerar o extraterrestre seguinte
A tabela DATA, de momento, só contém os dados da primeira vaga de ataque:
0 (AIRBORNE) Define o padrão de movimento do próprio spawner, embora não o vamos usar nesta primeira vez
165 (X) a coordenada x do spawner
8 (Y) a coordenada y do spawner
2 (J) o número de tipo de sprite do extraterrestre
2 (K) o número de imagem de sprite do extraterrestre
5 (E) o número de extraterrestres desta vaga de ataque
12 (G) o número de ciclos de cpu a esperar entre a geração de um extraterrestre e do seguinte
Antes de testarmos os nossos progressos, precisamos de inicializar o valor de SETTINGA a 0 quando o Spawner é gerado pela primeira vez
MENU: EVENTS > INITIALISE SPRITES
e acrescentar o código destacado:
EVENT INITSPRITE ADD 1 TO S ; increment the sprite counter IF TYPE 1 ; PHOTON TORPEDO LET SETTINGB = 0 ; initialise the distance counter ENDIF IF TYPE 7 ; SPAWNER LET SETTINGA = 0 ; RESET SETTINGA ENDIF
Vamos executá-lo e verificar se está tudo como esperado.
Sim, parece estar bem
Agora vamos acrescentar os dados da vaga seguinte. Desta vez vamos pôr o Spawner à esquerda do centro. No evento do teu spawner acrescenta/atualiza o código destacado, que vai acrescentar os dados da vaga / nível seguinte
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 ENDREPEAT 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 <= 8 ; are there 10 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 ADD 1 TO L ; get ready for next level IF L = 3 ; have we reached the end of the levels? LET L = 1 ; yes, start again ENDIF LET SETTINGA = 0 ; GET DATA FOR NEXT LEVEL ENDIF LET SETTINGB = 0 ; reset the timer ENDIF ADD 1 TO SETTINGB ; increment the timer DATA 0 165 8 2 2 5 12 0 80 8 2 2 5 12
Agora vamos compilá-lo outra vez:
{insert video}
Muito bem, tem bom aspeto: vemos a primeira vaga à esquerda e depois a segunda à direita, e o padrão repete-se em ciclo.
Mais tarde vamos ampliar as capacidades do Spawner, mas como podes ver no vídeo acima, o jogo tem um problema fundamental: não acontece nada quando os torpedos do jogador atingem os extraterrestres.
Por isso, vamos tratar disso a seguir...
PARTE 6: ELIMINAR EXTRATERRESTRES
