Sistema de Temporizadores

Un registro de temporizadores nombrados que centraliza el ticking por fotograma, permite a cada temporizador elegir entre tiempo escalado o no escalado, y mantiene en un único lugar el tiempo de sesión, la pausa y el timing de cinemáticas.

Para qué sirve este sistema

Todo juego necesita temporizadores. Cooldowns, cuentas atrás, relojes de sesión, retardos de cinemática — todos acaban dispersos en distintos MonoBehaviours, cada uno con su propia lógica de Update y cada uno decidiendo por separado si respetar Time.timeScale o no. Esa contabilidad dispersa se convierte en un problema en el momento en que hay que pausar el juego, saltarse una cinemática o entregar el tiempo transcurrido a un sistema de analíticas.

El módulo Timer de Serenity reemplaza esa dispersión con un único registro ITimerService. Los sistemas registran temporizadores con nombre, eligen un modo de tiempo y llaman a start, stop o reset. El servicio actualiza todos los temporizadores registrados una vez por fotograma desde un único punto, leyendo el delta a través de un ITimeProvider inyectado para que la dependencia de Unity nunca filtre al dominio.

El problema en Unity

El patrón habitual es copiar la misma lógica de cuenta atrás o tiempo transcurrido en cada sistema que la necesita. Cada copia tiene su propia llamada a Time.deltaTime o Time.unscaledDeltaTime, su propia bandera de ejecución y su propia lógica de reset. Cuando llega un nuevo requisito — la pausa debe congelar los cooldowns pero no el reloj de sesión, o una cinemática necesita progreso en tiempo real — hay que cambiar cada copia. Los bugs que se introducen al olvidar alguna son difíciles de rastrear porque nada une los temporizadores entre sí.

Sin una capa compartida de temporizadores, coordinar el tiempo transcurrido de Game Session, la pausa de Game Mode y una secuencia de cinemática exige o un acoplamiento estrecho entre esos sistemas o otra ronda de contabilidad de tiempo duplicada. Ninguna de las dos opciones escala.

Cómo lo aborda Serenity

Serenity modela el dominio del temporizador a través de ITimerService, ITimer y TimerFactory. ITimerService es el registro: crea temporizadores con AddTimer, los recupera por nombre con GetTimer, los elimina con RemoveTimer y los actualiza todos con una única llamada a UpdateTimers. ITimer representa un temporizador individual: expone SetTimer, StartTimer, StopTimer, GetTimeInSeconds y el punto de entrada por fotograma Tick. TimerFormatterService convierte segundos brutos en cadenas formateadas de horas, minutos y segundos para mostrar en pantalla.

El escalado de tiempo se controla por temporizador a través del enum TimeMode. Los temporizadores TimeMode.Scaled avanzan con Time.deltaTime y se pausan automáticamente cuando timeScale llega a cero. Los temporizadores TimeMode.Unscaled avanzan con el delta en tiempo real y continúan ejecutándose durante la pausa. ITimeProvider abstrae ese delta del dominio para que la capa de Unity se pueda reemplazar o simular en pruebas.

Cómo encaja en Serenity

El módulo Timer vive en el namespace Serenity.Timer y sigue la estructura por capas de la foundation. La capa de Dominio contiene el enum TimeMode y TimerFormatterService. La capa de Aplicación define ITimer, ITimerService, TimerFactory e ITimerServiceFactory. La capa de Infraestructura proporciona UnityTimer, UnityTimerService, UnityTimerFactory y UnityTimeProvider, que conectan las interfaces del dominio con la API de tiempo de Unity. UnityTimerInstaller registra el servicio en la pipeline de inicialización.

Timer coopera con Game Session, que es el propietario del temporizador de tiempo transcurrido de sesión y, por tanto, el consumidor principal de AddTimer en el arranque. Game Mode señaliza la pausa cambiando timeScale, lo que detiene automáticamente todos los temporizadores Scaled sin ninguna lógica de pausa específica de Timer. Los sistemas de secuencia y cinemática pueden registrar temporizadores Unscaled para rastrear el progreso en tiempo real de forma independiente a la velocidad del juego.

Flujo de trabajo práctico

  1. Resuelve ITimerService a través del instalador y llama a AddTimer con un identificador de cadena y un TimeMode para registrar un nuevo temporizador.
  2. Llama a StartTimer sobre el ITimer devuelto para comenzar a avanzarlo en cada fotograma.
  3. Llama a UpdateTimers una vez por fotograma desde el game loop — el servicio lee el delta de ITimeProvider y actualiza todos los temporizadores registrados.
  4. Recupera cualquier temporizador por nombre con GetTimer y lee su valor actual mediante GetTimeInSeconds o los accesores individuales GetHours, GetMinutes y GetSeconds.
  5. Usa TimerFormatterService para formatear segundos brutos en una cadena de visualización para etiquetas de cuenta atrás o tiempo transcurrido en la UI.
  6. Llama a StopTimer para pausar un temporizador concreto, ResetTimer para devolverlo a su duración configurada y RemoveTimer para desregistrarlo cuando el sistema propietario se apaga.

Qué incluye

  • Registro de temporizadores nombrados a través de ITimerService con AddTimer, GetTimer, RemoveTimer y GetAllTimers
  • Selección de modo de tiempo por temporizador mediante TimeMode.Scaled y TimeMode.Unscaled
  • Ticking centralizado por fotograma a través de UpdateTimers — los sistemas individuales no implementan su propia contabilidad de tiempo
  • Abstracción ITimeProvider que mantiene la API de tiempo de Unity fuera de la capa de dominio
  • Interfaz ITimer con SetTimer, StartTimer, StopTimer y GetTimeInSeconds
  • TimerFormatterService para convertir segundos brutos en cadenas de visualización en formato HH:MM:SS
  • TimerFactory e ITimerServiceFactory para crear instancias de temporizador sin acoplamiento a Unity
  • Integración con Game Session para el tiempo transcurrido y con Game Mode para la pausa a través de timeScale

Cuándo usarlo

  • Proyectos que necesitan múltiples temporizadores nombrados — cooldowns, relojes de sesión, cuentas atrás — gestionados desde un único lugar en lugar de dispersos entre MonoBehaviours.
  • Juegos en los que la pausa debe congelar algunos temporizadores pero no otros, lo que requiere un control explícito del tiempo escalado frente al no escalado por temporizador.
  • Bases de código donde las cinemáticas o secuencias necesitan rastrear el progreso en tiempo real de forma independiente a la velocidad del juego.
  • Equipos que quieren la lógica de temporizadores testeable en C# puro sin depender de la API de tiempo de Unity en la capa de dominio.

Sistemas relacionados

Usa Serenity cuando quieras que cada temporizador del juego esté registrado, nombrado y actualizado desde un único punto, con cada temporizador decidiendo por sí mismo si la pausa significa detenerse — y sin que nada de esa lógica filtre al dominio.

Volver a la página principal