전체적인 동작 흐름
우선 전체 흐름을 간단히 살펴보면 다음과 같습니다.
→ 최초로 타이머 계산
→ 코루틴 시작
→ 매 초마다 리셋 시간이 되었는지 확인
→ 리셋 시간이 되면 이벤트 호출 및 다음 리셋 스케줄링
→ 남은 시간을 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에 대해 더 깊이 이해할 수 있었던 것 같습니다.
무엇보다도 이벤트로 처리해 놓으니 나중에 퀘스트나 다른 시스템과의 연결이 편리해진다는 점이 가장 마음에 듭니다.
'내일배움캠프 > Unity Final Projcet' 카테고리의 다른 글
[Unity] Coroutine과 DOTween 차이 (0) | 2025.05.14 |
---|---|
[Unity] 상점 UI 구현 및 리팩토링: 데일리샵과 리소스 상점 통합 구성 (0) | 2025.05.13 |
[Unity] 퀘스트 시스템 구현 - 일일/주간/업적 관리와 UI 처리 구조 설계 (0) | 2025.05.09 |
[Unity] 강화카드 중복 제한 로직 구현 및 딕셔너리 기반 구조 개선하기 (0) | 2025.05.08 |
[Unity] 모바일 UI가 노치·카메라 홀에 가려질 때 레터박스 처리하기 (트러블 슈팅) (0) | 2025.05.07 |