Part of these game systems:
beginner Utilities

Timer / Countdown

Versatile timer with countdown, stopwatch, and repeating modes. Formatted time display and UnityEvent callbacks.

Unity 2022.3+ · 2.1 KB · Timer.cs

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

Timer.cs
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;
    }
}