15/07/2022
En el desarrollo de videojuegos, el control del tiempo es fundamental. No todas las acciones ocurren de forma instantánea; a menudo necesitamos introducir pausas, esperas o ejecutar código después de un cierto período. Ya sea para mostrar un mensaje durante unos segundos, activar un power-up, gestionar el cooldown de una habilidad o coreografiar una cinemática, saber cómo implementar un retraso de manera eficiente es una habilidad crucial para cualquier desarrollador de Unity. Afortunadamente, el motor nos ofrece múltiples herramientas para lograrlo, cada una con sus propias ventajas y casos de uso ideales. En esta guía completa, exploraremos los métodos más populares y efectivos para manejar el tiempo y los retrasos en tus proyectos de Unity.

El Método Más Versátil: Las Corrutinas
Cuando hablamos de retrasos y secuencias de eventos en Unity, las Corrutinas (Coroutines) son, sin duda, la herramienta más poderosa y flexible a nuestra disposición. Una corrutina es como una función que tiene la capacidad de pausar su ejecución y devolver el control a Unity, para luego continuar desde donde se quedó en un frame posterior. Esto nos permite escribir código secuencial que se ejecuta a lo largo del tiempo sin bloquear el hilo principal del juego.
WaitForSeconds: La Pausa Estándar
La forma más común de crear un retraso dentro de una corrutina es utilizando yield return new WaitForSeconds(tiempo). Esto pausará la ejecución de la corrutina durante la cantidad de segundos especificada.
Veamos un ejemplo práctico donde un personaje habla, espera unos segundos, y luego dice algo más:
using UnityEngine; using System.Collections; public class DialogoController: MonoBehaviour { public TMPro.TextMeshProUGUI textoDialogo; void Start() { // Iniciamos la corrutina StartCoroutine(SecuenciaDeDialogo()); } IEnumerator SecuenciaDeDialogo() { textoDialogo.text = "¡Bienvenido a la aventura!"; // Pausa la ejecución por 3 segundos yield return new WaitForSeconds(3f); textoDialogo.text = "Tu misión comienza ahora."; // Pausa la ejecución por 4 segundos yield return new WaitForSeconds(4f); textoDialogo.text = ""; // Limpiamos el texto } } WaitForSecondsRealtime: Ignorando la Escala del Tiempo
Una característica importante de WaitForSeconds es que se ve afectado por Time.timeScale. Esto significa que si pausas el juego estableciendo Time.timeScale = 0, la espera de WaitForSeconds también se detendrá. Para situaciones donde necesitas que un retraso continúe incluso si el juego está en pausa (por ejemplo, en un menú de pausa), debes usar yield return new WaitForSecondsRealtime(tiempo).
Esperando Condiciones con WaitUntil y WaitWhile
Las corrutinas no solo sirven para esperar una cantidad fija de tiempo. También pueden esperar hasta que una condición específica se cumpla o deje de cumplirse.
WaitUntil: Pausa la corrutina hasta que la condición que le pasamos se evalúe como verdadera.WaitWhile: Pausa la corrutina mientras la condición que le pasamos se evalúe como verdadera.
Ejemplo de WaitUntil, esperando que el jugador alcance una puntuación:
IEnumerator EsperarPuntuacionParaNivel() { Debug.Log("Esperando que el jugador alcance 1000 puntos..."); // La corrutina se detendrá en esta línea hasta que la puntuación sea 1000 o más. yield return new WaitUntil(() => GameManager.instance.puntuacion >= 1000); Debug.Log("¡Puntuación alcanzada! Cargando siguiente nivel..."); SceneManager.LoadScene("Nivel_02"); } La Solución Rápida y Sencilla: Invoke
Si lo único que necesitas es llamar a una función después de un retraso simple y no requieres la complejidad de una corrutina, Invoke es tu mejor aliado. Es una función de MonoBehaviour que ejecuta un método por su nombre (como una cadena de texto) después de un tiempo determinado.
public class Enemigo: MonoBehaviour { void Start() { Debug.Log("Un enemigo ha aparecido. Explotará en 5 segundos."); // Llama a la función "Explotar" después de 5 segundos. Invoke("Explotar", 5f); } void Explotar() { Debug.Log("¡BOOM!"); Destroy(gameObject); } } Principales desventajas de Invoke:
- No puede pasar parámetros: La función que llamas no puede recibir argumentos.
- Uso de strings: Si cambias el nombre de la función
Explotar, elInvokedejará de funcionar y Unity no te avisará del error hasta que se ejecute. Puedes mitigar esto usandonameof(Explotar)para mayor seguridad.
Ejecución Repetitiva con InvokeRepeating
Similar a Invoke, InvokeRepeating llama a una función después de un retraso inicial, pero luego continúa llamándola a intervalos regulares.

void Start() { // Llama a "GenerarMoneda" en 2 segundos, y luego cada 1 segundo. InvokeRepeating("GenerarMoneda", 2f, 1f); } void GenerarMoneda() { // Lógica para instanciar una moneda en una posición aleatoria. } Para detener todas las llamadas de Invoke en un script, puedes usar CancelInvoke().
Control Total: El Temporizador Manual con Update
A veces, necesitas un control más granular sobre tu temporizador. Quizás necesites mostrar el tiempo restante en la pantalla, o que el retraso pueda ser interrumpido o modificado dinámicamente. En estos casos, implementar un temporizador manual usando la función Update y Time.deltaTime es la mejor opción.
Time.deltaTime representa el tiempo en segundos que tardó en completarse el último frame. Al sumarlo en cada frame, podemos medir con precisión el paso del tiempo.
Temporizador de un solo uso
public class Bomba: MonoBehaviour { public float tiempoParaExplotar = 3f; private float temporizador = 0f; private bool cuentaRegresivaIniciada = true; void Update() { if (cuentaRegresivaIniciada) { temporizador += Time.deltaTime; // Actualizar UI aquí: textoTiempo.text = (tiempoParaExplotar - temporizador).ToString("F1"); if (temporizador >= tiempoParaExplotar) { Explotar(); cuentaRegresivaIniciada = false; // Detenemos el temporizador } } } void Explotar() { // Lógica de explosión Destroy(gameObject); } } Este método es ideal para mostrar el tiempo restante en la interfaz de usuario, ya que tienes acceso constante a la variable temporizador.
El Enfoque Moderno: Async/Await
Desde versiones más recientes, Unity ha mejorado su soporte para la programación asíncrona de C# con async y await. Esta metodología puede ser una alternativa a las corrutinas, ofreciendo un código que a menudo es más limpio y fácil de leer, especialmente en secuencias complejas.
Para crear un retraso, puedes usar await Task.Delay(). El tiempo se especifica en milisegundos.

using System.Threading.Tasks; public class GestorAsync: MonoBehaviour { async void Start() { Debug.Log("Iniciando secuencia asíncrona."); await RetrasoAsync(); Debug.Log("La secuencia ha terminado."); } async Task RetrasoAsync() { Debug.Log("Esperando 2 segundos..."); // Espera 2000 milisegundos (2 segundos) await Task.Delay(2000); Debug.Log("Espera finalizada."); } } Aunque es muy potente, para retrasos simples puede ser excesivo. Su verdadero poder brilla cuando se combina con otras operaciones asíncronas, como cargar datos de un servidor web o procesar archivos sin congelar el juego.
Tabla Comparativa de Métodos
Para ayudarte a decidir qué método usar, aquí tienes una tabla que resume las características de cada uno:
| Método | Mejor para... | Ventajas | Desventajas |
|---|---|---|---|
| Corrutinas | Secuencias complejas, pausas condicionales y control de flujo a lo largo del tiempo. | Muy flexibles, pueden pasar parámetros, código legible y secuencial. | Ligeramente más complejas de iniciar (StartCoroutine). Generan una pequeña cantidad de basura en memoria. |
| Invoke / InvokeRepeating | Llamadas a funciones simples y rápidas, con o sin repetición. | Muy fácil de usar, sintaxis simple. | No puede pasar parámetros, depende de strings (propenso a errores), menos flexible. |
Temporizador en Update | Cuando necesitas mostrar el tiempo restante, o tener control total sobre el estado del temporizador (pausar, reanudar, reiniciar). | Control total, ideal para UI, no genera basura en memoria. | Requiere más código manual (variables de estado), la lógica se ejecuta en cada frame. |
| Async / Await | Operaciones asíncronas complejas, integración con tareas que no son de Unity. | Sintaxis moderna y limpia, excelente para manejar múltiples tareas asíncronas. | Puede ser excesivo para retrasos simples, requiere un entendimiento de la programación asíncrona. |
Preguntas Frecuentes (FAQ)
¿Cuál es la diferencia principal entre `WaitForSeconds` y `WaitForSecondsRealtime`?
WaitForSeconds se ve afectado por la escala de tiempo del juego (Time.timeScale). Si pausas el juego (Time.timeScale = 0), la espera se detiene. WaitForSecondsRealtime ignora la escala de tiempo y continuará la cuenta atrás incluso si el juego está pausado, lo cual es útil para animaciones de UI en menús de pausa.
¿Cuándo debería usar `Invoke` en lugar de una Corrutina?
Usa Invoke para tareas de "disparar y olvidar" que no requieran complejidad. Si solo necesitas que un enemigo explote 5 segundos después de aparecer, Invoke es perfecto por su simplicidad. Si necesitas una secuencia de eventos (aparece, espera, se mueve, espera, ataca), una corrutina es la opción correcta.
¿Usar `Update()` para un temporizador afecta el rendimiento?
Para una pequeña cantidad de temporizadores, el impacto en el rendimiento es prácticamente nulo. La función Update se ejecuta en cada frame de todas formas. Sin embargo, si tienes cientos o miles de objetos con temporizadores en Update, podría ser más eficiente gestionarlos desde un único sistema centralizado para evitar la sobrecarga de tantas llamadas a la función.
¿Puedo pasarle parámetros a una función con `Invoke`?
No, esta es una de sus mayores limitaciones. La función llamada por Invoke no puede tener parámetros. Si necesitas pasar datos, debes usar una corrutina o un temporizador manual que acceda a variables de la clase.
Conclusión
Dominar las técnicas para crear retrasos y gestionar el tiempo es un paso esencial para pasar de ser un desarrollador principiante a uno intermedio en Unity. No existe una única "mejor manera", sino la herramienta adecuada para cada trabajo. Las Corrutinas ofrecen la máxima flexibilidad para secuencias complejas. Invoke es ideal para retrasos simples y rápidos. El temporizador manual en Update te da un control total, especialmente para la interfaz de usuario. Y Async/Await presenta un paradigma moderno y potente para tareas asíncronas. Experimenta con cada uno de estos métodos y elige el que mejor se adapte a las necesidades específicas de tu juego. ¡Ahora tienes el poder de controlar el tiempo!
Si quieres conocer otros artículos parecidos a Controla el Tiempo en Unity: Guía de Retrasos puedes visitar la categoría Juegos.
