Part of these game systems:
Timer / Countdown
Versatile timer with countdown, stopwatch, and repeating modes. Formatted time display and UnityEvent callbacks.
How to Use
1
Attach Timer to any GameObject
2
Choose mode: Countdown (default), Stopwatch, or Repeating
3
Set duration in inspector (in seconds)
4
Connect OnTimerComplete event to trigger actions
5
Display: timer.FormattedTime gives 'MM:SS' string
6
Control: StartTimer(), Pause(), Resume(), Stop()
Source Code
C#
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Versatile timer supporting countdown, stopwatch, and repeating modes.
/// Provides formatted time display and event callbacks.
/// </summary>
public class Timer : MonoBehaviour
{
public enum TimerMode { Countdown, Stopwatch, Repeating }
[Header("Settings")]
[SerializeField] private TimerMode mode = TimerMode.Countdown;
[SerializeField] private float duration = 60f;
[SerializeField] private bool startOnAwake = false;
[SerializeField] private bool useUnscaledTime = false;
[Header("Events")]
public UnityEvent OnTimerStart;
public UnityEvent OnTimerComplete;
public UnityEvent<float> OnTimerTick; // normalized 0-1
private float currentTime;
private bool isRunning;
/// <summary>Current time in seconds.</summary>
public float CurrentTime => currentTime;
/// <summary>Is the timer currently running?</summary>
public bool IsRunning => isRunning;
/// <summary>Normalized progress (0 to 1).</summary>
public float Progress => mode == TimerMode.Stopwatch
? (duration > 0f ? Mathf.Clamp01(currentTime / duration) : 0f)
: (duration > 0f ? Mathf.Clamp01(1f - currentTime / duration) : 0f);
/// <summary>Formatted time string (MM:SS).</summary>
public string FormattedTime
{
get
{
float t = mode == TimerMode.Stopwatch ? currentTime : Mathf.Max(0f, currentTime);
int minutes = Mathf.FloorToInt(t / 60f);
int seconds = Mathf.FloorToInt(t % 60f);
return string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
private void Start()
{
if (startOnAwake)
StartTimer();
}
private void Update()
{
if (!isRunning) return;
float dt = useUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime;
switch (mode)
{
case TimerMode.Countdown:
currentTime -= dt;
OnTimerTick?.Invoke(Progress);
if (currentTime <= 0f)
{
currentTime = 0f;
isRunning = false;
OnTimerComplete?.Invoke();
}
break;
case TimerMode.Stopwatch:
currentTime += dt;
OnTimerTick?.Invoke(Progress);
break;
case TimerMode.Repeating:
currentTime -= dt;
OnTimerTick?.Invoke(Progress);
if (currentTime <= 0f)
{
OnTimerComplete?.Invoke();
currentTime = duration;
}
break;
}
}
/// <summary>Start or restart the timer.</summary>
public void StartTimer()
{
currentTime = (mode == TimerMode.Stopwatch) ? 0f : duration;
isRunning = true;
OnTimerStart?.Invoke();
}
/// <summary>Pause the timer.</summary>
public void Pause() => isRunning = false;
/// <summary>Resume the timer.</summary>
public void Resume() => isRunning = true;
/// <summary>Stop and reset the timer.</summary>
public void Stop()
{
isRunning = false;
currentTime = (mode == TimerMode.Stopwatch) ? 0f : duration;
}
/// <summary>Set a new duration at runtime.</summary>
public void SetDuration(float newDuration)
{
duration = newDuration;
}
}