Desarrolla un juego 'Word Drop' en Corona SDK
Spanish (Español) translation by Steven (you can also view the original English article)
En este tutorial, te enseñaré cómo crear un juego de palabras usando el SDK Corona. Construiremos el juego de principio a fin, por lo que te invito a seguirlo. En este tutorial, trabajaremos con temporizadores, física e implementaremos nuestros propios controles táctiles. Empecemos.
Introducción
El objetivo del juego es crear palabras con las letras que están dentro de bolas. Si las bolas cruzan la barra en la parte superior, el juego termina. Tienes tres bombas, que puedes usar para eliminar cinco bolas, así que es clave usarlas con moderación. Cada diez segundos, más bolas caen desde la parte superior de la pantalla. Echa un vistazo a la captura de pantalla para tener una idea del juego.

1. Nuevo proyecto
Abre el simulador de Corona y elige Nuevo Proyecto.



En la siguiente pantalla, aplica las siguientes configuraciones.



Haz clic en Siguiente y elige Abrir en Editor. Esto abrirá Main.lua
en el editor de texto que elijas.
2. Configuración del proyecto
Abre Config.lua y reemplaza el contenido del archivo con la configuración que se muestra a continuación. Esto establecerá el ancho, la altura, la escala y los FPS predeterminados del proyecto (cuadros por segundo). La configuración letterBox
significa que la aplicación se escalará en ambas direcciones de la manera más uniforme posible. Si es necesario, sin embargo, el juego será buzón.
application = { content = { width = 320, height = 480, scale = "letterBox", fps = 30, } }
3. Ocultar la barra de estado
Para evitar que la barra de estado se muestre en la parte superior de la pantalla, agrega el siguiente fragmento de código a Main.lua
.
display.setStatusBar(display.HiddenStatusBar)
4. Variables locales
En el siguiente fragmento de código, puedes ver una lista de las variables que usaremos en este juego. Lee los comentarios para tener una idea de la responsabilidad de cada variable.
local gameBackground = display.newImage("background.png",true) local theWord = "" -- the word the user spells local theWordText -- shows the word local isNewGame = true local allBallsGroup -- group to hold all the balls local wordTable = {} -- will hold the words from the text file local chosenBalls = {} -- holds the balls the user has chosen local instructionsText -- shows instructions local countDownText -- shows the time local numBallsToGenerate = 10 -- how many balls to generate local allBalls = {} -- holds all the balls that have been generated local theBombs = {} -- holds references to the bomb image local generateBallTimer -- timer for generating balls local clockTimer -- timer for the clock local gameTime = 10 -- how many seconds before new balls drop
5. Configuración de la física
Con el siguiente fragmento de código, requerimos y arrancamos el motor de física. Establecemos la gravedad alta para asegurarnos de que las bolas caigan rápido.
local physics = require("physics") physics.start(true) physics.setGravity(0,50)
6. setup
En setup
, sembramos el generador aleatorio para asegurarnos de que math.random
genera un número aleatorio cuando lo necesitamos.
function setup() math.randomseed(os.time()) end
Llama a setup
inmediatamente después de su declaración.
setup()
7. setupGameAssets
En los siguientes pasos, se mostrarán algunas funciones, que implementaremos un poco más adelante en este tutorial.
En setupGameAssets
, los recursos del juego están configurados.
function setupGameAssets() end
8. newGame
La función newGame
es responsable de comenzar un nuevo juego.
function newGame() end
9. setupGameBoundaries
En setupGameBoundaries
, se establecen los límites de las bolas a la izquierda, derecha e inferior de las pantallas.
function setupGameBoundaries() end
10. setupButtons
La función setupButtons
configura los botones.
function setupButtons() end
11. setupTextFields
En setupTextFields
, los campos de texto están configurados.
function setupTextFields() end
12. setupBombs
Esta función configura las imágenes para las bombas.
function setupBombs() end
13. setBombsVisible
Al invocar a setBombsVisible
, las bombas se establecen para ser visibles.
function setBombsVisible() end
14. setBombsInvisible
Esta función no necesita explicación. ¿Verdad?
function setBombsInvisible() end
15. generateBalls
La función generateBalls
es responsable de generar nuevas bolas a intervalos de tiempo regulares.
function generateBalls() end
16. startTimer
startTimer
inicia el temporizador.
function startTimer() end
17. doCountDown
En doCountDown
, el tiempo de juego se decrementa.
function doCountdown() end
18. createBall
La función createBall
es responsable de crear una bola. Se necesita un argumento, la letra que contiene la pelota.
function createBall(createVowel) end
19. checkGameOver
En checkGameOver
, verificamos si el juego ha terminado, es decir, si la pared de bolas ha cruzado la barra en la parte superior de la pantalla.
function checkGameOver() end
20. checkWord
En checkWord
, verificamos si la palabra escrita por el usuario es válida.
function checkWord() end
21. resetWord
La función resetWord
se invoca cuando el jugador cancela un envío.
function resetWord() end
22. createVowelBalls
La función createVowelBalls
asegura que algunas de las bolas contengan una vocal. Se necesita un parámetro, el número de bolas que debe contener una vocal.
function createVowelBalls(number) end
23. formString
Esta función crea una cadena a partir de las letras en las bolas que el usuario ha elegido.
function formString(e) end
24. explode
La función explode
es invocada cuando el jugador usa una bomba. Quita cinco bolas del juego.
function explode(e) end
25. removeBall
La función removeBall
elige una bola aleatoria para eliminar del juego.
function removeBall() end
26. readTextFile
La función readTextFile
se usa para leer un archivo de texto y almacenar las palabras que contiene en una tabla para usar en el juego.
function readTextFile() end
27. Progress Check
Vuelve a comprobar que has creado implementaciones de código auxiliar para las funciones anteriores antes de continuar. En los siguientes pasos, implementaremos cada función.
28. Implementando readTextFile
En readTextFile
, leemos el archivo de texto del directorio de recursos y almacenamos cada palabra en wordTable
. Hacemos uso de string.sub
para recortar espacios en blanco desde el final de cada palabra.
local path = system.pathForFile( "wordlist.txt", system.ResourceDirectory) local file = io.open( path, "r" ) for line in file:lines() do line = string.sub(line, 1, #line - 1) table.insert(wordTable,line) end io.close( file ) file = nil
readTextFile
se invoca en setupGameAssets
como se muestra a continuación. Actualizaremos setupGameAssets
un poco más más adelante en este tutorial.
function setupGameAssets() readTextFile() end
29. Implementando setupGameBoundaries
En setupGameBoundaries
, los límites del juego están definidos. lineLeft
y lineRight
son los límites derecho e izquierdo, mientras que GroundLine
es la parte inferior de la pantalla, el suelo por así decirlo. Estos son utilizados por el motor de física e impedirán que las bolas se muevan fuera del área de juego. Los configuramos para que no sean visibles porque no necesitamos verlos. La razón por la que hemos usado -29
, es porque el radio de las bolas es 29
y el sistema de física usa el centro de los objetos al probar la colisión.
local groundLine = display.newRect(0, 380,display.contentWidth, 2) local lineLeft = display.newRect(-29,0,2,display.contentHeight) local lineRight = display.newRect(display.contentWidth-29,0,2,display.contentHeight) physics.addBody(groundLine, 'static',{bounce=0,friction=0}) physics.addBody(lineLeft, 'static',{bounce=0,friction=0}) physics.addBody(lineRight,'static',{bounce=0,friction=0}) groundLine.isVisible = false lineLeft.isVisible = false lineRight.isVisible = false
Al igual que readTextFile
, setupGameBoundaries
se invoca en setupGameAssets
.
function setupGameAssets() readTextFile() setupGameBoundaries() end
30. Implementando setupButtons
Implementa setupButtons
como se muestra a continuación e invoca la función en setupGameAssets
.
local goButton = display.newImage("goButton.png",260,420) goButton:addEventListener('tap', checkWord) local stopButton = display.newImage("stopButton.png",5,430) stopButton:addEventListener('tap',resetWord) local bar = display.newImage("bar.png",0,100)
function setupGameAssets() readTextFile() setupGameBoundaries() setupButtons() end
31. Implementando setupTextFields
Implementa setupTextFields
como se muestra a continuación e invoca la función en setupGameAssets
.
countDownText = display.newText(gameTime,290,10,native.systemFontBold,20) countDownText:setTextColor("#000000") theWordText = display.newText("",60,437,native.systemFontBold,25) theWordText:setTextColor("#000000") instructionsText = display.newText("",0,0,native.systemFontBold,25) instructionsText.x = display.contentWidth/2 instructionsText.y = display.contentHeight/2 instructionsText:setTextColor("#000000")
function setupGameAssets() readTextFile() setupGameBoundaries() setupButtons() setupTextFields() end
32. Implementando setupBombs
Implementa setupBombs
como se muestra a continuación e invoca la función en setupGameAssets
. En setupBombs
, generamos tres imágenes de bombas. Al almacenar las imágenes en una tabla, podemos hacer referencia a ellas sin tener que declarar tres variables de imagen separadas.
for i=1, 3 do local tempBomb = display.newImage("bomb.png") tempBomb.width = 30 tempBomb.height = 30 tempBomb.x = 33 * i tempBomb.y = 20 tempBomb:addEventListener('tap', explode) table.insert(theBombs,tempBomb) end
function setupGameAssets() readTextFile() setupGameBoundaries() setupButtons() setupTextFields() setupBombs() end
33. Acabando setupGameAssets
Finaliza la implementación de setupGameAssets
agregando el fragmento de código que se muestra a continuación. Se inicializa el grupo para las bolas.
allBallsGroup = display.newGroup();
34. Acabando setup
Con la función setupGameAssets
lista para usar, podemos invocarla en la función setup
como se muestra a continuación.
function setup() math.randomseed(os.time()) setupGameAssets() end
35. Implementando createBall
Tenemos dos tablas, una para el alfabeto y otra solo para las vocales, ya que queremos asegurarnos de que algunas de las bolas contengan vocales. Luego generamos valores aleatorios para las variables ballType
y ballSize
. El valor de ballType
varía de 1
a 4
, mientras que el valor de ballSize
varía de 1
a 2
. Al usar estas variables, obtenemos un color de bola y establecemos su radio. letterText
utiliza la letra aleatoria que generamos y establece que x
e y
son iguales a la bola. Luego insertamos la letra y la bola en un grupo para que aparezcan como un elemento en el juego. A continuación, generamos una posición x
aleatoria para la bola y la colocamos en -40
para la posición y
. Añadimos física a la pelota para asegurarnos de que caiga desde la parte superior de la pantalla hasta la parte inferior. Asígnale el name
y las teclas letter
, agrega un evento tap e insértalo en la tabla allBalls
, así como en la tabla allBallsGroup
. Este último nos permite trabajar con todas las bolas actualmente en el juego.
local var alphabetArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"} local vowels = {"A","E","I","O","U"} local ballType = math.random(4) local ballSize = math.random(2) local letterIndex local letter if(createVowel == true) then letterIndex = math.random(#vowels) letter = vowels[letterIndex] else letterIndex = math.random(#alphabetArray); letter = alphabetArray[letterIndex] end local ballGroup = display.newGroup(); local ball local ballRadius if(ballType == 1) then ball = display.newImage("greenBall.png") elseif(ballType == 2) then ball = display.newImage("brownBall.png") elseif(ballType == 3) then ball = display.newImage("pinkBall.png") else ball = display.newImage("redBall.png") end if(ballSize == 1)then ball.width = 48 ball.height = 48 ballRadius = 24 else ball.width = 58 ball.height = 58 ballRadius = 29 end local letterText = display.newText( letter, 0,0, native.systemFontBold, 25 ); letterText:setTextColor(0,0, 0) letterText.x = ball.x letterText.y = ball.y ballGroup:insert(ball) ballGroup:insert(letterText) ballGroup.x = math.random(ballRadius,display.contentWidth-ballRadius*2) ballGroup.y= - 40 physics.addBody(ballGroup, 'dynamic',{friction = 0,bounce = 0,radius = ballRadius}) ballGroup.name = "ball" ballGroup.letter = letter ballGroup:addEventListener('tap',formString) table.insert(allBalls,ballGroup) allBallsGroup:insert(ballGroup)
Si invocas esta función en setup
, deberías ver una bola que se está creando y caerá desde la parte superior de la pantalla hacia la parte inferior. No olvides eliminar la llamada desde el método setup
cuando hayas terminado la prueba.
36. Implementando generateBalls
En generateBalls
, invocamos checkGameOver
después de 1500 milisegundos, lo que le da a las bolas el tiempo suficiente para caer más allá de la barra. Si es un juego nuevo, necesitamos generar diez bolas, de lo contrario generamos cuatro bolas, de las cuales al menos una contiene una vocal. Exploraremos la implementación de createVowelBalls
en breve. Si invocas a GenerateBalls
en setup
, deberías ver que se generan diez pelotas.
function generateBalls() timer.performWithDelay(1500,checkGameOver) if(isNewGame == true) then numBallsToGenerate = 10 else numBallsToGenerate = 4 createVowelBalls(1) end generateBallTimer = timer.performWithDelay( 50, createBall, numBallsToGenerate) end
37. Implementando createVowelBalls
Lo único que hace esta función es invocar a createBall
tantas veces como el valor del número que se pasó a la función. Pasamos true
como parámetro, lo que significa que createBall
generará una bola que contiene una vocal.
for i=1, number do createBall(true) end
38. Implementando removeBall
Esta función elige una bola aleatoria de la tabla allBalls
y la elimina. Esta función es invocada por la función explode
, que implementaremos en unos momentos.
local randomIndex = math.random(#allBalls) local tempBall = allBalls[randomIndex] tempBall:removeSelf() tempBall = nil table.remove(allBalls,randomIndex)
39. Implementando setBombsVisible
En setBombsVisible
, recorremos las bombas y las configuramos como visibles.
for i=1, #theBombs do theBombs[i].isVisible = true end
40. Implementando setBombsInvisible
En esta función, hacemos exactamente lo contrario que hicimos en setBombsVisible
.
for i=1, #theBombs do theBombs[i].isVisible = false end
41. Implementando explode
En explode
, comprobamos si allBalls
contiene menos de cinco bolas. Si hay menos de cinco bolas presentes, eliminamos todas las bolas, de lo contrario solo eliminamos cinco bolas.
local thisSprite = e.target thisSprite.isVisible = false local randomIndex local randomBall if(#allBalls < 5) then for i=1, #allBalls do removeBall() end else for i=1, 5 do removeBall() end end
42. Implementando formString
En formString
, formamos una palabra cada vez que el usuario hace clic en una bola. Recuerda que cada bola tiene una tecla letter
añadida. Verificamos si la tabla chosenBalls
no contiene la bola que seleccionaron. Si no es así, insertamos la bola en la tabla chosenBalls
, pegamos la letra al final de la variable theWord
y la mostramos en el campo de texto. Si la bola ya fue elegida y agregada a chosenBalls
, no la agregamos a chosenBalls
e imprimimos un mensaje en la consola. Ya puedes probar nuestro juego tocando algunas bolas y viendo aparecer la palabra en el campo de texto.
local thisSprite = e.target local theLetter = thisSprite.letter if(table.indexOf(chosenBalls,thisSprite) == nil) then table.insert(chosenBalls,thisSprite) theWord = theWord .. theLetter theWordText.text = theWord theWordText.x = display.contentWidth/2 else print("already chose that ball") end
43. Implementando resetWord
instructionsText.text = ""; theWord = '' theWordText.text = "" chosenBalls = {}
Esta función restablece la palabra actual y borra la tabla chosenBalls
. Si pruebas el juego, puedes hacer clic en el botón Cancelar para borrar el campo de texto.
44. Implementando checkWord
En checkWord
, verificamos si la longitud de theWord
es menor o igual a uno. Si es así, volvemos de la función. Debemos asegurarnos de que el jugador haya elegido una palabra con un mínimo de dos letras. Luego necesitamos verificar si theWord
coincide con una palabra de la tabla wordTable
. Si no es así, configuramos las instrucciones de texto instructionsText
en NO ES UNA PALABRA y se las mostramos al jugador. Si lo hace, recorremos la tabla de bolas elegidas chosenBalls
y eliminamos cada bola del juego.
if(#theWord <= 1) then return; end local lowerCaseWord = string.lower(theWord) local tempBall if(table.indexOf(wordTable,lowerCaseWord) == nil) then instructionsText.text = "NOT A WORD!" instructionsText:toFront() else for i=1, #chosenBalls do table.remove(allBalls,table.indexOf(allBalls,chosenBalls[i])) chosenBalls[i]:removeSelf() chosenBalls[i] = nil theWord = "" theWordText.text = "" end chosenBalls = {} end
45. Implementando doCountDown
En doCountDown
, tomamos el número del campo de texto, lo disminuimos y comprobamos si es igual a cero. Si es así, llamamos a generateBalls
, lo restablecemos y llamamos a startTimer
, que a su vez invoca a doCountDown.
local currentTime = countDownText.text currentTime = currentTime -1 countDownText.text = currentTime if(currentTime == 0) then generateBalls() countDownText.text = gameTime startTimer() end
46. Implementando startTimer
La implementación de startTimer
es simple. Llamamos a doCountdown
cada segundo y repetimos esto tantas veces como el valor de gameTime
.
clockTimer = timer.performWithDelay(1000,doCountdown,gameTime)
47. Implementando newGame
Para iniciar un nuevo juego, las variables que declaramos anteriormente se restablecen y se invoca startTimer
para iniciar el juego.
isNewGame = true chosenBalls = {} allBalls = {} theWord = "" theWordText.text = "" instructionsText.text = "" countDownText.text = gameTime; createVowelBalls(2) generateBalls() setBombsVisible() startTimer() isNewGame = false
48. Implementando checkGameOver
En esta función, recorremos la tabla allBalls
y verificamos si el valor de y
de alguna de las bolas es mayor que 100. Si es así, una o más bolas cruzan la barra en la parte superior y el juego termina. Si el juego termina, las bolas se eliminan de la tabla ballGroup
. Los temporizadores se cancelan para hacer invisibles las bombas y se invoca newGame
después de tres segundos.
local gameOver = false; for i=1,#allBalls do if(allBalls[i].y < (100 - allBalls[i].height))then gameOver = true break; end end if(gameOver) then for i=allBallsGroup.numChildren,1,-1 do local child = allBallsGroup[i] child:removeSelf() child = nil end timer.cancel(generateBallTimer) timer.cancel(clockTimer) instructionsText.text = "GameOver" instructionsText:toFront() setBombsInvisible() timer.performWithDelay(3000,newGame)
Finalizando el juego
Todo lo que nos queda por hacer es llamar a newGame
o en la función setup
.
function setup() math.randomseed(os.time()) setupGameAssets() newGame() end
Conclusión
En este tutorial, creamos un creativo juego de palabras. Siéntete libre de experimentar con el código que hemos escrito para ver cómo afecta el juego. Espero que hayas encontrado útil este tutorial.