
유니티 타이머는 Update 에서 매 프레임 Time.deltaTime 을 빼거나 더하면 만들어집니다. 빼면 카운트다운, 더하면 스톱워치이고, 더하지 않고 건너뛰면 일시정지입니다.
time_current -= Time.deltaTime; // 카운트다운(스톱워치는 += Time.deltaTime)
퍼즐 게임의 남은 시간, 레이싱 게임의 랩 기록, 보스전의 제한 시간처럼 타이머는 거의 모든 게임에 들어갑니다. 그리고 만들다 보면 꼭 멈춤 버튼과 다시 하기 버튼이 필요해집니다. 이 블로그의 예전 타이머 글 Time.deltaTime 타이머 · Time.time 타이머 · 시스템 시간 타이머 는 정해진 시간까지 세어 올라가는 예제였습니다. 이번 글에서는 스크립트 하나로 카운트다운과 스톱워치를 고르고, 일시정지 · 다시 시작 · 처음으로를 버튼에 연결해 보겠습니다. 게임 전체를 멈추는 Time.timeScale 과 타이머가 어떻게 얽히는지도 함께 정리합니다.
C# (UNITY 3D)

▲ 아래 GameTimer 의 스톱워치 셈법을 그대로 옮긴 웹 데모입니다(Unity 화면 아님). 왼쪽은 Time.deltaTime, 오른쪽은 Time.unscaledDeltaTime 으로 셉니다.
Time.timeScale 을 0.5 로 내리면 왼쪽 타이머만 절반 속도로 가고, 0 으로 내리면 왼쪽만 멈춥니다. 오른쪽은 timeScale 과 상관없이 실제 시간대로 흐릅니다. 게임 안의 제한 시간은 왼쪽처럼, 일시정지 메뉴에서도 흘러야 하는 시간은 오른쪽처럼 셉니다.
1. 준비 — 글자 하나와 빈 오브젝트
Hierarchy 에서 UI > Text - TextMeshPro 로 시간을 보여 줄 글자를 만들고, 빈 오브젝트에 아래 GameTimer 스크립트를 붙입니다. 스크립트의 text_timer 칸에 그 글자를 끌어다 넣으면 됩니다. 이 글은 예전 글의 Text 대신 TextMeshPro(TMP_Text)를 씁니다. 프로젝트에서 TextMeshPro 를 처음 만들면 「Import TMP Essentials」 창이 뜨는데, 눌러서 기본 글꼴을 가져오면 됩니다.
using UnityEngine;
using UnityEngine.Events;
using TMPro;
카운트다운이 끝났을 때 할 일을 Inspector 에서 고르려고 UnityEngine.Events 를, 글자에 TextMeshPro 를 쓰려고 TMPro 를 사용합니다.
2. 카운트다운 · 스톱워치 타이머 코드
using UnityEngine;
using UnityEngine.Events;
using TMPro;
//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
public class GameTimer : MonoBehaviour
{
public enum TimerMode { Countdown, Stopwatch }
public TMP_Text text_timer; // 시간을 보여 줄 글자(TextMeshPro)
public TimerMode timer_mode = TimerMode.Countdown;
public float time_limit = 60f; // 카운트다운 시작 값(초)
public bool use_unscaled_time = false; // 켜면 Time.timeScale 이 0 이어도 흐름
public bool start_on_play = true;
public UnityEvent on_finished = new UnityEvent(); // 카운트다운이 0 이 되면 한 번
float time_current;
bool is_running;
void Start()
{
Reset_Timer();
if (start_on_play)
Start_Timer();
}
void Update()
{
if (!is_running)
return;
float delta = use_unscaled_time ? Time.unscaledDeltaTime : Time.deltaTime;
if (timer_mode == TimerMode.Countdown)
{
time_current -= delta;
if (time_current <= 0f)
{
time_current = 0f;
is_running = false;
on_finished.Invoke();
}
}
else
{
time_current += delta;
}
Show_Time();
}
public void Start_Timer()
{
if (timer_mode == TimerMode.Countdown && time_current <= 0f)
return; // 이미 끝난 카운트다운은 Reset_Timer 뒤에 다시 시작
is_running = true;
}
public void Pause_Timer()
{
is_running = false;
}
public void Toggle_Pause()
{
if (is_running)
Pause_Timer();
else
Start_Timer();
}
public void Reset_Timer()
{
is_running = false;
time_current = timer_mode == TimerMode.Countdown ? time_limit : 0f;
Show_Time();
}
void Show_Time()
{
if (text_timer == null)
return;
// 카운트다운은 올림(0.3초 남으면 00:01), 스톱워치는 내림 — 화면에 00:00 이 끝보다 먼저 보이지 않게
int seconds = timer_mode == TimerMode.Countdown ? Mathf.CeilToInt(time_current) : Mathf.FloorToInt(time_current);
text_timer.text = string.Format("{0:00}:{1:00}", seconds / 60, seconds % 60);
}
}
timer_mode 를 Countdown 으로 두면 time_limit(기본 60초)에서 줄어들고, Stopwatch 로 두면 0 에서 늘어납니다. 시간은 00:59 처럼 분:초로 보입니다. 카운트다운이 0 이 되면 타이머가 멈추고 on_finished 에 넣어 둔 일이 한 번 실행됩니다.
이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어가고, Start · Update 가 예외 없이 도는 것을 확인했습니다.

변수(variable)
text_timer(TMP_Text) : 시간을 보여 줄 글자
timer_mode(TimerMode) : Countdown(줄어듦) · Stopwatch(늘어남)
time_limit(float) : 카운트다운 시작 값(초)
use_unscaled_time(bool) : 켜면 Time.timeScale 과 상관없이 실제 시간으로 셈
start_on_play(bool) : Play 하자마자 시작할지
on_finished(UnityEvent) : 카운트다운이 끝나면 부를 일
time_current(float) : 지금 시간(초)
is_running(bool) : 흐르는 중인지
함수(function)
Start_Timer : 타이머를 시작하거나 이어서 흐르게 합니다.
Pause_Timer : 타이머를 멈춥니다.
Toggle_Pause : 멈춤 · 이어서 하기를 번갈아 합니다.
Reset_Timer : 처음 값으로 되돌리고 멈춥니다.
Show_Time : 시간을 분:초 글자로 바꿔 보여 줍니다.
3. 일시정지 · 다시 하기 버튼 연결
버튼 두 개를 만들고 각 버튼 Inspector 의 On Click() 에서 + 를 눌러 GameTimer 가 붙은 오브젝트를 넣습니다. 멈춤 · 이어서 버튼에는 GameTimer.Toggle_Pause(멈춘 상태에서 누르면 다시 흐릅니다. 이미 끝난 카운트다운은 다시 하기로), 다시 하기 버튼에는 GameTimer.Reset_Timer 를 고릅니다. 다시 하기 뒤 바로 흐르게 하려면 같은 버튼의 On Click() 에서 Reset_Timer 아래에 Start_Timer 를 하나 더 넣습니다. 순서가 반대면 시작한 뒤 다시 멈춥니다. 카운트다운이 끝났을 때 결과 창을 띄우거나 소리를 내려면 on_finished 칸에서 같은 방법으로 함수를 고르면 됩니다.
4. 타이머만 멈추기 vs 게임 전체 멈추기 (Time.timeScale)
Pause_Timer 는 이 타이머만 멈춥니다. 게임 전체를 멈출 때는 보통 Time.timeScale = 0f 를 씁니다. Unity 문서에 따르면 timeScale 이 0 이면 Time.deltaTime 도 0 이 되므로, deltaTime 으로 세는 타이머는 따로 멈추지 않아도 같이 멈춥니다. timeScale 이 0.5 면 게임 안 시간이 실제의 절반 속도로 흐르니 타이머도 절반 속도가 됩니다.

일시정지 메뉴에 「남은 대기 시간」 처럼 멈춘 동안에도 흘러야 하는 시간이 있다면 use_unscaled_time 을 켭니다. 그러면 Time.unscaledDeltaTime 으로 세어 timeScale 의 영향을 받지 않습니다. 반대로 게임 안 제한 시간에 이것을 켜면 일시정지 중에도 시간이 줄어드니 주의합니다.
Time.timeScale = 0f; // 게임 전체 멈춤 — deltaTime 타이머도 멈춤, unscaled 타이머는 계속
Time.timeScale = 1f; // 다시 원래 속도
5. 타이머가 이상할 때 — 자주 하는 실수
일시정지 메뉴에서 버튼 애니메이션 · 타이머가 안 움직임
timeScale 을 0 으로 둔 동안에는 deltaTime 이 0 이라 deltaTime 으로 움직이는 것은 모두 멈춥니다. 메뉴 쪽 타이머는 use_unscaled_time 을 켜고, 애니메이터라면 Update Mode 를 Unscaled Time 으로 바꿉니다. Unity 문서는 timeScale 이 0 이면 FixedUpdate 와 WaitForSeconds 로 기다리는 코루틴도 불리지 않는다고 적고 있으니, 메뉴에서 코루틴으로 기다릴 때는 WaitForSecondsRealtime 을 씁니다.
00:00 이 보였는데 아직 안 끝남(또는 1초 늦게 끝남)
남은 0.3초를 내림하면 00:00 이 먼저 보입니다. 이 예제처럼 카운트다운은 올림(Mathf.CeilToInt)으로 보여 주면 00:01 다음에 끝나는 순간 00:00 이 됩니다. 스톱워치는 지난 시간이니 내림이 맞습니다.
on_finished 가 여러 번 불림
0 이 된 뒤에도 계속 빼면 끝나는 조건이 매 프레임 참이 됩니다. 이 예제는 0 이 되면 is_running 을 false 로 두고, Start_Timer 에서도 끝난 카운트다운은 시작하지 않게 막았습니다.
오래 켜 두면 기록이 실제 시간과 조금 어긋남
deltaTime 을 float 로 계속 더하면 작은 오차가 쌓일 수 있고, 프레임이 크게 끊기면 deltaTime 이 Time.maximumDeltaTime 에서 잘려 그만큼 덜 셉니다. 실제 시간과 맞아야 하는 기록이라면 시작 시각(시스템 시간이나 Time.realtimeSinceStartup)을 저장해 두고 빼는 방식이 낫습니다. 예전 글 시스템 시간 타이머 가 그 방식입니다. Time.time 타이머 는 더해서 생기는 오차는 없지만 timeScale 을 따르는 게임 시간입니다.
Time 동작은 Unity 공식 Scripting API 문서 Time.deltaTime · Time.unscaledDeltaTime · Time.timeScale 를 기준으로 적었습니다.
'Unity3D > C#' 카테고리의 다른 글
| 유니티 마우스 커서 이미지 바꾸기 (Cursor.SetCursor, 커스텀 커서 hotspot) (0) | 2026.10.18 |
|---|---|
| 유니티 Image.fillAmount 로 쿨타임 · 로딩 바 만들기 (Filled, fillMethod, fillOrigin) (0) | 2026.10.17 |
| 유니티 랜덤 Random.Range 정수 · 실수 차이와 자주 하는 실수 (int, float, InitState) (0) | 2026.10.16 |
| 유니티 안드로이드 화면 최대 밝기, 화면 꺼짐 방지 (screenBrightness / Screen.sleepTimeout) (0) | 2026.10.15 |
| 유니티 떨어지는 이미지, 화면 벗어나면 자동 파괴 (0) | 2020.11.22 |