Minilop Retroware

Tutoriales de shoot 'em up · Parte 5 de 12

Vertical Shmup - Parte 5 - Una estructura para los enemigos

2 de agosto de 2022

Para empezar, voy a crear un sprite de nave alienígena y luego escribir algo de código para su movimiento.

Captura de pantalla del tutorial

En la primera oleada de ataque quiero que un flujo de estos tipos aparezca por la parte superior de la pantalla, baje en línea recta y luego se desvíe hacia un lado de la pantalla.

Quiero que el flujo de alienígenas aparezca en dos puntos de la parte superior de la pantalla, uno a la derecha del centro y otro a la izquierda.

El flujo de la derecha debe desviarse hacia el borde derecho, y el de la izquierda hacia el borde izquierdo.

Pero para empezar, vamos a dejar resuelto el movimiento básico. Usaremos el Sprite Type 2 para este alienígena:

MENÚ: 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

Ahora añadiremos dos de estos tipos a nuestra pantalla: pondremos uno a la izquierda del centro, arriba, y otro a la derecha del centro, y asignaremos a cada uno el Sprite Type 2. El código de arriba debería averiguar dónde está cada uno y establecer su movimiento en consecuencia.

MENÚ: EDITOR > SPRITE POSITIONS

Captura de pantalla del tutorial

Vamos a compilar el juego a ver qué pasa

{INSERT VIDEO}

Vale, no está mal, podemos refinarlos y mejorarlos más adelante.

Pero primero tenemos un problema mayor que resolver. ¿Cómo hacemos que aparezca un flujo de estos tipos?

Podría simplemente crear una pantalla para cada nivel, diseñar algunos sprites, escribir código de movimiento y usar las posiciones de sprites para definir sus puntos de partida, pero no creo que eso fuera el uso más eficiente de la memoria disponible.

Voy a adoptar un enfoque distinto: definiré un sprite Spawner, un sprite invisible cuya tarea es colocar enemigos en la pantalla en el momento adecuado.

El Spawner necesita tener propiedades propias, como:

  1. Si está en una posición fija o si se mueve

  2. Qué alienígenas genera (su imagen y su tipo de sprite)

  3. ¿Cuánto debe esperar entre cada aparición?

  4. ¿Cuántos alienígenas debe generar en cada oleada de ataque / nivel?

Este es un escenario ideal para guardar cada una de estas propiedades en una tabla DATA y luego leerlas con READ según haga falta.

Pero primero, vamos a crear el sprite Spawner creando un sprite nuevo con una imagen en blanco.

Captura de pantalla del tutorial

Antes de empezar a programar nuestro Spawner crearemos una variable nueva, 'L', que contendrá el nivel del juego: empezará en 1, el primer nivel, y avanzará a medida que el jugador derrote cada oleada de ataque.

MENÚ: EVENTS > GAME INITIALISATION

EVENT GAMEINIT

LET L = 1			; Level (starts at 1)

A continuación definiremos el Spawner en el Sprite Type 7 y escribiremos algo de 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 

Bien, aquí es donde la cosa se complica un poco, pero ten paciencia conmigo, intentaré explicarlo lo más sencillo posible. Vamos a usar algunas de las funciones menos conocidas de MPAGD que no están disponibles en el AGD normal ni siquiera en AGDx, pero cuando las entiendas te garantizo que empezarás a crear mejores juegos.

Hablo de READ y DATA. Los he usado a fondo en todos los juegos que he publicado: me han ayudado a ahorrar memoria, a escribir mejor código, a crear mecánicas nuevas e interesantes y mucho más. En mi opinión, son las mejores funciones de MPAGD.

Lo que hace básicamente el código de arriba es:

  1. Cuando el spawner aparece por primera vez, lee el primer número de la tabla DATA y lo guarda en la variable de sprite AIRBORNE (¡ya lo explicaré!)

  2. Lee el segundo número de la tabla de datos: será la posición X del Spawner

  3. Lee el tercer número de la tabla de datos: será la posición Y del Spawner

  4. Lee el cuarto número de la tabla de datos: nos dirá el número de Sprite Type del alienígena que se generará

  5. Lee el quinto número de la tabla de datos: nos dirá el número de imagen de sprite del alienígena que se generará

  6. Lee el sexto número de la tabla de datos: nos dirá cuántos enemigos hay que generar en esta oleada de ataque / nivel

  7. Lee el séptimo número de la tabla de datos: nos dirá cuánto debe esperar el spawner antes de generar el siguiente alienígena

La tabla DATA, por el momento, solo contiene los datos de la primera oleada de ataque:

  1. 0 (AIRBORNE) Define el patrón de movimiento del propio spawner, aunque no lo usaremos en esta primera ocasión

  2. 165 (X) la coordenada x del spawner

  3. 8 (Y) la coordenada y del spawner

  4. 2 (J) el número de tipo de sprite del alienígena

  5. 2 (K) el número de imagen de sprite del alienígena

  6. 5 (E) el número de alienígenas de esta oleada de ataque

  7. 12 (G) el número de ciclos de cpu que hay que esperar entre la aparición de un alienígena y el siguiente

Antes de probar nuestros avances, necesitamos inicializar el valor de SETTINGA a 0 cuando se genera el Spawner por primera vez

MENÚ: EVENTS > INITIALISE SPRITES

y añadir el código resaltado:

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 a ejecutarlo y comprobar que todo va como esperamos.

Sí, parece que está bien

Ahora vamos a añadir los datos de la siguiente oleada. Esta vez pondremos el Spawner a la izquierda del centro. En el evento de tu spawner añade/actualiza el código resaltado, que añadirá los datos de la siguiente oleada / nivel

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 

Ahora vamos a compilarlo de nuevo:

{insert video}

Vale, tiene buena pinta: vemos la primera oleada a la izquierda y luego la segunda a la derecha, y el patrón se repite en bucle.

Más adelante ampliaremos las capacidades del Spawner, pero como puedes ver en el vídeo de arriba, el juego tiene un problema fundamental: no pasa nada cuando los torpedos del jugador alcanzan a los alienígenas.

Así que lo abordaremos a continuación...

PARTE 6: ELIMINAR ALIENÍGENAS

Todos los tutoriales de shoot 'em up