Minilop Retroware

Tutoriais de shoot 'em up · Parte 8 de 12

Vertical Shmup - Parte 8 - Uma abordagem mais flexível para os extraterrestres

2 de agosto de 2022

Até agora temos duas vagas de extraterrestres e são bastante fáceis de derrotar, mas vejo um problema na forma como o escrevemos. No jogo espero ter muitos padrões de movimento diferentes para os extraterrestres, estou a pensar num estilo Galaga, ou pelo menos nas partes em que os extraterrestres entram a voar no ecrã.

Com o nosso padrão inicial de extraterrestres houve algum grau de reutilização de código, mas acho que podemos aproveitar mesmo os comandos DATA & READ do MPAGD para chegar a uma abordagem muito mais flexível, que nos poderia ajudar a criar rapidamente infinitos padrões de movimento de extraterrestres (ou pelo menos até ficarmos sem memória!

Primeiro vou criar um sprite novo de nave extraterrestre:

Captura de ecrã do tutorial

Vou começar um Sprite Type novo para os meus novos extraterrestres flexíveis... Vou usar o Sprite Type 5.

Pensemos nas direções possíveis em que um extraterrestre se pode mover. Acho que as 16 direções alargadas da rosa dos ventos nos darão flexibilidade suficiente:

Captura de ecrã do tutorial

Depois vamos escrever código de movimento para cada ponto cardeal, usando a variável local DIRECTION, começando em 0 para N, 1 para NNE e assim sucessivamente no sentido dos ponteiros do relógio:

EVENT SPRITETYPE5

ANIMATE SLOW

							; FLEXI ALIENS
							; =====================
							

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

Muito bem, isto vai definir cada um dos movimentos possíveis de um extraterrestre. Agora só precisamos de uma forma de definir um padrão de movimentos, que podia ser tão simples como uma sequência de valores para DIRECTION e o número de repetições.

Assim, 8 20 significaria: executa a DIRECTION 8 (sul) 20 vezes.

Se te lembras do nosso método anterior para mover a primeira vaga de extraterrestres, iam para sul e depois desviavam-se para sudoeste,

No nosso método novo isso poderia ser representado assim:

8 20 (sul 20 vezes)

7 8 (SSE 8 vezes)

6 8 (SE 8 vezes)

5 8 (ESE 8 vezes)

4 16 (E 16 vezes)

O que se presta na perfeição a ser guardado numa tabela DATA, por exemplo:

DATA 8 20 7 8 6 8 5 8 4 16

Depois poderíamos criar facilmente padrões novos que descrevam o movimento dos extraterrestres de cada nível, apenas neste evento.

Isso deve ser mesmo eficiente!

Mas há algumas coisas de que vamos precisar para isto funcionar:

1) Uma variável para guardar o número de repetições da direção de movimento atual

2) Uma forma de encontrar o ponto de partida certo do padrão específico do nível / vaga.

O ponto 1 é bastante fácil de resolver: vamos simplesmente usar SETTINGB para ler para ela o número de repetições

Vamos usar SETTINGA como variável de controlo, para garantir que fazemos as coisas no momento certo

E vamos usar JUMPSPEED como contador do número de repetições.

Acrescenta o código destacado ao teu evento SPRITE TYPE 5:

EVENT SPRITETYPE5



							; FLEXI ALIENS
							; =====================
SPRITEINK C					; Colour the aliens							
				
							
							; FIND THE STARTING POINT FOR THIS LEVEL
							;=======================================
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
							; GET THE NEXT DIRECTION & # REPETITIONS
							;=======================================
IF SETTINGA = 1				; Ready to get direction & reptitions
	READ DIRECTION    		; Read the next movement into DIRECTION
	READ SETTINGB     		; Read the repetitions into SettingB
	LET JUMPSPEED = 0			; reset the counter
	LET SETTINGA = 2			; now we are ready to move
ENDIF

							; MOVEMENTS
							;==============
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 number of aliens still in
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 number of aliens still in
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 number of aliens still in
ENDIF


		; INCREMENT REPETITION COUNTER & CHECK IF WE NEED NEXT DIRECTION
		;============================== 
SUBTRACT 1 FROM SETTINGB	; decrement the number of repetitions
IF SETTINGB = 0			; have we done all the required repetitions
	LET SETTINGA = 1		; yes, request the next direction
ENDIF



; MOVEMENT & REPETITIONS DATA TABLE

DATA 51 0 8 20 7 8 6 8 5 8 4 16
DATA 5e2 0 8 20 9 8 10 8 11 8 12 16

Primeiro, acrescentei um comando spriteink para colorir os extraterrestres com o valor C (vamos acrescentá-lo à tabela de dados do spawner daqui a pouco)

Assim, quando este evento for executado pela primeira vez para um sprite extraterrestre, SettingA será 0 (vamos ter de o acrescentar a Initialise Sprites daqui a pouco)

Isto vai restaurar a tabela de dados (o que significa que o READ seguinte será o primeiro valor da tabela). Depois vamos percorrer os dados até encontrarmos um valor igual a 50 + o número do nível (ou seja, para o nível 1 procuramos 51, para o nível 2 procuramos 52 e assim sucessivamente).

Escolhi 50 porque é pouco provável que precise de mais de 50 direções (0-15 representam os nossos 16 pontos cardeais), mas deixa-me margem para acrescentar mais depois e significa que poderia ter mais de 200 padrões de movimento neste evento, o que deverá ser mais do que suficiente.

Depois de o encontrarmos, vamos ler o número seguinte para SETTINGB (vamos usar apenas 0) e depois pôr SettingA a 1 para não restaurarmos os dados em cada ciclo.

É importante perceber que cada sprite sabe sempre até onde leu da tabela de dados, a menos que façamos um RESTORE, momento em que volta ao início.

Agora que SettingA é 1, vamos fazer um READ do número de DIRECTION e do número de REPETITIONS necessárias para SETTINGB, e depois pômos settingA a 2 para impedir que isto volte a ser executado até todas as repetições da direção de movimento terem sido realizadas.

Repara que também coloquei uma verificação de limites (não muito eficiente, vamos melhorá-la mais tarde) que removerá os extraterrestres que saiam do ecrã.

Por fim, temos a nossa tabela DATA. De momento limitei-me a replicar os nossos dois padrões de movimento originais: descer e desviar para a direita, e descer e desviar para a esquerda.

Muito bem, antes de o testarmos temos mais algumas tarefas para concluir.

MENU: EVENTS > INITIALISE SPRITES:

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 5				; FLEXI ALIENS
	LET SETTINGA = 0		; Initialise SettingA
ENDIF

IF TYPE 7				; SPAWNER
	LET SETTINGA = 0		; RESET SETTINGA	
ENDIF

A seguir, vamos atualizar o código do evento do Spawner:

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				; Number of points scored if enemy is killed
	READ C				; get the colour value for the alien
	ENDREPEAT
	LET D = E			; store the number of enemies to be killed in D
	LET F = E		; also store #enemies in F to count kills & exits
	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			; return to the spawner
		ENDIF
	ELSE					; all enemies in this level have been spawned
		IF F = 0			; if all enemies killed or exited
			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
	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 
DATA 0 100 8 5 2 8 8 10 68

Aqui acrescentei um valor novo a cada conjunto de dados e separei os dois níveis em linhas próprias para ser mais fácil de ler. O valor novo (o nono) é o que será lido para C.

Acho que estamos prontos para testar! Vamos compilar este petiz!

Sim! Olha como eles voam!

Muito bem, agora vamos divertir-nos e criar alguns padrões novos na tabela DATA do sprite type 5. Garante que começas cada padrão com 200+Nível# seguido de um zero e depois os teus pares de DIRECTION # e Repetition #

Experimenta estes para começar, e depois inventa os teus:

Substitui a tabela DATA do teu evento de extraterrestre (Sprite Type 5) por:

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

E substitui a tabela de dados do teu Spawner (Sprite Type 7) por:

DATA 0 132 8 5 2 8 8 5 67 
DATA 0 100 8 5 2 8 8 10 68
DATA 0 232 16 5 5 8 6 15 69 
DATA 0 8 16 5 5 8 6 15 70 
DATA 0 232 146 5 2 16 6 20 71 
DATA 0 8 146 5 2 16 6 20 66 

Agora que temos 6 vagas de ataque / níveis, vais ter de atualizar esta linha no evento do teu spawner

			IF L = 3			; have we reached the end of the levels?
			

para:

			IF L = 7			; have we reached the end of the levels?

Guarda o teu jogo, e vamos compilá-lo e disparar a alguns extraterrestres!

Fixe! Sente-te à vontade para experimentar e criar muitos mais padrões de ataque!

No entanto, já está na altura de dar aos extraterrestres armas próprias. Se nos apoiarmos na estrutura que já criámos e usarmos alguns dos truques que aprendemos, deveremos conseguir acrescentar muito mais variedade ao jogo.

SEGUINTE: Parte 9: Os extraterrestres contra-atacam!

Todos os tutoriais de shoot 'em up