¿Te has encontrado con esos escenarios de juego donde aparecen enemigos cada cierto tiempo y seguirán apareciendo hasta que derrotas al Jefe final o completas una tarea especifica? Este tipo de acción automática hace que como jugador siempre tengas que mantenerte alerta y planear muy bien lo que deseas hacer, además resulta una maravilla cuando balanceas muy bien la frecuencia de enemigos en pantalla e incluso puedes hacer que la dificultad se incremente con solo reducir el tiempo de creación de enemigos / objetos.
Afortunadamente es bastante sencillo emular este tipo de acción usando ejecuciones de funciones controladas por tiempo determinado y en Corona existe una librería que nos permite lograrlo fácilmente: la librería timer. Esta librería tiene 4 funciones especificas para controlar esta operación:
[su_table]
timer.performWithDelay( delay, listener [, iterations] ) : Crea un nuevo timer que se encargará de ejecutar una función cada cierto tiempo una cantidad de veces definida.
• delay : El tiempo que espera antes de ejecutar la función. Este dato se mide en milisegundos (1000 = 1 segundo) |
timer.pause( timerID) : Pausa la ejecución de un timer especifico.
• timerID : El Id / referencia del timer creado con la función |
timer.resume( timerID) : Reanuda la ejecución de un timer especifico. • timerID : El Id / referencia del timer creado con la función timer.performWithDelay() |
timer.cancel( timerID): Elimina la ejecución de un timer especifico.
• timerID : El Id / referencia del timer creado con la función |
[/su_table]
Ejemplo de como usar un timer
Para entender como trabajan estas funciones crearemos una función que muestre una imagen en pantalla en una posición aleatoria y con 4 botones controlaremos las diferentes funciones de la librería timer (perform, pause, resume y cancel)
local widget = require ( "widget" ) -- librería que nos permite crear los botones local creaExplosionesTimer -- En esta variable guardaremos la referencia del timer (timerID) function fnt_agregaExplosiones(event) local explosion = display.newImageRect( "sprites/explosion3.png", 120, 120 ) explosion.x = math.random(100, display.actualContentWidth - 160) explosion.y = math.random(100, display.actualContentHeight - 160) end
Como la idea es usar botones necesitamos cargar la libería widget, ademas definimos una variable de nombre creaExplosionesTimer
que es donde guardaremos la referencia del timer que vayamos a crear. Finalmente la función fnt_agregaExplosiones
es la que permite mostrar una imagen en una posición aleatoria dentro de nuestra pantalla.
function fnt_creaTimer() creaExplosionesTimer = timer.performWithDelay( 1000, fnt_agregaExplosiones, -1) end function fnt_pausaTimer() timer.pause( creaExplosionesTimer ) end function fnt_reanudaTimer() timer.resume( creaExplosionesTimer ) end function fnt_cancelaTimer() timer.cancel( creaExplosionesTimer ) end
A continuación definimos cada una de las funciones que serán ejecutadas por nuestros botones. Observa que al crear el timer guardamos la referencia del mismo en la variable creaExplosionesTimer
para poder hacer referencia a ese timer en especifico en las operaciones de pausar, reanudar y cancelar. Si necesitáramos mas de un timer se puede guardar la referencia de cada nuevo timer en una nueva variable y así tener cuantos timers hicieran falta para nuestro juego.
local bg = display.newImageRect( "sprites/bg2.jpg", display.actualContentWidth, display.actualContentHeight ) bg.x = display.contentCenterX bg.y = display.contentCenterY local opcionesBoton = { width = 150, height = 120, fontSize = 50 } opcionesBoton.label = "Perform" opcionesBoton.onRelease = fnt_creaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 300 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Pause" opcionesBoton.onRelease = fnt_pausaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 500 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Resume" opcionesBoton.onRelease = fnt_reanudaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 700 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Cancel" opcionesBoton.onRelease = fnt_cancelaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 900 boton1.y = display.actualContentHeight - 100
Finalmente creamos y colocamos los botones en pantalla, cada uno con un texto especifico y cada uno ejecutando una función diferente. Es importante que observes que todos los botones se construyen a partir del mismo conjunto de propiedades (la tabla opcionesBoton
) y en esta insertamos / modificamos la información de la tabla asociativa para que cada botón tenga un texto diferente y una función diferente (Si tienes duda de porque podemos hacer esto consulta el post LUA 101 – Tablas ).
Ejecutando el ejemplo de timers.
Al realizar la ejecución en order de los botones podemos observar lo siguiente:
- Cuando presionamos el botón Perform el timer es creado y cada segundo podemos ver como aparece una imagen en pantalla.
- Al presionar el botón pause el timer creado se pausa y las imágenes dejarán de crearse en pantalla.
- Al presionar el botón resume las imágenes seguirán creándose en pantalla.
- Al presionar el botón cancel las imágenes dejarán de crearse en pantalla.
Problemas comunes al usar timers.
- Ejecutar timer.resume() después de ejecutar timer.cancel(). Aunque desde el punto de vista de quien ve la pantalla
timer.pause()
ytimer.cancel()
parecen hacer lo mismo (detener la ejecución recurrente de una función)timer.cancel()
elimina por completo un timer creado, por lo que, si quisiéramos volver a ejecutar este timer tendríamos que volver a crearlo contimer.performWithDelay()
. De lo contrario veríamos un error como el siguiente:
[su_note note_color=”#FEFBBC” text_color=”#000000″]WARNING: timer.resume() cannot resume a timerId that is already expired.[/su_note]
- Crear multiples instancias de un mismo timer. En el ejemplo que hemos creado anteriormente vemos como la función
fnt_creaTimer
inicializa y crea el timer pero ¿Qué sucedería si presionamos varias veces el mismo botón? Se crearían multiples instancias que ejecutan la funciónfnt_agregaExplosiones
pero solo la última de estas instancias estaría guardad en la variablecreaExplosionesTimer
. Lo primero que observaríamos es que empezarían a aparecer muchas imágenes cada vez mas rápido y los botones de Pause y Cancel parecería que no sirven. En realidad siguen ejecutándose solo que pausan / cancelan el último timer creado. ¿Cómo podemos evitar esto? Simplemente validando si la variable en donde estamos guardando la referencia de un timer ya tiene un valor asignado, veamos la solución en código:
function fnt_creaTimer() if creaExplosionesTimer == nil then -- Si el timer no ha sido creado proceder a su creación print("creando el timer") creaExplosionesTimer = timer.performWithDelay( 1000, fnt_agregaExplosiones, -1) end end
A continuación encontraras el código completo y corregido del ejemplo que usamos en este post.
local composer = require( "composer" ) local scene = composer.newScene() local widget = require ( "widget" ) local creaExplosionesTimer function fnt_agregaExplosiones(event) print("Cantidad de instancias: "..event.count) local explosion = display.newImageRect( "sprites/explosion3.png", 120, 120 ) explosion.x = math.random(100, display.actualContentWidth - 160) explosion.y = math.random(100, display.actualContentHeight - 160) end function fnt_creaTimer() if creaExplosionesTimer == nil then creaExplosionesTimer = timer.performWithDelay( 1000, fnt_agregaExplosiones, -1) end end function fnt_pausaTimer() timer.pause( creaExplosionesTimer ) end function fnt_reanudaTimer() timer.resume( creaExplosionesTimer ) end function fnt_cancelaTimer() timer.cancel( creaExplosionesTimer ) end --local sceneGroup = self.view local bg = display.newImageRect( "sprites/bg2.jpg", display.actualContentWidth, display.actualContentHeight ) bg.x = display.contentCenterX bg.y = display.contentCenterY --sceneGroup:insert(bg) local opcionesBoton = { width = 150, height = 120, fontSize = 50 } opcionesBoton.label = "Perform" opcionesBoton.onRelease = fnt_creaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 300 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Pause" opcionesBoton.onRelease = fnt_pausaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 500 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Resume" opcionesBoton.onRelease = fnt_reanudaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 700 boton1.y = display.actualContentHeight - 100 opcionesBoton.label = "Cancel" opcionesBoton.onRelease = fnt_cancelaTimer local boton1 = widget.newButton( opcionesBoton ) boton1.x = 900 boton1.y = display.actualContentHeight - 100
Espero hayas disfrutado y aprendido muchas cosas de este artículo y por favor siéntete libre de compartir esta información en tus redes sociales para que otras personas interesadas también puedan conocer algo nuevo que les pueda ayudar en sus proyectos futuros. ¡Hasta la próxima!