Minilop Retroware

Tutoriales de shoot 'em up · Parte 9 de 12

Vertical Shmup - Parte 9 - ¡Los alienígenas contraatacan!

2 de agosto de 2022

Esto está empezando a cobrar forma de verdad: tenemos una estructura fácilmente ampliable para crear patrones de movimiento de alienígenas, pero son un poco inofensivos de momento, ya que no pueden disparar armas propias. ¡Vamos a remediarlo!

Primero diseñaremos un sprite de misil alienígena:

Captura de pantalla del tutorial

Voy a usar el Sprite Type 3 para los misiles alienígenas, pero antes de empezar a programar hay algo que tenemos que tener en cuenta: la detección de colisiones de MPAGD. Como todos los sprites miden 16 px x 16 px, pero nuestro misil alienígena solo ocupa 3 píxeles de ancho, podrían producirse colisiones bastante injustas que visualmente no parecen un misil chocando con la nave espacial del jugador, pero que el motor registra como colisión. Por eso tendremos que incluir código que ayude a comprobar si la parte gráfica del sprite ha chocado realmente con la nave espacial.

También tenemos que pensar en cómo viajan los misiles: ¿bajan simplemente en línea recta?, ¿deben perseguir al jugador?, o ¿deben desviarse a la izquierda o a la derecha según la dirección en que viajaba el alienígena cuando lanzó el misil?

Esta última opción es mi favorita y, como todas las direcciones de los alienígenas se guardan en la variable DIRECTION, debería ser bastante fácil programar la dirección del misil: si un alienígena viajaba hacia la derecha cuando soltó el misil, el misil debe desviarse hacia la derecha, y así sucesivamente.

Otras cosas que debemos tener en cuenta son:

Hasta ahora hemos construido una estructura bastante flexible, así que estoy pensando en ampliar aún más la flexibilidad para poder variar la capacidad de disparo de misiles de cada oleada de ataque de alienígenas, lo que nos ayudará a hacer las oleadas de ataque progresivamente más difíciles.

Bien, pasemos al código. Primero, vamos a ampliar el código de nuestro Spawner para incluir algunas variables nuevas que controlen las capacidades de disparo de los alienígenas. Abre tu evento Spawner (Sprite Type 7) y añade el código resaltado:

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

Todo lo que hemos añadido aquí son tres variables nuevas que leeremos de la tabla de datos:

W (Weapon) - 0 significará que no pueden disparar misiles, 1 que sí pueden

H (probabilidad de disparar un arma): cuanto mayor sea el valor, menos probabilidad hay de que disparen un misil realmente

Q (altura de disparo): es el valor de Y por debajo del cual (es decir, más arriba en la pantalla, ya que las coordenadas Y del Spectrum empiezan en la parte superior de la pantalla) debe estar un alienígena para que se le permita disparar misiles.

He añadido algunos valores para cada una de las 6 oleadas de ataque definidas actualmente en la tabla de datos al final.

A continuación, necesitaremos añadir un par de líneas de código a nuestro evento del jugador para guardar los valores de las coordenadas X & Y del jugador, ya que las necesitaremos en el evento del alienígena para calcular a qué distancia horizontal está el alienígena del jugador. Abre tu evento del jugador y añade estas líneas al final de tu script:

LET M = X			; store player X in M
LET N = Y			; store player Y in N

A continuación, añadiremos algo de código al evento del alienígena (Sprite Type 5). Ábrelo y añade/actualiza el código resaltado:

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

 

Lee los comentarios del código resaltado y, con suerte, entenderás lo que estamos haciendo:

  1. Comprueba si el alienígena está a menos de 8 píxeles a la izquierda y x píxeles a la derecha del jugador

  2. si lo está, hay una probabilidad aleatoria de que se dispare un misil (siempre que además esté por encima de la distancia vertical mínima establecida en la variable Q) Y si todavía no hay demasiados sprites en pantalla

  3. La probabilidad de que se dispare un misil es de 1 entre H (donde H se define en la tabla de datos de la oleada de ataque, en el evento del spawner.

  4. Y si se superan todas estas pruebas, se genera un misil (type 3, Image 6)

  5. Después, pon el valor de DIRECTION del misil generado igual que el DIRECTION en el que viajaba el alienígena cuando disparó el misil.

Ahora solo nos falta escribir el código del misil, así que abre el Sprite Type 3 y pega esto:

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

Así que comprobamos las colisiones con el jugador y luego calculamos cuánto de directo es el impacto, creando en la práctica una caja de impacto rápida. Puedes ajustar estos valores a tu propio juego y tus gráficos.

Una última cosa por actualizar: fíjate en que, cuando registramos un impacto en el jugador, enviamos una instrucción al sprite del jugador para poner su dirección en 99, así que tendremos que añadir una cláusula a nuestro evento del jugador. Abre tu evento del jugador y añade:

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

He hecho esto para ahorrar memoria, ya que puede haber varios sitios en el código donde hacemos explotar al jugador (fíjate en que antes sustituí los ajustes de type, image, frame y direction en la colisión Alien/Player del evento Alien, el sprite type 5). ¡Cada byte cuenta!

Bien, eso debería ser todo lo que necesitamos para dar potencia de fuego a los alienígenas... ¡hora de probar!

Por ahora me parece bien, siempre podemos refinarlo más adelante según avance el juego.

A continuación haremos un poco de limpieza y ordenaremos algunas cosas.

SIGUIENTE: PARTE 10: UN POCO DE ORDEN

Todos los tutoriales de shoot 'em up