내일배움캠프/Unity Final Projcet

[Unity]일일 및 주간 리셋 타이머 구현

danpat77 2025. 5. 12. 21:10

전체적인 동작 흐름

우선 전체 흐름을 간단히 살펴보면 다음과 같습니다.

 

→ 최초로 타이머 계산
→ 코루틴 시작
→ 매 초마다 리셋 시간이 되었는지 확인
→ 리셋 시간이 되면 이벤트 호출 및 다음 리셋 스케줄링
→ 남은 시간을 UI에 표시


ScheduleNextDailyReset()

private void ScheduleNextDailyReset()
{
    DateTime now = DateTime.Now;
    var todayNine = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);
    nextDailyResetTime = now < todayNine ? todayNine : todayNine.AddDays(1);
}

 

  • 역할:
    • 매일 정해진 시각(여기서는 오전 0시)을 기준으로 다음 리셋 시각을 설정합니다.
  • 상세 설명:
    • 현재 시간이 오전 0시 전이라면 당일 0시를 다음 리셋 시간으로 설정합니다.
    • 오전 0시가 지났다면 다음날 0시를 리셋 시간으로 설정합니다.

ScheduleNextWeeklyReset()

private void ScheduleNextWeeklyReset()
{
    DateTime now = DateTime.Now;
    int daysUntilMonday = ((int)DayOfWeek.Monday - (int)now.DayOfWeek + 7) % 7;
    var targetDay = now.Date.AddDays(daysUntilMonday).AddHours(9);
    nextWeeklyResetTime = now < targetDay ? targetDay : targetDay.AddDays(7);
}

 

 

  • 역할:
    • 매주 월요일 오전 9시를 기준으로 다음 리셋 시각을 계산하여 설정합니다.
  • 상세 설명:
    • 현재 요일과 시간을 기준으로 다음에 오는 월요일 오전 9시를 구합니다.
    • 현재 시각이 이미 이번 주 월요일 9시를 넘었다면, 다음 주 월요일 오전 9시로 설정합니다.

CountdownCoroutine()

private IEnumerator CountdownCoroutine()
{
    while (true)
    {
        DateTime now = DateTime.Now;

        // 일일 리셋 체크
        if (now >= nextDailyResetTime)
        {
            OnDailyReset?.Invoke();
            ScheduleNextDailyReset();
        }

        // 주간 리셋 체크
        if (now >= nextWeeklyResetTime)
        {
            OnWeeklyReset?.Invoke();
            ScheduleNextWeeklyReset();
        }

        // 남은 시간 계산 및 UI 업데이트
        TimeSpan remainDaily = nextDailyResetTime - now;
        TimeSpan remainWeekly = nextWeeklyResetTime - now;
        TimeUI(remainDaily, remainWeekly);

        yield return new WaitForSeconds(1f);
    }
}

 

  • 역할:
    • 1초마다 리셋 시각이 지났는지 체크합니다.
    • 리셋 시각이 되었다면, 연결된 이벤트를 호출하고 다음 리셋을 설정합니다.
    • UI 갱신을 위해 남은 시간을 계속해서 계산합니다.
  • 상세 설명:
    코루틴을 통해 지속적으로 동작하며, 매초마다 현재 시간을 점검하여 조건이 충족되면 즉시 이벤트가 호출됩니다.

TimeUI(TimeSpan, TimeSpan)

private void TimeUI(TimeSpan remainDaily, TimeSpan remainWeekly)
{
    // 일일 타이머 표시 (HH시MM분SS초)
    string dailyStr = string.Format("{0:D2}시{1:D2}분{2:D2}초",
        (int)remainDaily.TotalHours, remainDaily.Minutes, remainDaily.Seconds);

    // 주간 타이머 표시 (D일 HH시MM분 또는 HH시MM분SS초)
    string weeklyStr;
    if (remainWeekly.Days > 0)
    {
        weeklyStr = string.Format("{0}일 {1:D2}시{2:D2}분",
            remainWeekly.Days, remainWeekly.Hours, remainWeekly.Minutes);
    }
    else
    {
        weeklyStr = string.Format("{0:D2}시{1:D2}분{2:D2}초",
            (int)remainWeekly.TotalHours, remainWeekly.Minutes, remainWeekly.Seconds);
    }

    UIManager.Instance.questUIHandler.SetUITimer(dailyStr, weeklyStr);
}

 

 

  • 역할:
    • 화면(UI)에 표시할 남은 시간을 형식에 맞게 계산하여 UI로 넘겨줍니다.
  • 상세 설명:
    • 일일 타이머는 항상 시/분/초로 표현합니다.
    • 주간 타이머는 1일 이상 남았으면 D일 HH시MM분 형태로 표시하고, 하루 미만이면 시간까지 초 단위로 자세하게 표시합니다.

이벤트 연결

private void OnEnable()
{
    TimerManager.OnDailyReset += ResetDailyQuest;
    TimerManager.OnWeeklyReset += ResetWeeklyQuest;
}

private void OnDisable()
{
    TimerManager.OnDailyReset -= ResetDailyQuest;
    TimerManager.OnWeeklyReset -= ResetWeeklyQuest;
}

활용법:
위처럼 다른 스크립트에서 TimerManager의 이벤트를 연결하여 사용하면 됩니다.


마무리

이번에 Unity에서 일일 및 주간 리셋 타이머 기능을 직접 만들어 보면서, 간단한 타이머 구현이라 생각했는데도 생각보다 세세한 부분에서 신경 쓸 게 많다는 걸 깨달았습니다. 특히나 날짜 계산이나 주간 기준일을 잡는 부분에서는 처음엔 약간 헷갈렸지만, 하나하나 해결해 나가면서 DateTime과 Coroutine에 대해 더 깊이 이해할 수 있었던 것 같습니다.

무엇보다도 이벤트로 처리해 놓으니 나중에 퀘스트나 다른 시스템과의 연결이 편리해진다는 점이 가장 마음에 듭니다.