En esta serie de tutoriales, aprenderemos a crear un juego de disparos en el espacio al igual que el juego clásico Defender del espacio. Sigue leyendo!
En esta versión de Space Defender, el jugador tendrá que defender su espacio disparando a los enemigos. Cada vez que el jugador destruye exitosamente a un enemigo, ganará puntos y cuando el jugador haya alcanzado 20 o 40 puntos, su arma recibirá una mejora. Para mezclar las cosas, este juego enviará paquetes de bonos que valen 5 puntos. Para ver el juego en acción, mira el video corto de arriba.
En la parte 1 de esta serie, aprendimos cómo configurar nuestra aplicación, cómo usar fuentes personalizadas, cómo usar un guión gráfico y cómo configurar nuestro menú principal. En la Parte 2 de esta serie, aprenderemos cómo crear el juego de nuestra aplicación. Entonces empecemos!
Nuestro primer paso es crear un nuevo archivo llamado game.lua. Una vez creado, abre el archivo en tu editor favorito.
Como estamos empezando una nueva escena, necesitamos algunas bibliotecas. Utilizaremos el motor de física incorporado en Corona SDK para la detección de colisiones.
guión gráfico local = requiere ("guión gráfico") escena local = storyboard.newScene () física local = requiere ("física")
Después de que tengamos configuradas nuestras bibliotecas, configuraremos el motor de física. Las siguientes configuraciones establecerán la gravedad en 0 (como en el espacio) y establecerán las iteraciones en 16. El setPositionIterations significa que el motor pasará a través de 16 posiciones por fotograma. Cualquier cosa superior a 16 puede afectar negativamente el rendimiento del juego.
physics.start () physics.setGravity (0, 0) physics.setPositionIterations (16)
Aunque este paso no es necesario para este tutorial, es una buena práctica "inicializar" el generador de números aleatorios. Me gusta usar el tiempo actual para sembrar el generador..
math.randomseed (os.time ())
Ahora definiremos algunas variables para nuestro juego. Cada variable tiene un comentario al lado que explica el propósito de la variable.
local screenW, screenH, halfW, halfY = display.contentWidth, display.contentHeight, display.contentWidth * 0.5, display.contentHeight * 0.5 local gameover_returntomenu - adelante declard our game over button - Configurar la configuración del juego motionx = 0; - Variable usada para mover el carácter a lo largo de la velocidad del eje y = 10; - Controla la velocidad del barco playerScore = 0; - Establece la puntuación del jugador playerLives = 20; - Establece el número de vidas para el jugador slowEnemySpeed = 2375; - Establece qué tan rápido se mueven las naves blancas a través de la pantalla slowEnemySpawn = 2400; - Establece la tasa de generación de la nave blanca fastEnemySpeed = 1875; - Establece qué tan rápido se mueven las naves verdes a través de la pantalla fastEnemySpawn = 1800; - Establece la tasa de generación de la nave verde bulletSpeed = 325; - Establece qué tan rápido viaja la bala a través de la pantalla bulletSpawn = 250; - Establece la tasa de generación de bala.
Después de crear las variables, vamos a configurar la escena dentro de la función. escena: createScene
. Si recuerdas la Parte 1, esta función se usa para crear elementos visuales y la lógica del juego. En una función posterior, utilizaremos estas funciones para ejecutar el juego..
En el siguiente código, estamos creando el escena: createScene
Función y añadiendo el fondo y las paredes superior / inferior. Ambas paredes están configuradas como objetos de física estática para evitar que el jugador salga de la pantalla.
escena de la función: createScene (evento) grupo local = self.view: configura elementos visuales y muros local bg = display.newImageRect ("images / BKG.png", 480, 320) bg.x = halfW bg.y = halfY grupo: insertar (bg) topwall local = display.newRect (0,0, screenW, 20) topwall.y = -5 topwall: setFillColor (0,0,0) topwall.alpha = 0,01 physics.addBody (topwall, "static ") grupo: insertar (pared superior) pared inferior local = pantalla.nuevoRect (0,0, screenW, 20) pared inferior.y = 325 pared inferior: setFillColor (0,0,0) parte inferior.alpha = 0,01 physics.addBody (pared inferior," grupo ") estático: inserte el final (parte inferior)
Dentro de la misma escena: createScene
función, pero después de la pared inferior
objeto de visualización, vamos a añadir cuatro objetos de visualización más. Aquí hay una explicación del propósito de cada objeto..
btn_up
, btn_down
: Estos objetos de visualización actuarán como botones en el lado izquierdo de la pantalla y cada objeto moverá la nave hacia arriba o hacia abajo respectivamente. Sin embargo, no son operables hasta que configuramos la función de movimiento..Barra enemiga
: Este objeto de visualización está configurado como un sensor y solo reaccionará ante colisiones físicas. Cuando reacciona a las colisiones, eliminará el objeto enemigo y restará uno de las vidas de los jugadores..local btn_up = display.newRect (0,0,75,160) btn_up: setReferencePoint (display.TopLeftReferencePoint) btn_up.x, btn_up.y = 0,0; btn_up.alpha = 0.01 grupo: insertar (btn_up) local btn_down = display.newRect (0,0,75,160) btn_down: setReferencePoint (display.BottomLeftReferencePoint) btn_down.x, btn_down.y = 0, screenH; btn_down.alpha = 0.01 grupo: inserta (btn_down) local enemyHitBar = display.newRect (-20,0,20,320) enemyHitBar: setFillColor (0,0,0) enemyHitBar.name = "enemyHitBar" physics.addBody (enemyHitBar, isSensor = verdadero) grupo: insertar (enemyHitBar)
Inmediatamente después del objeto de visualización de la barra de enemigos, vamos a agregar algunos elementos de la GUI para mostrar la puntuación del jugador y la vida del jugador. También mostraremos texto en la pantalla que dice "Mover hacia arriba" y "Mover hacia abajo" para notificar al jugador dónde deben tocar para mover la nave hacia arriba o hacia abajo..
local gui_score = display.newText ("Puntuación:" ... playerScore, 0,0, "Kemco Pixel", 16) gui_score: setReferencePoint (display.TopRightReferencePoint) gui_score.x = screenW group: insert (gui_score) local gui_lives = display.new ("Vidas:" ... jugador Vidas, 0,0, "Kemco Pixel", 16) gui_lives: setReferencePoint (display.BottomRightReferencePoint) gui_lives.x = screenW gui_lives.y = screenH group: insert (gui_lives) local gui_moveup = display.newtext ( "Move Up", 0,0,50,100, "Kemco Pixel", 16) grupo: insertar (gui_moveup) local gui_movedown = display.newText ("Move Down", 0,0,50,23, "Kemco Pixel", 16 ) gui_movedown: setReferencePoint (display.BottomLeftReferencePoint) gui_movedown.y = screenH group: insert (gui_movedown)
A continuación, agregaremos la nave del jugador a la pantalla. El barco se agregará como un objeto de física dinámico para que pueda reaccionar a las colisiones con otros objetos de física. Profundizaremos en las colisiones más adelante en este tutorial..
local ship = display.newImageRect ("images / spaceShip.png", 29, 19) ship.x, ship.y = 75, 35 ship.name = "ship" physics.addBody (ship, "dynamic", friction = 0.5, bounce = 0) grupo: insertar (enviar)
¿Recuerdas el btn_up
y btn_down
Mostrar los objetos que agregamos? Ahora vamos a agregar oyentes de eventos a estos objetos para ayudar a que el jugador se mueva. Cuando btn_up
se toca, haremos que nuestra velocidad variable sea negativa y cuando btn_down
Es tocado vamos a hacer nuestra velocidad positiva. Al hacer esta variable positiva y negativa, le estamos diciendo a nuestra próxima función que mueva la nave hacia arriba o hacia abajo.
-- Cuando se toca el botón hacia arriba, configure nuestro movimiento para mover la función de envío hacia arriba btn_up: touch () motionx = -speed; end btn_up: addEventListener ("touch", btn_up) - Cuando se toca el botón hacia abajo, configure nuestro movimiento para mover la función de envío hacia abajo btn_down: touch () motionx = speed; end btn_down: addEventListener ("touch", btn_down)
Después de que hemos añadido oyentes de eventos a nuestro btn_up
y btn_down
Mostrar objetos, vamos a crear dos escuchas de eventos en tiempo de ejecución con sus respectivas funciones. Estas funciones ejecutarán cada fotograma y la única captura con las funciones de tiempo de ejecución es que debe especificar cuándo detenerlas. Vamos a cubrir eso más tarde. Por ahora, la función de parada establecerá la variable motionx
a 0 (porque no se toca ninguno de los botones) y el moveguy
la función agregará la variable motionx
a nuestro barco y
posición.
función local stop (evento) si event.phase == "terminó" entonces motionx = 0; end end Runtime: addEventListener ("toque", detener): esta función realmente moverá el barco según la función local de movimiento moveguy (evento) ship.y = ship.y + motionx; end Runtime: addEventListener ("enterFrame", moveguy)
Por ahora, tenemos nuestra nave en movimiento, ¡pero no está disparando! Para preparar el barco para disparar balas, tenemos que crear el despacho de bomberos ()
función. Esta función creará nuevos objetos de visualización que reaccionan a colisiones físicas y esta función también moverá el objeto a través de la pantalla de izquierda a derecha.
Para que el juego sea más interesante, permitiremos al jugador disparar más balas cuando alcancen una puntuación determinada. Cuando el jugador llegue a 20, el barco disparará dos balas y cuando el jugador llegue a 40, el barco disparará una tercera bala que dispara hacia abajo en diagonal.
function fireShip () bullet = display.newImageRect ("images / bullet.png", 13, 8) bullet.x = ship.x + 9 bullet.y = ship.y + 6 bullet: toFront () bullet.name = " bullet "physics.addBody (bullet, isSensor = true) transition.to (bullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end; ) si (playerScore> = 20) entonces secondBullet = display.newImageRect ("images / bullet.png", 13, 8) secondBullet.x = ship.x + 9 secondBullet.y = ship.y + 12 secondBullet: toFront ( ) secondBullet.name = "bullet" physics.addBody (secondBullet, isSensor = true) transition.to (secondBullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end;) end si (playerScore> = 40) luego thirdBullet = display.newImageRect ("images / bullet.png", 13, 8) thirdBullet.x = ship.x + 9 thirdBullet.y = ship. y + 12 thirdBullet: toFront () thirdBullet.name = "bullet" physics.addBody (thirdBullet, isSensor = true) transition.to (thirdBullet, time = bulletSpeed, x = 500, y = ship.y + 100, onComplete = function (self) self.parent: remove (self); self = nil; fin; )
Después de configurar nuestra nave para disparar, ¡debemos darle al jugador algunos enemigos para disparar! Crearemos dos funciones diferentes. - createSlowEnemy ()
y createFastEnemy ()
. Ambas funciones crearán un objeto de visualización física que se mueve de derecha a izquierda con la velocidad del enemigo y la única diferencia de imagen..
function createSlowEnemy () enemy = display.newImageRect ("images / enemy.png", 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "enemigo" physics.addBody (enemigo, isSensor = verdadero) transition.to (enemigo, tiempo = slowEnemySpeed, x = -20) end function createFastEnemy () enemy = display.newImageRect ("images / fastEnemy.png" , 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "enemigo" physics.addBody (enemigo, isSensor = true) transición. a (enemigo, tiempo = fastEnemySpeed, x = -20) final
A continuación, crearemos paquetes de bonificación para que nuestro jugador los capture dentro de la función. CreaBonus ()
. los CreaBonus ()
La función creará un objeto de visualización física que se mueve de derecha a izquierda y cada paquete extra que el jugador toma, ganará 5 puntos..
función createBonus () bonus = display.newImageRect ("images / bonus.png", 18, 18) bonus.rotation = 180 bonus.x = 500 bonus.y = math.random (10, screenH-10) bonus.name = "bonus" physics.addBody (bonus, isSensor = true) transition.to (bonus, time = 1475, x = -20, onComplete = function () display.remove (bonus) bonus = nil end;) fin
Nuestra siguiente función es la función updateLives (). Esta función se activará cada vez que un enemigo supere al jugador para darle al jugador el objetivo de defender su lado del espacio. Si el número de vidas es superior a 0, entonces esta función restará una vida y actualizará el texto en pantalla. De lo contrario, resultará en un juego sobre la escena..
En el juego sobre escena, estamos cancelando todos nuestros temporizadores y eliminamos a todos nuestros oyentes de eventos. Con el Corona SDK, es muy importante recordar que debe indicar explícitamente a su aplicación cuándo eliminar los escuchas y los temporizadores en tiempo de ejecución (solo cuando el temporizador está funcionando). Una vez que se hayan eliminado, mostraremos un mensaje sobre el juego y permitiremos que el jugador regrese al menú.
function updateLives () if (playerLives> = 0) y luego playerLives = playerLives - 1 gui_lives.text = "Lives:"… playerLives gui_lives.x = screenW else timer.cancel (tmr_fireShip) timer.cancel (tmrssscsscsscsscscsscsscscsscssscscscsssrssccscsssrssccscsscss) ) timer.cancel (tmr_sendFastEnemies) timer.cancel (tmr_sendBonus) Runtime: removeEventListener ("collision", onCollision) Runtime: removeEventListener ("enterFrame", moveguy) Runtime: removeEventListener ("touch") gameover_message = display.newText ("Game Over!", 0,0, "Kemco Pixel", 32) gameover_message.x = halfW gameover_message.y = halfY - 15 group: insert (gameover_message) function returnToMenuTouch (evento) if (event. fase == "comenzó") luego storyboard.gotoScene ("menu", "slideRight", "1000") end end gameover_returntomenu = display.newText ("Return To Menu", 0,0, "Kemco Pixel", 28) gameover_returntomenu .x = halfW gameover_returntomenu.y = halfY + 35 gameover_returntomenu: addEventListener ("touch", returnToMenuTouch) grupo: en sert (gameover_returntomenu) end end end
Estamos listos para nuestra función final dentro de nuestra escena: ¡la función createScene ()! Esta función manejará toda nuestra detección de colisiones comparando la propiedad mi nombre
de object1 a la que tiene el objeto 2. Cada objeto se pasa como un parámetro a esta función bajo el nombre de la variable evento
.
Para facilitarte las cosas, he desglosado los cinco casos de colisión..
función onCollision (evento) si (evento.objeto1.nombre == "bullet" y evento.objeto2.nombre == "enemigo") luego muestra.remueve (evento.objeto2) playerScore = jugadorScore + 1 elseif (evento.objeto1.nombre == "enemigo" y event.object2.name == "bullet") luego display.remove (event.object1) playerScore = playerScore + 1 elseif (event.object1.name == "ship" y event.object2.name = = "bonus") luego display.remove (event.object2) playerScore = playerScore + 5 elseif (event.object1.name == "enemigo" y event.object2.name == "enemyHitBar") luego display.remove (evento. object1) updateLives () elseif (event.object1.name == "enemyHitBar" y event.object2.name == "enemy") luego display.remove (event.object2) updateLives () end gui_score.text = "Score:" … PlayerScore gui_score.x = screenW end
Ya que tenemos todo configurado para nuestro juego, ¡solo necesitamos hacer que todo se mueva! Dentro de la funcion escena: enterScene ()
- recuerda que el enterScene
la función está fuera de la crearScene
Función: crearemos 5 temporizadores y un detector de tiempo de ejecución. Los temporizadores enviarán las balas, enemigos y bonificaciones, mientras que el oyente en tiempo de ejecución manejará la detección de colisión..
escena de la función: enterScene (evento) grupo local = self.view tmr_fireShip = timer.perform. createSlowEnemy, 0) tmr_sendFastEnemies = timer.performWithDelay (fastEnemySpawn, createFastEnemy, 0) tmr_sendBonus = timer.performWithDelay ("2500, createBonus, 0) Runtime: addEventListener (" collision ", en
La adición final (¡lo prometo!) Es la escena: destroyScene ()
La función y los oyentes del evento de escena. La función de destruir escena se asegurará de que la física se elimine una vez que el jugador abandone la escena. Los oyentes del evento de escena llamarán al crearScene
, enterScene
, y destruir Escena
respectivamente.
escena de la función: destroyScene (evento) grupo local = self.view package.loaded [physics] = nil physics = nil end scene: addEventListener ("createScene", scene) scene: addEventListener ("enterScene", scene) scene: addEventListener (" destroyScene ", scene) return scene
¡Felicidades! ¡Has aprendido sobre muchas cosas, como la función de guión gráfico de Corona, la física, las colisiones y mucho más! Estas son habilidades valiosas que se pueden aplicar a casi cualquier juego y si quieres crear este juego para tu dispositivo, te recomiendo los documentos oficiales de Corona para construir el dispositivo..
Muchas Gracias Por Leer! Si tiene alguna pregunta, por favor déjelos en los comentarios a continuación..