Minilop Retroware

Tutoriais de shoot 'em up · Parte 9 de 12

Vertical Shmup - Parte 9 - Os extraterrestres contra-atacam!

2 de agosto de 2022

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:

Captura de ecrã do tutorial

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:

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:

  1. Verifica se o extraterrestre está a menos de 8 pixels à esquerda e x pixels à direita do jogador

  2. 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ã

  3. 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.

  4. E se todos estes testes forem superados, é gerado um míssil (type 3, Image 6)

  5. 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

Todos os tutoriais de shoot 'em up