Crea un juego de combate de aviones en Corona Juego de acabado

Lo que vas a crear

Introducción

En la cuarta y última parte de esta serie, continuamos donde lo dejamos en el tutorial anterior. Crearemos aviones enemigos que el jugador necesita para evitar o disparar, y también crearemos un juego sobre la pantalla..

1. generarEnemys

los generarEnemys función genera un número entre Tres y Siete, y llama al generarEnemigoPlano funciona cada dos segundos por muchas veces numberOfEnemysToGenerate es igual a. Ingrese el siguiente fragmento de código para gamelevel.lua.

función generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) end

También necesitamos invocar esta función en el enterScene método como se muestra a continuación.

escena de la función: enterScene (evento) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys () end

Vamos a ver cual es la implementación de generarEnemigoPlano parece.

2. generarEnemigoPlano

los generarEnemigoPlano La función genera un plano enemigo. Hay tres tipos de aviones enemigos en este juego..

  • Regular , Se desplaza por la pantalla en línea recta.
  • Vacilar, Se mueve en un patrón de onda en el eje x
  • Cazador, persigue el avión del jugador
La función genereEnemolibro () si (gameOver ~ = verdadero) entonces local randomGridSpace = math.random (11) local randomEnemyNumber = math.random (3) tempEnemy local if (planeGrid [randomGridSpace] ~ = 0) luego generaEnemyPlane () devuelve else if ( randomEnemyNumber == 1) luego tempEnemy = display.newImage ("enemy1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "regular" elseif (randomEnemyNumber == 2) luego tempEnemy = display.newImage ( "enemy2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "waver" else tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" end planeGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; end if (numberOfEnemysGenerated == numberOfEnemysToGenerate) entonces numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) end end end end end

Primero verificamos que el juego no haya terminado todavía. Entonces generamos un randomGridSpace, un numero entre 111, y un azar randomEnemyNumber, un numero entre 1 y 3. los randomGridSpace se utiliza para colocar el plano en una de las once ranuras en la parte superior de la pantalla en el eje x. Si piensa que el área de juego está dividida en once secciones, solo queremos colocar nuevos aviones en una ranura que no haya sido tomada por otro plano. los planeGrid tabla contiene once 0y cuando colocamos un nuevo plano en una de las ranuras, establecemos la posición correspondiente en la tabla para 1 para indicar que la ranura ha sido tomada por un avión.

Verificamos si el índice de la randomGridSpace en la tabla no es igual a 0. Si no es así, sabemos que la ranura está actualmente ocupada y no debemos continuar, por lo que llamamos generarEnemigoPlano y regreso de la función..

A continuación, comprobamos qué randomEnemyNumber es igual a y establece tempEnemy a una de las tres imágenes enemigas, también le damos una propiedad de cualquiera regular, vacilar, o cazador. Debido a que Lua es un lenguaje dinámico, podemos agregar nuevas propiedades a un objeto en tiempo de ejecución. Luego establecemos cualquier índice que sea igual a randomGridSpace a 1 en el planeGrid mesa.

Insertamos tempEnemy en el Planos enemigos Tabla para referencia posterior e incremento numberOfEnemysGenerated. Si numberOfEnemysGenerated es igual a  numberOfEnemysToGenerate, fueron puestos numberOfEnemysGenerated a 0, invocar resetPlaneGrid, y establecer un temporizador que llamará generarEnemys de nuevo después de dos segundos. Este proceso se repite mientras el juego no haya terminado..

3. moveEnemyPlanes

Como habrás adivinado, la moveEnemyPlanes La función es responsable de mover los aviones enemigos. Dependiendo del avión tipo, La función apropiada se llama.

La función moveEnemyPlanes () si (#enemyPlanes> 0) luego para i = 1, #enemyPlanes hace si (enemyPlanes [i] .type == "regular") luego moveRegularPlane (enemyPlanes [i]) elseif (enemyPlanes [i] .type == "waver") luego moveWaverPlane (enemyPlanes [i]) else moveChaserPlane (enemyPlanes [i]) end end end end end end

Esta función necesita ser invocada en el gameLoop función.

function gameLoop () --SNIP-- checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () moveEnemyPlanes () end

4. moveRegularPlane

los moveRegularPlane simplemente mueve el avión por la pantalla a través del eje y.

función moveRegularPlane (plane) plane.y = plane.y + 4 end

5. moverWaverPlane

los moverWaverPlane La función mueve el plano hacia abajo de la pantalla a través del eje y y, en un patrón de onda, a través del eje x. Esto se logra utilizando el cos función de la biblioteca de matemáticas de Lua.

Si este concepto te es ajeno, Michael James Williams escribió una excelente introducción a Sinusoidal Motion. Se aplican los mismos conceptos, la única diferencia es que estamos usando coseno. Deberías pensar seno cuando se trata con el eje y y coseno cuando se trata con el eje x.

función moveWaverPlane (plane) plane.y = plane.y + 4 plane.x = (display.contentWidth / 2) + 250 * math.cos (numberOfTicks * 0.5 * math.pi / 30) final

En el fragmento anterior, usamos el numberOfTicks variable. Necesitamos incrementar esto cada vez que el gameLoop se llama funcion Agregue lo siguiente como la primera línea en el gameLoop función.

function gameLoop () numberOfTicks = numberOfTicks + 1 end

6. moveChaserPlane

los moveChaserPlane la función tiene el plano persiguiendo el jugador. Se mueve hacia abajo en el eje y a una velocidad constante y se mueve hacia la posición del jugador en el eje x. Eche un vistazo a la implementación de moveChaserPlane para aclarar.

función moveChaserPlane (plane) if (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x) luego plane.x = plane.x - 4 end plane.y = plane.y + 4 end

Si prueba el juego ahora, debería ver los aviones que se mueven por la pantalla.

7. fireEnemyBullets

De vez en cuando, queremos que los aviones enemigos disparen una bala. Sin embargo, no queremos que todos ellos disparen al mismo tiempo, así que elegimos solo un par de aviones para disparar.

function fireEnemyBullets () if (#enemyPlanes> = 2) y luego local numberOfEnemyPlanesToFire = math.floor (# enemyPlanes / 2) local tempEnemyPlanes = table.copy (enemyPlanes) local function fireBullet () local randIndex = math.random (#tempEnemyPlanes) tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: insert (tempBullet) .insert (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) end para i = 0, numberOfEnemyPlanesToFire do fireBullet () end end end end

Primero revisamos para asegurarnos de que el Planos enemigos La mesa tiene más de dos planos en ella. Si lo hace, obtenemos la numberOfEnemyPlanes disparar tomando la longitud de la Planos enemigos Mesa, dividirla por dos, y redondearla hacia abajo. También hacemos una copia del Planos enemigos Mesa, para que podamos manipularla por separado..

los FireBullet función elige un plano de la tempEnemyPlanes Mesa y hace que el avión dispare una bala. Generamos un número aleatorio basado en la longitud del tempEnemyPlanes tabla, cree una imagen con viñeta y colóquela utilizando el plano que se encuentre en la randIndex en el tempEnemyPlanes mesa. Luego eliminamos ese plano de la tabla temporal para asegurarnos de que no se volverá a elegir la próxima vez FireBullet se llama.

Repetimos este proceso sin embargo muchas veces numerOfEnemyPlanesToFire es igual a y llama al FireBullet función.

Necesitamos iniciar el temporizador que llama a esta función de vez en cuando. Para lograr esto, agregue lo siguiente a la startTimers función.

function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, recolectado en este artículo) , fireEnemyBullets, -1) end

8. moveEnemyBullets

También necesitamos mover las balas enemigas que están en pantalla. Esto es bastante simple usando el siguiente fragmento de código.

La función moveEnemyBullets () si (#enemyBullets> 0) entonces para i = 1, # enemyBullets do enemyBullets [i]. y = enemyBullets [i] .y + 7 end end end end

Invoca esta función en el gameLoop función.

function gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () end

9. checkEnemyBulletsOutOfBounds

Además de mover las balas enemigas, debemos verificar cuándo las balas enemigas han salido de la pantalla y eliminarlas cuando lo hacen. La implementación de checkEnemyBulletsOutOfBounds debería sentirse familiar a estas alturas.

función checkEnemyBulletsOutOfBounds () if (#enemyBullets> 0) luego para i = # enemyBullets, 1, -1 do if (enemyBullets [i] .y> display.contentHeight) luego enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (enemyBullets, i) end end end end end

Invoca esta función en el gameLoop función.

función gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () end

10. checkEnemyPlanesOutOfBounds

También deberíamos comprobar si los aviones enemigos se han movido fuera de la pantalla..

function checkEnemyPlanesOutOfBounds () if (#enemyPlanes> 0) y luego para i = # enemyPlanes, 1, -1 do if (enemyPlanes [i] .y> display.contentHeight) y luego enemyPlanes [i]: removeSelf () enemyPlanes [i] = nil table.remove (enemyPlanes, i) end end end end end

Invoca esta función en el gameLoop función

function gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () end

11. checkPlayerBulletsCollideWithEnemyPlanes

los checkPlayerBulletCollidesWithEnemyPlanes la función usa el hasColido función para comprobar si alguna de las balas del jugador ha chocado con alguno de los aviones enemigos.

function checkPlayerBulletsCollideWithEnemyPlanes () if (#playerBullets> 0 y #enemyPlanes> 0) luego para i = # playerBullets, 1, -1 do para j = # enemyPlanes, 1, -1 do if (hasCollided (playerBullets [i], enemyPlanes [ j])) luego playerBullets [i]: removeSelf () playerBullets [i] = nil table.remove (playerBullets, i) generaExplosion (enemyPlanes [j] .x, enemyPlanes [j] .y) enemyPlanes [j]: removeSelf ( ) enemyPlanes [j] = nil table.remove (enemyPlanes, j) local explosion = audio.loadStream ("explosion.mp3") local backgroundMusicChannel = audio.play (explosion, fadein = 1000) end end end end end end

Esta función utiliza dos anidados. para Bucles para comprobar si los objetos han colisionado. Para cada uno de los jugadorBullets, nos topamos con todos los planos en el Planos enemigos mesa y llamar al hasColido función. Si hay una colisión, eliminamos la bala y el avión, llamamos al generarExplosion Funciona, y carga y reproduce un sonido de explosión..

Invoca esta función en el gameLoop función.

function gameLoop () --SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () end

12. generarExplosion

los generarExplosion La función utiliza la clase SpriteObject de Corona. Los Sprites permiten secuencias animadas de cuadros que residen en las Hojas de imagen o Sprite. Al agrupar imágenes en una sola imagen, puede extraer ciertos cuadros de esa imagen y crear una secuencia de animación.

función generateExplosion (xPosition, yPosition) opciones locales = ancho = 60, altura = 49, numFrames = 6 local explosionSheet = graphics.newImageSheet ("explosion.png", opciones) local sequenceData = name = "explosion", start = 1, count = 6, time = 400, loopCount = 1 local explosionSprite = display.newSprite (explosionSheet, sequenceData) explosionSprite.x = xPosition explosionSprite.y = yPosition explosionSprite: addEventListener ("sprite", explosionListener) explosionSprite: play () final

los newImageSheet El método toma como parámetros la ruta a la imagen y una tabla de opciones para la Hoja de Sprite. Las opciones que configuramos son las anchura, la altura, y el NumFrames, Cuántas imágenes individuales componen esta hoja. Hay seis imágenes de explosión separadas como se muestra en la imagen de abajo.

A continuación, configuramos una mesa., secuenciaDatos, que es necesario por el SpriteObject. Establecemos el comienzo propiedad a 1, la contar a 6, y tiempo para 400.  los comienzo propiedad es el marco en el que comenzará la animación, el contar es cuántos cuadros incluye la animación, y la hora La propiedad es el tiempo que tarda la animación en reproducirse..

Entonces creamos el SpriteObject pasando en el hoja de explosión y secuenciaDatos, establece las posiciones x e y, y agrega un oyente al sprite. El oyente se utilizará para eliminar el sprite una vez que haya terminado su animación..

13. explosiónListener

los explosiónListener La función se utiliza para eliminar el sprite. Si el eventoes fase propiedad es igual a terminó, entonces sabemos que el sprite ha terminado su animación y podemos eliminarlo.

function explosionListener (evento) si (event.phase == "finalizó") entonces explosión local = evento. objetivo explosión: eliminar Explosión () (explosión) = final nulo

14. checkEnemyBulletsCollideWithPlayer

los checkEnemyBulletsCollideWithPlayer comprueba si alguna de las balas de los enemigos ha chocado con el avión del jugador.

función checkEnemyBulletsCollideWithPlayer () if (#enemyBullets> 0) luego para i = # enemyBullets, 1, -1 do if (hasCollided (enemyBullets [i], player)) luego enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (enemyBullets, i) if (playerIsInvincible == false) entonces killPlayer () end end end end end end end

Recorremos el enemigoBullets Mesa y comprueba si alguno de ellos ha chocado con el jugador. Si es verdadero, eliminamos esa bala en particular y, si playerIsInvincible es falso, invocamos killPlayer.

Invoca esta función en el gameLoop función.

function gameLoop () --SNIP-- checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () end

15. killPlayer

los killPlayer La función es responsable de verificar si el juego ha terminado y generar un nuevo jugador si no lo está..

function killPlayer () numberOfLives = numberOfLives- 1; if (numberOfLives == 0) entonces gameOver = true doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true end end

Primero decrementamos número de vidas por 1, y, si es igual a 0, llamamos al juego terminado función. Si el jugador tiene vidas restantes, lo llamamos spawnNewPlayer, seguido por ocultar vidas, show Lives, y establecer playerIsInvincible a cierto.

dieciséis. DoGameOver

los DoGameOver función le dice al guión gráfico para ir a la juego terminado escena.

función doGameOver () storyboard.gotoScene ("gameover") fin

17. spawnNewPlayer

los spawnNewPlayer La función es responsable de generar un nuevo jugador después de que haya muerto. El avión del jugador parpadea durante unos segundos para mostrar que es invencible temporalmente.

function spawnNewPlayer () número localOfTimesToFadePlayer = 5 número localOfTimesPlayerHasFaded = 0 función local fadePlayer () player.alpha = 0; transition.to (jugador, tiempo = 200, alfa = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 if (numberOfTimesPlayerHasFaded == numberOfTimesTofadePappPapPPPPPFPPPPPPFPPPPFPPPPFPPPPFPPPPPPPPPP)

Para hacer que el avión del jugador parpadee, lo introducimos cinco veces. En el fadePlayer función, fijamos el avión de alfa propiedad a 0, lo que lo hace transparente. Luego usamos la biblioteca de transición para desvanecer el alfa de regreso 1 Durante un período de 200 milisegundos. los a método de la transición objeto toma una tabla de opciones. En nuestro ejemplo, la tabla de opciones incluye un tiempo en milisegundos y la propiedad que nos gustaría animar, alfa, y el valor deseado, 1.

Nosotros incrementamos numberOfTimesThePlayerHasFaded y verifica si es igual a la cantidad de veces que quisimos que el jugador se desvaneciera. Entonces establecemos playerIsInvincible a falso. Usamos un temporizador para llamar al fadePlayer funciona sin embargo muchas veces numberOfTimerToFadePlayer es igual a.

Hay una manera de hacer todo esto sin usar el temporizador y es usando el transiciónes iteraciones propiedad en combinación con su onComplete entrenador de animales. Lea la documentación para obtener más información sobre este enfoque alternativo..

18. checkEnemyPlaneCollidesWithPlayer

Hay un control de colisión más que deberíamos hacer y es para ver si un avión enemigo choca con el avión del jugador..

function checkEnemyPlaneCollideWithPlayer () if (#enemyPlanes> 0) luego para i = # enemyPlanes, 1, -1 do if (hasCollided (enemyPlanes [i], player)) luego enemyPlanes [i]: removeSelf () enemyPlanes [i] = nil table.remove (enemyPlanes, i) if (playerIsInvincible == false) luego killPlayer () end end end end end end end

Recorremos los aviones enemigos y vemos si alguno de ellos choca con el avión del jugador. Si es verdad, eliminamos ese avión enemigo y llamamos. killPlayer. Si crees que hace que el juego sea más interesante, también podrías generar una explosión aquí..

19. exitScene

Cuando el juego termina, hacemos la transición al juego terminado escena. Recordar de antes en el tutorial, el exitScene La función es donde se eliminan los detectores de eventos, se detienen los temporizadores y se detiene el audio que se está reproduciendo..

escena de la función: exitScene (evento) grupo local = self.view rectUp: removeEventListener ("touch", movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("touch" , movePlane) audio.stop (planeSoundChannel) audio.dispose (planeSoundChannel) Tiempo de ejecución: removeEventListener ("enterFrame", gameLoop) cancelTimers () end scene: addEventListener ("exitScene", scene) 

Básicamente estamos deshaciendo lo que hicimos en el enterScene función. Llamamos al disponer método en el audio Objeto para liberar la memoria asociada al canal de audio. Vocación detener solo no libera la memoria.

20. cancelTimers

Como su nombre lo indica, el cancelTimers la función hace lo contrario de  startTimers, cancela todos los temporizadores.

función cancelTimers () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel (fireEnemyBulletsTimer) timer.cancel (generateFreeLifeTimer) final

21. Juego sobre escena

Es hora de crear el juego terminado escena. Comience agregando un nuevo archivo Lua a su proyecto llamado gameover.lua, y añádele el siguiente código.

storyboard local = requiere ("storyboard") escena local = storyboard.newScene () local gameOverText local newGameButton return scene 

22. crearScene

Agregue lo siguiente a gameover.lua encima escena de retorno. De aquí en adelante, todo el código debe colocarse sobre el escena de retorno declaración.

escena de la función: createScene (evento) local group = self.view local background = display.newRect (0, 0, display.contentWidth, display.contentHeight) background: setFillColor (0, .39, .75) group: insert (background) gameOverText = display.newText ("Game Over", display.contentWidth / 2,400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 gameOverText.anchorY = .5 group: insert (gameOverText ) newGameButton = display.newImage ("newgamebutton.png", 264,670) grupo: insertar (newGameButton) newGameButton.isVisible = false end

Como hicimos en las dos escenas anteriores, damos la juego terminado Escena de un fondo azul. Entonces creamos un Objeto de texto instancia llamando nuevo texto en monitor. los nuevo texto El método toma algunas opciones, el texto del objeto, su posición y la fuente a usar. Le damos un color amarillo por invocación. setFillColor, Pasando en valores RGB como porcentajes. Finalmente, creamos un botón y lo ocultamos por el momento..

23. enterScene

Cuando el guión gráfico ha pasado completamente a la juego terminado escena, la enterScene método se llama.

En enterScene, Eliminamos la escena anterior del guión gráfico. Utilizamos el método de conveniencia. escala a de la biblioteca de transición para escalar el gameOverText por un factor de 4. Añadimos un onComplete escucha la transición que llama elshowButton funciona una vez que la transición se ha completado. Por último, agregamos un detector de eventos de tap al botón del juego que invoca el empieza un juego nuevo función.

escena de la función: enterScene (evento) grupo local = self.view storyboard.removeScene ("gamelevel") transition.scaleTo (gameOverText, xScale = 4.0, yScale = 4.0, time = 2000, onComplete = showButton)) newGameButton: addEventListener (" pulse ", iniciarNuevoJuego) final

24. showButton

los showButton función oculta la gameOverText y muestra el nuevoGameButton.

 función showButton () gameOverText.isVisible = false newGameButton.isVisible = true end

25. empieza un juego nuevo

los empieza un juego nuevo función le dice al guión gráfico para la transición a la nivel de juego escena.

function startNewGame () storyboard.gotoScene ("gamelevel") end

26. exitScene

Necesitamos hacer algo de limpieza cuando salgamos de la juego terminado escena. Eliminamos el detector de eventos de tap que agregamos anteriormente al nuevoGameButton.

escena de la función: exitScene (evento) grupo local = self.view newGameButton: removeEventListener ("tap", startNewGame) final

27. Añadir Escuchas Escénicas

La última pieza del rompecabezas es agregar a los oyentes del evento de escena de los que hablamos anteriormente. Para hacer esto, agregue el siguiente fragmento de código a gameover.lua.

scene: addEventListener ("createScene", scene) scene: addEventListener ("enterScene", scene) scene: addEventListener ("exitScene", scene)

Conclusión

Hemos llegado al final de esta serie y ahora tenemos un juego de combate de aviones totalmente funcional. Espero que hayas encontrado útiles estos tutoriales y hayas aprendido algo en el camino. Gracias por leer.