Corona SDK: Crear un juego de obstáculos para helicópteros
() translation by (you can also view the original English article)
En este tutorial, te mostraré cómo crear un juego de evasión de obstáculos usando el Corona SDK. Aprenderás más sobre los controles táctiles, la detección de colisiones y la física. El objetivo del juego es evitar tantos obstáculos como puedas para obtener la mayor puntuación. Empecemos.
1. Descripción de la aplicación



Usando gráficos ya hechos, crearemos un juego entretenido usando el lenguaje de programación Lua y las APIs de Corona SDK El jugador podrá mover un helicóptero por la pantalla y deberá evitar los obstáculos que encuentre. Puedes modificar los parámetros en el código para personalizar el juego.
2. Dispositivo objetivo



Lo primero que tenemos que hacer es seleccionar la plataforma en la que queremos ejecutar nuestra aplicación para poder elegir el tamaño de las imágenes que vamos a utilizar.
La plataforma iOS tiene los siguientes requisitos:
- iPad 1/2/Mini: 1024px x 768px, 132 ppi
- iPad Retina: 2048px x 1536px, 264 ppi
- iPhone/iPod Touch: 320px x 480px, 163 ppi
- iPhone/iPod Retina: 960px x 640px, 326 ppi
- iPhone 5/iPod Touch: 1136px x 640px, 326 ppi
Dado que Android es una plataforma abierta, existen muchos dispositivos y resoluciones diferentes. Algunas de las características de pantalla más comunes son:
- Asus Nexus 7 Tablet: 800px x 1280px, 216 ppi
- Motorola Droid X: 854px x 480px, 228 ppi
- Samsung Galaxy SIII: 720px x 1280px, 306 ppi
En este tutorial, nos centraremos en la plataforma iOS en términos de gráficos. En particular, desarrollaremos para el iPhone y el iPod Touch. Sin embargo, el código de este tutorial también se puede utilizar en tu objetivo de la plataforma Android.
3. Interface



Utilizaremos una interfaz de usuario sencilla que incluye múltiples formas, botones, mapas de bits, etc. Los gráficos que utilizaremos para este tutorial se encuentran en el proyecto incluido en este tutorial.
4. Export Graphics



Dependiendo del dispositivo que hayas seleccionado, puede que tengas que convertir los gráficos a la resolución recomendada (ppi), lo que puedes hacer en tu editor de imágenes favorito. Yo he utilizado la opción Ajustar tamaño... del menú Herramientas de la aplicación Vista previa en OS X. Recuerda dar a las imágenes un nombre descriptivo y guardarlas en la carpeta del proyecto.
5. Configuración de la aplicación
Utilizaremos un archivo de configuración, config.lua
, para hacer que la aplicación vaya a pantalla completa en todos los dispositivos. El archivo de configuración muestra el tamaño original de la pantalla y el método utilizado para escalar el contenido en caso de que la aplicación se ejecute en una resolución diferente.
1 |
application = |
2 |
{ |
3 |
content = |
4 |
{ |
5 |
width = 320, |
6 |
height = 480, |
7 |
scale = "letterbox" |
8 |
}, |
9 |
} |
6. main.lua
Vamos a escribir la aplicación real. Abre tu editor de Lua preferido. Cualquier editor de texto plano funcionará, pero se recomienda utilizar un editor de texto que tenga resaltado de sintaxis. Crea un nuevo archivo y guárdalo como main.lua
en tu carpeta de proyecto.
7. Estructura del código
Estructuraremos nuestro código como si fuera una clase. Si estás familiarizado con ActionScript o Java, la estructura del proyecto te resultará familiar.
1 |
Necessary Classes |
2 |
|
3 |
Variables and Constants |
4 |
|
5 |
Declare Functions |
6 |
|
7 |
constructor (Main function) |
8 |
|
9 |
class methods (other functions) |
10 |
|
11 |
call Main function |
8. Ocultar la barra de estado
1 |
display.setStatusBar(display.HiddenStatusBar) |
Este fragmento de código oculta la barra de estado. La barra de estado es la barra situada en la parte superior de la pantalla del dispositivo que muestra la hora, la señal y otros indicadores.
9. Importar Physics
Utilizaremos la biblioteca de física para manejar las colisiones. Importa la biblioteca utilizando el fragmento de código que se muestra a continuación.
1 |
-- Physics |
2 |
|
3 |
local physics = require('physics') |
4 |
physics.start() |
10. Antecedentes



Un simple fondo para la interfaz de usuario de la aplicación. El siguiente fragmento de código dibuja el fondo en la pantalla.
1 |
-- Graphics |
2 |
|
3 |
-- [Background] |
4 |
|
5 |
local gameBg = display.newImage('gameBg.png') |
11. Vista del título



Esta es la vista del título. Es la primera pantalla interactiva que aparece en nuestro juego. Estas variables almacenan sus componentes.
1 |
-- [Title View] |
2 |
|
3 |
local title |
4 |
local playBtn |
5 |
local creditsBtn |
6 |
local titleView |
12. Vista de los créditos



La vista de créditos muestra los créditos y el copyright de la aplicación. Esta variable se utiliza para almacenarla.
1 |
-- [CreditsView] |
2 |
|
3 |
local creditsView |
13. Mensaje de instrucciones

Al principio del juego aparecerá un mensaje con instrucciones y desaparecerá después del primer toque.
1 |
-- Instructions |
2 |
|
3 |
local ins |
14. Helicóptero

Este es el gráfico del helicóptero. El jugador controlará el helicóptero mediante controles táctiles.
1 |
-- Helicopter |
2 |
|
3 |
local helicopter |
15. Bloques

Los bloques son los obstáculos que el jugador debe evitar.
1 |
-- Blocks |
2 |
|
3 |
local blocks = {} |
16. Alerta

La alerta se muestra cuando el jugador pierde la pelota y el juego se acaba. Muestra un mensaje y termina el juego.
1 |
-- Alert |
2 |
|
3 |
local alertView |
17. Sonidos

Para animar el juego, utilizaremos efectos de sonido. Los sonidos utilizados en el juego se obtuvieron de as3soundfxr. La música de fondo la encontré en playonloop.
1 |
-- Sounds |
2 |
|
3 |
local bgMusic = audio.loadStream('POL-rocket-station-short.wav') |
4 |
local explo = audio.loadSound('explo.wav') |
18. Variables
El siguiente fragmento de código muestra las variables que utilizaremos. Lee los comentarios para entender para qué se utiliza cada variable.
1 |
-- Variables |
2 |
|
3 |
local timerSrc --Blocks timer |
4 |
local yPos = {90, 140, 180} --Possible positions for the blocks |
5 |
local speed = 5 --Block speed |
6 |
local speedTimer --Increases blocks speed |
7 |
local up = false --Used to determine if helicopter is going up |
8 |
local impulse = -60 --physics value to make helicopter go up |
19. Declarar funciones
Declarar todas las funciones como local
al principio.
1 |
-- Functions |
2 |
|
3 |
local Main = {} |
4 |
local startButtonListeners = {} |
5 |
local showCredits = {} |
6 |
local hideCredits = {} |
7 |
local showGameView = {} |
8 |
local gameListeners = {} |
9 |
local createBlock = {} |
10 |
local movePlayer = {} |
11 |
local increaseSpeed = {} |
12 |
local update = {} |
13 |
local alert = {} |
20. Constructor
A continuación, creamos la función que inicializará la lógica del juego.
1 |
function Main() |
2 |
-- code... |
3 |
end |
21. Añadir vista de título
Comenzamos colocando la vista de título en el escenario y llamamos a una función que añadirá escuchas de toque a los botones.
1 |
function Main() |
2 |
titleBg = display.newImage('titleBg.png') |
3 |
playBtn = display.newImage('playBtn.png', 220, 178) |
4 |
creditsBtn = display.newImage('creditsBtn.png', 204, 240) |
5 |
titleView = display.newGroup(titleBg, playBtn, creditsBtn) |
6 |
|
7 |
startButtonListeners('add') |
8 |
end |
22. oyentes del botón de inicio
La siguiente función añade los listeners necesarios a los botones del TitleView
.
1 |
function startButtonListeners(action) |
2 |
if(action == 'add') then |
3 |
playBtn:addEventListener('tap', showGameView) |
4 |
creditsBtn:addEventListener('tap', showCredits) |
5 |
else |
6 |
playBtn:removeEventListener('tap', showGameView) |
7 |
creditsBtn:removeEventListener('tap', showCredits) |
8 |
end |
9 |
end |
23. Mostrar créditos
La pantalla de créditos se muestra cuando el usuario pulsa el botón sobre. Se añade un receptor de toques a la vista de créditos para descartarla cuando el usuario la pulse.
1 |
function showCredits:tap(e) |
2 |
playBtn.isVisible = false |
3 |
creditsBtn.isVisible = false |
4 |
creditsView = display.newImage('credits.png', -110, display.contentHeight-80) |
5 |
transition.to(creditsView, {time = 300, x = 55, onComplete = function() creditsView:addEventListener('tap', hideCredits) end}) |
6 |
end |
24. Ocultar créditos
Cuando el usuario toca la vista de los créditos, se anima a salir del escenario y a eliminarlos.
1 |
function hideCredits:tap(e) |
2 |
playBtn.isVisible = true |
3 |
creditsBtn.isVisible = true |
4 |
transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end}) |
5 |
end |
25. Mostrar la vista del juego
Cuando se pulsa el botón de jugar, la vista del título se anima fuera de la pantalla y se revela el juego. Hay una serie de partes móviles, así que vamos a ver con más detalle cada una de ellas.
1 |
function showGameView:tap(e) |
2 |
transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end}) |
26. Mensaje de instrucciones
El siguiente fragmento de código añade el mensaje de instrucciones.
1 |
ins = display.newImage('ins.png', 180, 270) |
2 |
transition.from(ins, {time = 200, alpha = 0.1, onComplete = function() timer.performWithDelay(2000, function() transition.to(ins, {time = 200, alpha = 0.1, onComplete = function() display.remove(ins) ins = nil end}) end) end}) |
27. Marcador TextField
El siguiente fragmento de código crea un campo de texto que muestra la puntuación actual del jugador en la parte superior derecha del escenario.
1 |
-- TextFields |
2 |
|
3 |
scoreTF = display.newText('0', 450, 5, 'Marker Felt', 14) |
4 |
scoreTF:setTextColor(255, 255, 255) |
28. Helicóptero
A continuación, es el momento de añadir el gráfico del helicóptero al escenario, como se muestra a continuación.
1 |
-- Helicopter |
2 |
|
3 |
helicopter = display.newImage('helicopter.png', 23, 152) |
29. Paredes
En el siguiente fragmento de código, utilizamos la API de gráficos de Corona para crear varias líneas que añadiremos a la simulación de física un poco más adelante en este tutorial. Se utilizarán para detectar colisiones con el helicóptero.
1 |
-- Walls |
2 |
|
3 |
local top = display.newRect(0, 60, 480, 1) |
4 |
top:setFillColor(34, 34, 34) |
5 |
local bottom = display.newRect(0, 260, 480, 1) |
6 |
bottom:setFillColor(34, 34, 34) |
7 |
end |
30. Física
A continuación, tenemos que añadir la física necesaria a cada objeto.
1 |
-- Add physics |
2 |
|
3 |
physics.addBody(helicopter) |
4 |
physics.addBody(top, 'static') |
5 |
physics.addBody(bottom, 'static') |
31. Inicio del juego
Creamos un grupo para los bloques, invocamos la función gameListeners
e iniciamos la música de fondo.
1 |
blocks = display.newGroup() |
2 |
gameListeners('add') |
3 |
audio.play(bgMusic, {loops = -1, channel = 1}) |
4 |
end |
32. Oyentes del juego
El siguiente fragmento de código puede parecer complejo, pero simplemente añade unas cuantas listeners para iniciar la lógica del juego.
1 |
function gameListeners(action) |
2 |
if(action == 'add') then |
3 |
gameBg:addEventListener('touch', movePlayer) |
4 |
Runtime:addEventListener('enterFrame', update) |
5 |
timerSrc = timer.performWithDelay(1300, createBlock, 0) |
6 |
speedTimer = timer.performWithDelay(5000, increaseSpeed, 5) |
7 |
helicopter:addEventListener('collision', onCollision) |
8 |
else |
9 |
gameBg:addEventListener('touch', movePlayer) |
10 |
Runtime:removeEventListener('enterFrame', update) |
11 |
timer.cancel(timerSrc) |
12 |
timerSrc = nil |
13 |
timer.cancel(speedTimer) |
14 |
speedTimer = nil |
15 |
helicopter:removeEventListener('collision', onCollision) |
16 |
end |
17 |
end |
33. Crear un bloque
Como su nombre indica, la función createBlock
crea un bloque y lo dibuja en la pantalla. El objeto resultante se añade al motor de física para comprobar si hay colisiones.
1 |
function createBlock() |
2 |
local b |
3 |
local rnd = math.floor(math.random() * 4) + 1 |
4 |
b = display.newImage('block.png', display.contentWidth, yPos[math.floor(math.random() * 3)+1]) |
5 |
b.name = 'block' |
6 |
-- Block physics |
7 |
physics.addBody(b, 'kinematic') |
8 |
b.isSensor = true |
9 |
blocks:insert(b) |
10 |
end |
34. Función de desplazamiento
En la función movePlayer
, actualizamos la variable up
. Mientras su valor sea igual a true
, la función de update
mueve el helicóptero hacia arriba.
1 |
function movePlayer(e) |
2 |
if(e.phase == 'began') then |
3 |
up = true |
4 |
end |
5 |
if(e.phase == 'ended') then |
6 |
up = false |
7 |
impulse = -60 |
8 |
end |
9 |
end |
35. Aumentar la velocidad
Para hacer el juego más interesante, un temporizador aumenta la velocidad cada cinco segundos. Se muestra un icono para avisar al jugador del cambio de velocidad.
1 |
function increaseSpeed() |
2 |
speed = speed + 2 |
3 |
-- Icon |
4 |
local icon = display.newImage('speed.png', 204, 124) |
5 |
transition.from(icon, {time = 200, alpha = 0.1, onComplete = function() timer.performWithDelay(500, function() transition.to(icon, {time = 200, alpha = 0.1, onComplete = function() display.remove(icon) icon = nil end}) end) end}) |
6 |
end |
36. Mover el helicóptero
La función update
comprueba el valor de up
y mueve el helicóptero hacia arriba si es igual a true
.
1 |
function update(e) |
2 |
-- Move helicopter up |
3 |
if(up) then |
4 |
impulse = impulse - 3 |
5 |
helicopter:setLinearVelocity(0, impulse) |
6 |
end |
37. Mover bloques
A continuación, es el momento de mover los bloques. Utilizamos la variable speed
para determinar cuántos píxeles deben moverse los bloques en cada fotograma.
1 |
-- Move Blocks |
2 |
if(blocks ~= nil)then |
3 |
for i = 1, blocks.numChildren do |
4 |
blocks[i].x = blocks[i].x - speed |
5 |
end |
6 |
end |
38. Actualización de la puntuación
El algoritmo para actualizar la puntuación es sencillo. La puntuación se incrementa en uno cada fotograma mientras el helicóptero no se haya estrellado. Es sencillo. ¿No es así?
1 |
-- Score |
2 |
scoreTF.text = tostring(tonumber(scoreTF.text) + 1) |
3 |
end |
39. Colisiones
La función onCollision
verifica si el helicóptero ha colisionado con un bloque. Si se detecta una colisión, reproducimos un efecto de sonido, una explosión, eliminamos los gráficos y mostramos una alerta al jugador.
1 |
function onCollision(e) |
2 |
audio.play(explo) |
3 |
display.remove(helicopter) |
4 |
alert() |
5 |
end |
40. Alerta
La función alert
crea una vista de alerta y la muestra al usuario. A continuación, el juego termina.
1 |
function alert() |
2 |
gameListeners('rmv') |
3 |
alert = display.newImage('alert.png', (display.contentWidth * 0.5) - 105, (display.contentHeight * 0.5) - 55) |
4 |
transition.from(alert, {time = 300, xScale = 0.5, yScale = 0.5}) |
5 |
end |
41. Invocar la función Main
Para iniciar el juego, es necesario invocar la función Main
. Con el resto del código en su lugar, lo hacemos aquí.
1 |
Main() |
42. Pantalla de carga

En la plataforma iOS, el archivo llamado Default.png
se muestra mientras se lanza la aplicación. Añade esta imagen a la carpeta de fuentes de tu proyecto, será añadida automáticamente por el compilador de Corona.
43. Icono

Utilizando los gráficos que has creado antes, ahora puedes crear un bonito icono. Las dimensiones del icono para un iPhone sin retina son 57px x 57px, mientras que la versión con retina debe ser de 114px x 114px. El material gráfico para iTunes debe ser de 1024px x 1024px. Te sugiero que crees primero la ilustración de iTunes y luego crees las imágenes de menor tamaño reduciendo la ilustración de iTunes a las dimensiones correctas. No es necesario hacer que el icono de la aplicación sea brillante o añadir esquinas redondeadas, ya que el sistema operativo se encarga de ello por ti.
44. Pruebas en el simulador



Es hora de probar nuestra aplicación en el simulador. Abre el Simulador Corona, busca la carpeta de tu proyecto y haz clic en Abrir. Si todo funciona como se espera, estás listo para el paso final.
45. Construir un proyecto



En el Corona Simulator, ve a File > Build y selecciona el dispositivo de destino. Rellena los campos requeridos y haz clic en Build. Espera unos segundos y tu aplicación estará lista para ser probada en un dispositivo y/o para ser enviada para su distribución.
Conclusión
En este tutorial, hemos aprendido sobre las escuchas táctiles, la detección de colisiones, la física, así como algunas otras habilidades que pueden ser útiles en un amplio número de juegos. Experimenta con el resultado final e intenta modificar el juego para crear tu propia versión del mismo. Espero que te haya gustado este tutorial y te haya resultado útil. Gracias por leerlo.