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..
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.
generarEnemigoPlano
los generarEnemigoPlano
La función genera un plano enemigo. Hay tres tipos de aviones enemigos en este juego..
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 1 y 11, 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 0
y 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..
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
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
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
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.
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
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
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
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
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
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..
explosiónListener
los explosiónListener
La función se utiliza para eliminar el sprite. Si el evento
es 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
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
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
.
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
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ón
es 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..
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í..
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.
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
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
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..
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
showButton
los showButton
función oculta la gameOverText
y muestra el nuevoGameButton
.
función showButton () gameOverText.isVisible = false newGameButton.isVisible = true end
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
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
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)
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.