웨이브 소환용 글로벌 타이머 생성
This commit is contained in:
269
Assets/Scripts/GlobalTimer.cs
Normal file
269
Assets/Scripts/GlobalTimer.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 전역 타이머 - 주기적으로 반복되며 모든 클라이언트에 동기화
|
||||
/// </summary>
|
||||
public class GlobalTimer : NetworkBehaviour
|
||||
{
|
||||
public static GlobalTimer Instance { get; private set; }
|
||||
|
||||
[Header("Timer Settings")]
|
||||
public float cycleLength = 60f; // 한 주기 길이 (초)
|
||||
public bool autoStart = true;
|
||||
public bool pauseOnZero = false; // 0에 도달하면 일시정지
|
||||
|
||||
[Header("Debug")]
|
||||
public bool showDebugLogs = false;
|
||||
|
||||
// 현재 타이머 값 (초)
|
||||
private NetworkVariable<float> _currentTime = new NetworkVariable<float>(
|
||||
0f,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 타이머 실행 중 여부
|
||||
private NetworkVariable<bool> _isRunning = new NetworkVariable<bool>(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 현재 사이클 번호
|
||||
private NetworkVariable<int> _cycleCount = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 이벤트
|
||||
public event Action<float> OnTimerTick; // 매 프레임 업데이트
|
||||
public event Action OnCycleComplete; // 사이클 완료
|
||||
public event Action<int> OnCycleStart; // 새 사이클 시작
|
||||
public event Action<float> OnHalfwayPoint; // 사이클 중간 지점
|
||||
|
||||
private bool _hasReachedHalfway;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsServer && autoStart)
|
||||
{
|
||||
StartTimer();
|
||||
}
|
||||
|
||||
// 클라이언트도 이벤트 수신을 위해 변경 감지
|
||||
_currentTime.OnValueChanged += OnCurrentTimeChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_currentTime.OnValueChanged -= OnCurrentTimeChanged;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer || !_isRunning.Value)
|
||||
return;
|
||||
|
||||
_currentTime.Value -= Time.deltaTime;
|
||||
|
||||
// 중간 지점 체크
|
||||
if (!_hasReachedHalfway && _currentTime.Value <= cycleLength / 2f)
|
||||
{
|
||||
_hasReachedHalfway = true;
|
||||
OnHalfwayPoint?.Invoke(cycleLength / 2f);
|
||||
NotifyHalfwayClientRpc();
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=yellow>[GlobalTimer] 사이클 중간 지점 도달</color>");
|
||||
}
|
||||
|
||||
// 사이클 완료
|
||||
if (_currentTime.Value <= 0f)
|
||||
{
|
||||
CompleteCycle();
|
||||
}
|
||||
|
||||
OnTimerTick?.Invoke(_currentTime.Value);
|
||||
}
|
||||
|
||||
private void CompleteCycle()
|
||||
{
|
||||
OnCycleComplete?.Invoke();
|
||||
NotifyCycleCompleteClientRpc();
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=green>[GlobalTimer] 사이클 {_cycleCount.Value} 완료</color>");
|
||||
|
||||
if (pauseOnZero)
|
||||
{
|
||||
_isRunning.Value = false;
|
||||
_currentTime.Value = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 다음 사이클 시작
|
||||
_currentTime.Value = cycleLength;
|
||||
_cycleCount.Value++;
|
||||
_hasReachedHalfway = false;
|
||||
|
||||
OnCycleStart?.Invoke(_cycleCount.Value);
|
||||
NotifyCycleStartClientRpc(_cycleCount.Value);
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=cyan>[GlobalTimer] 사이클 {_cycleCount.Value} 시작</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCurrentTimeChanged(float previousValue, float newValue)
|
||||
{
|
||||
// 클라이언트에서도 Tick 이벤트 발생
|
||||
if (!IsServer)
|
||||
{
|
||||
OnTimerTick?.Invoke(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// 타이머 시작 (서버만)
|
||||
/// </summary>
|
||||
public void StartTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
_currentTime.Value = cycleLength;
|
||||
_isRunning.Value = true;
|
||||
_cycleCount.Value = 1;
|
||||
_hasReachedHalfway = false;
|
||||
|
||||
OnCycleStart?.Invoke(_cycleCount.Value);
|
||||
NotifyCycleStartClientRpc(_cycleCount.Value);
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=green>[GlobalTimer] 타이머 시작: {cycleLength}초</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 타이머 일시정지 (서버만)
|
||||
/// </summary>
|
||||
public void PauseTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
_isRunning.Value = false;
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=yellow>[GlobalTimer] 타이머 일시정지</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 타이머 재개 (서버만)
|
||||
/// </summary>
|
||||
public void ResumeTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
_isRunning.Value = true;
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=green>[GlobalTimer] 타이머 재개</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 타이머 리셋 (서버만)
|
||||
/// </summary>
|
||||
public void ResetTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
_currentTime.Value = cycleLength;
|
||||
_cycleCount.Value = 0;
|
||||
_isRunning.Value = false;
|
||||
_hasReachedHalfway = false;
|
||||
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"<color=yellow>[GlobalTimer] 타이머 리셋</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 남은 시간 (초)
|
||||
/// </summary>
|
||||
public float GetCurrentTime() => _currentTime.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 남은 시간 (분:초 형식)
|
||||
/// </summary>
|
||||
public string GetFormattedTime()
|
||||
{
|
||||
int minutes = Mathf.FloorToInt(_currentTime.Value / 60f);
|
||||
int seconds = Mathf.FloorToInt(_currentTime.Value % 60f);
|
||||
return $"{minutes:00}:{seconds:00}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 진행률 (0.0 ~ 1.0)
|
||||
/// </summary>
|
||||
public float GetProgress() => 1f - (_currentTime.Value / cycleLength);
|
||||
|
||||
/// <summary>
|
||||
/// 현재 사이클 번호
|
||||
/// </summary>
|
||||
public int GetCycleCount() => _cycleCount.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 타이머 실행 중 여부
|
||||
/// </summary>
|
||||
public bool IsRunning() => _isRunning.Value;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Client RPCs
|
||||
|
||||
[ClientRpc]
|
||||
private void NotifyCycleCompleteClientRpc()
|
||||
{
|
||||
if (!IsServer)
|
||||
{
|
||||
OnCycleComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void NotifyCycleStartClientRpc(int cycleNumber)
|
||||
{
|
||||
if (!IsServer)
|
||||
{
|
||||
OnCycleStart?.Invoke(cycleNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void NotifyHalfwayClientRpc()
|
||||
{
|
||||
if (!IsServer)
|
||||
{
|
||||
OnHalfwayPoint?.Invoke(cycleLength / 2f);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/GlobalTimer.cs.meta
Normal file
2
Assets/Scripts/GlobalTimer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b643f7c446ade3f448e57e7501ac5a67
|
||||
Reference in New Issue
Block a user