Información actualizada sobre esta entrada

Este artículo pertenece a una serie que consiste en hacer un juego simple en primera persona acerca de encontrar objetos dentro de un laberinto. Es una de mis primeras series de cuando empecé el canal, ahora he mejorado mucho este proyecto y puedes descargar el código fuente para importarlo en tu propio proyecto de Unity. Algún día vamos a hacer un remake de esta serie, suscríbete a mi canal para estar al tanto del nuevo contenido sobre Blender, Unity y programación.

Sígueme en itch.io y descarga el código fuente de este proyecto

PUEDES TESTEAR ESTE JUEGO AQUÍ. TAL VEZ TARDE UN POCO EN CARGAR
🔻

MOVEMENT: WASD CAMERA LOOK: MOUSE

Introducción al artículo original

En este artículo vamos a ver cómo hacer un timer en Unity, el funcionamiento será contar hacia atrás desde los valores iniciales de minutos y segundos que indiquemos y además el Timer se encargará de actualizar la información en la interfaz de usuario creada en el video 5 de la serie del laberinto.

En el artículo en el que creamos la interfaz de usuario, lo que hicimos fue crear algunos elementos para el Canvas, que permitirán al usuario comenzar a jugar y ver información sobre el juego.

Ir a la página principal del proyecto

Vídeo relacionado a este artículo – Cómo hacer un timer para cuenta regresiva en Unity


Descargar Scripts de esta parte del proyecto

Descripción del objetivo

El prototipo es un juego en primera persona, en el cual el escenario es un laberinto. Dentro del laberinto habrá un objeto escondido que se debe encontrar antes de que se acabe el tiempo.

Ahora vamos a resolver la parte en la que el tiempo se acaba. Crearemos un Script que se encargará de llevar la cuenta del tiempo que transcurre.

El Timer comenzará la cuenta atrás desde un valor de minutos y segundos que indicaremos en el inspector, esto quiere decir que cada segundo de reloj debemos actualizar la información.

El Script Timer además se encargará de modificar el valor del elemento texto del Canvas (parte superior en figura 1).

Cuando la cuenta del tiempo llegue a cero, el Script Timer ejecutará el método EndGame del Script GameControl, de esta forma el juego terminará y regresaremos al menú principal.

Fig. 1: Se busca controlar el texto de la parte superior para que muestre el tiempo restante.

Resolución

Comenzamos creando el Script Timer y lo abrimos en el editor. Vamos a asegurarnos de incluir el namespace UnityEngine.UI en la parte superior del Script (figura 3). De esta forma podremos acceder a los objetos y métodos para implementar una interfaz de usuario.

Fig. 2: Se crea un nuevo Script C# de nombre Timer.

Fig. 3: Nos aseguramos de importar UnityEngine.UI para acceder a los métodos de la interfaz de usuario.

Campos y variables

Para resolver la funcionalidad del Timer en c# necesitamos contar con una serie de variables.

En la figura 4 vemos que se utiliza [SerializeField], esto simplemente hará que podamos ver y editar estos campos en el inspector.

Los dos enteros serializados (minutes y seconds) son los valores iniciales en los que empezará la cuenta regresiva.

Mientras que los dos enteros privados (m y s) los usaremos para llevar la cuenta del tiempo.

Fig. 4: Definimos algunos campos para que el timer pueda cumplir su función.

Además necesitamos un objeto tipo Text para guardar la referencia de la componente Text del Canvas que tenemos que modificar.

Seleccionamos el objeto Control de la jerarquía y le asignamos el Script Timer. Podemos observar los campos serializados del Script Timer, minutes, seconds y timerText.

Fig. 5: Asignamos el Script Timer al GameObject Control de la jerarquía.

Fig. 6: Tomamos el GameObject Timer y lo llevamos al campo TimerText de la figura anterior.

Vamos a escribir un minuto y cinco segundos para el inicio de la cuenta regresiva. Luego tomamos el GameObject Timer de la jerarquía (que contiene una componente Text, figura 6) y lo arrastramos al campo timerText.

Fig. 7: Escribimos valores para inicializar las variables.

En la figura 7 observamos que en el campo timerText se ha reconocido una componente tipo Text.

Métodos a implementar

Teniendo en mente cómo funciona un Timer, surgen algunas ideas de posibles métodos. Primero para que el Timer comience a funcionar ejecutaremos un método llamado StartTimer. Luego otro método que se ejecute en intervalos de a un segundo que es el que se va a encargar de los cálculos, lo llamaremos UpdateTimer. Un tercer método para detener el Timer, que se ejecutará cuando este termine de contar, este será StopTimer.

Fig. 8: Eliminamos el método Update y creamos nuevos métodos para resolver el problema.

Por último un método que se encargue de modificar lo que se está mostrando en la interfaz de usuario, este método se llamará WriteTimer y tomará como parámetros dos enteros para los minutos y segundos que debe escribir en la interfaz gráfica.

Si no estás muy familiarizado con qué es un método en programación en mi canal hay un video que podría ser útil, también escribí un artículo para la sección de aprender a programar.

Fig. 9: Agregamos también un método para escribir el Timer.

Noten que los parámetros que requiere este método tienen exactamente el mismo nombre que los dos enteros definidos previamente, que se encargan de llevar la cuenta del Timer.

Esto está hecho así a propósito para hablar un poco sobre contextos, probablemente para más adelante en otro artículo.

Instrucciones para los métodos

Método UpdateTimer

Hemos propuesto algunos métodos de manera intuitiva considerando a grandes rasgos el funcionamiento del Timer, ahora debemos rellenar esos métodos con instrucciones C# que son las que harán el trabajo.

El método UpdateTimer se encargará de restar un segundo, chequear si el tiempo no se agotó, y modificar los minutos apropiadamente. Finalmente utilizará el método WriteTimer pasándole los valores de minutos y segundos. En la figura 10 vemos un ejemplo de cómo implementar esto.

Fig. 10: Instrucciones correspondientes al método UpdateTimer.

En la primera instrucción del método disminuimos en una unidad la variable segundos, luego chequeamos si esta variable es negativa, es decir si previamente valía 0 y ahora vale -1.

Si esto pasa nos fijamos si la variable de los minutos vale 0, si es verdadero esto significa que el tiempo del Timer se agotó (dejo el comentario //endGame en esa región). Si los minutos no son iguales a cero, disminuimos en una unidad la variable minutos y hacemos que la variable segundos valga 59.

Finalmente ejecutamos el método WriteTimer pasando las variables m y s como parámetros.

Método WriteTimer

Este método se va a encargar de modificar el objecto Text que habíamos definido. Dependiendo de si la variable tiene un solo dígito (es decir es menor a 10) o tiene dos dígitos (mayor o igual a 10), vamos a ejecutar las instrucciones que se ven en la figura 11.

Lo que se hace es concatenar una serie de strings o cadenas de texto, en el caso de que s sea menor a 10, concatenamos un 0 delante de los segundos para que conserve el formato de dos números.

Hay otras formas de hacer esto como establecer un formato para las cadenas de texto, pero de esta forma aprovechamos para ver cómo fácilmente podemos concatenar texto usando el operador suma.

Fig. 11: Instrucciones correspondientes al método WriteTimer.

Método StartTimer

Este método realizará la inicialización del Timer, es decir tomamos las variables de minutos y segundo que aparecían en el inspector con los valores iniciales y las asignamos a las variables m y s que llevan la cuenta.

Se ejecuta el método para escribir los valores en la interfaz gráfica.

Finalmente utilizamos el método Invoke, dando como parámetros el string «updateTimer», el cual es el nombre del método y el segundo parámetro es 1f que representa un segundo. Esto hará que el método updateTimer se ejecute luego de un segundo.

Fig. 12: Instrucciones correspondientes al método StartTimer. Invocación de updateTimer.

Vamos a agregar esta misma instrucción al final del método updateTimer, de esta forma el método se va a auto invocar cada un segundo. Esto también se puede hacer utilizando el método InvokeRepeating en lugar de Invoke.

Fig. 13: El método updateTimer se invoca a sí mismo cada un segundo.

Para ver más sobre el método Invoke te invito a leer el artículo de la serie fundamental de Unity en el que hacemos que una máquina expendedora nos entregue una lata luego de un tiempo, también puedes ver el video aquí.

Interacciones entre Scripts

El Timer es como un aparato que cumple su función, podemos configurarlo, ponerlo a correr y detenerlo. Teniendo esto en mente vamos a hacer que el Script GameControl sea el que controle el Timer, pudiendo iniciarlo y detenerlo utilizando sus métodos públicos.

Primero vamos a definir un objeto tipo Timer para usar en el Script GameControl.

Luego en el método Start encontramos la referencia de Timer que está asignado al mismo GameObject Control de la jerarquía (jerarquía en figura 6).

Fig. 14: Agregamos un objeto tipo Timer a los campos del Script GameControl. Encontramos referencia en método Start.

En la figura 14 vemos subrayadas, la declaración de este objeto Timer y cómo encontramos la referencia en el método Start.

Ahora el Script GameControl tiene la referencia de la instancia del Script Timer, esto significa que podrá acceder a sus métodos y campos públicos usando el operador punto.

En el método StartGame de GameControl vamos a hacer que el Timer comience a trabajar.

Fig. 15: En el método startGame ejecutamos el método startTimer de Timer.

Paralelamente, también queremos que el Timer tenga alguna manera de avisarle al Script GameControl que se acabó el tiempo, por lo tanto también necesitamos la referencia del objeto GameControl dentro del Script Timer.

En la figura 16 vemos la declaración de este objeto y cómo se encuentra la referencia dentro del método Start.

Fig. 16: Agregamos un objeto tipo GameControl en Timer y encontramos la referencia en Start.

Por ahora vamos a hacer que cuando se acabe el tiempo el juego termine y regresemos al menú principal, es decir lo mismo que ocurriría si apretamos la tecla Escape. Esto podemos lograrlo ejecutando el método EndGame de GameControl.

En videos anteriores lo habíamos declarado con visibilidad privada, lo que implica este método no puede ser ejecutado desde un contexto externo, por ejemplo el Script Timer.

Para que podamos ejecutarlo desde el Script Timer cambiamos a visibilidad pública. Ver figuras 17 y 18.

Fig. 17: Observamos que la visibilidad del método EndGame es privada, por eso no podemos ejecutarlo desde Timer.

Fig. 18: Cambiamos la visibilidad a público para que sea accesible desde otro contexto.

Ahora si, en la región de UpdateTimer en la que detectamos que la cuenta llega a cero (habíamos dejado el comentario //endGame), ejecutamos el método EndGame del Script GameControl y luego la sentencia return para terminar la ejecución del método UpdateTimer.

Fig. 19: Identificamos la región del código en la que se ha agotado el tiempo y ejecutamos el método EndGame.

En el método EndGame del Script GameControl vamos a detener el Timer, como se observa en la figura 20.

¿Por qué no hicimos que el método UpdateTimer produzca la detención del Timer cuando la cuenta llega a cero?

Se pudo haber hecho de muchas formas distintas, en su momento opté por hacerlo así teniendo en mente que el Timer solo cumple la función de llevar la cuenta del tiempo y avisar cuando el tiempo se acaba, luego el Script GameControl que utiliza el Timer decide qué hacer con él.

Fig. 20: En el método EndGame ejecutamos la acción para detener el Timer.

Método StopTimer

En este método sólo vamos a cancelar todas las invocaciones que puedan estar pendientes, también podríamos resetear los valores del temporizador a los valores iniciales.

Como observación, en el código de la figura 19 si el Timer llega a cero, se ejecuta el método EndGame y posteriormente return, quiere decir que no hay ninguna invocación de updateTimer pendiente y en ese caso la instrucción CancelInvoke() no sería necesaria.

Pero podría ser que ejecutemos el método EndGame debido a otras razones, por ejemplo apretar la tecla Escape o el jugador gana la partida, en ese caso si es necesario cancelar las invocaciones de UpdateTimer.

Fig. 21: En el método StopTimer cancelamos las invocaciones que puedan estar pendientes.

Pruebas finales

Voy a asegurarme de que funcione como se espera, primero voy a colocar como valores iniciales 0 minutos 5 segundos y entro en el modo juego.

Fig. 22: Configuro los valores iniciales para observar rápidamente los resultados.

Al entrar en el modo juego el Timer comienza con 5 segundos, hace la cuenta regresiva y al llegar a 0 vemos en la figura 24 que vuelve al menú principal.

Fig. 23: Entramos en el modo juego para probar el funcionamiento de la cuenta regresiva.

Fig. 24: Al agotarse el tiempo, el resultado es el mismo que apretar la tecla Escape, se ejecuta el método EndGame y volvemos al menú principal.

Ahora verificamos que se respete el formato de dos números para los segundos y que se decrementen correctamente los minutos cuando los segundos pasan de cero a 59. Iniciamos el Timer con 10 minutos 5 segundos y entramos en el modo juego.

Observamos que se respeta el formato de dos dígitos para los segundos y además los minutos se decrementan correctamente. El ejercicio está terminado.

Fig. 25: Probamos nuevamente con el timer en 10 minutos 5 segundos.
Fig. 26: Se respeta el formato de la cuenta regresiva.

Conclusión

Hemos visto cómo crear un temporizador simple en Unity, que cumple la función de llevar la cuenta del tiempo, mostrarla en la interfaz de usuario y cuando el tiempo se agota informar al Script GameControl para que decida qué hacer.

Este temporizador se pudo haber implementado de varias formas distintas, en este caso usamos el método Invoke porque me pareció una alternativa bastante simple.

Vimos como podemos modificar fácilmente una componente texto del Canvas siempre que contemos con su referencia, por lo tanto podemos utilizar esto para hacer otras cosas como mostrar el puntaje, mensajes de información, etc.

En este ejercicio están implícitos los conceptos de clase y objeto de programación, el Script Timer es una clase que contiene campos y métodos para resolver un problema en particular, cuenta con la referencia a otros objetos como GameControl para poder enviarle mensajes cuando lo necesite.

Salir de la versión móvil
Secured By miniOrange