namespace Zinnia.Utility { using System; using UnityEngine; using UnityEngine.Events; using Zinnia.Extension; /// /// Counts down from a given start time until zero and emits appropriate events throughout the process. /// public class CountdownTimer : MonoBehaviour { /// /// Defines the event with the specified . /// [Serializable] public class FloatUnityEvent : UnityEvent { } #region Timer Settings [Header("Timer Settings")] [Tooltip("The time to start the countdown at.")] [SerializeField] private float startTime = 1f; /// /// The time to start the countdown at. /// public float StartTime { get { return startTime; } set { startTime = value; if (this.IsMemberChangeAllowed()) { OnAfterStartTimeChange(); } } } [Tooltip("Whether to start the countdown timer when the component becomes enabled.")] [SerializeField] private bool beginOnEnable; /// /// Whether to start the countdown timer when the component becomes enabled. /// public bool BeginOnEnable { get { return beginOnEnable; } set { beginOnEnable = value; } } #endregion #region Timer Events /// /// Emitted when the countdown starts. /// [Header("Timer Events")] public UnityEvent Started = new UnityEvent(); /// /// Emitted when the countdown is canceled. /// public UnityEvent Cancelled = new UnityEvent(); /// /// Emitted when the countdown is paused. /// public UnityEvent Paused = new UnityEvent(); /// /// Emitted when the countdown is resumed. /// public UnityEvent Resumed = new UnityEvent(); /// /// Emitted when the countdown completes. /// public UnityEvent Completed = new UnityEvent(); /// /// Emitted when the status of the countdown is checked and is still running. /// public UnityEvent StillRunning = new UnityEvent(); /// /// Emitted when the status of the countdown is checked and is not running. /// public UnityEvent NotRunning = new UnityEvent(); /// /// Emitted when the elapsed time is checked. /// public FloatUnityEvent ElapsedTimeEmitted = new FloatUnityEvent(); /// /// Emitted when the remaining time is checked. /// public FloatUnityEvent RemainingTimeEmitted = new FloatUnityEvent(); #endregion /// /// Determines if the countdown is still running. /// public bool IsRunning { get; protected set; } /// /// Determines if the countdown is currently paused. /// public bool IsPaused { get; protected set; } /// /// Elapsed time of the timer. /// public float ElapsedTime { get { if (IsRunning && !IsPaused) { currentTime = Time.time; } return currentTime - beginTime; } } /// /// Remaining time of the timer. /// public float RemainingTime { get { if (IsRunning && !IsPaused) { currentTime = Time.time; } return StartTime + (beginTime - currentTime); } } /// /// when is called. /// protected float beginTime; /// /// of the current frame. /// protected float currentTime; /// /// The at the point of calling . /// protected float remainingAtPauseTime; /// /// Starts the timer counting down. /// public virtual void Begin() { if (!this.IsValidState()) { return; } IsRunning = true; StartTimer(StartTime); Started?.Invoke(); } /// /// Cancels the timer counting down. /// public virtual void Cancel() { CancelInvoke(nameof(Complete)); if (IsRunning) { currentTime = Time.time; Cancelled?.Invoke(); IsRunning = false; IsPaused = false; remainingAtPauseTime = 0f; } } /// /// Pauses the current countdown timer. /// public virtual void Pause() { if (!IsRunning || IsPaused) { return; } remainingAtPauseTime = RemainingTime; IsPaused = true; CancelInvoke(nameof(Complete)); Paused?.Invoke(); } /// /// resumes the current countdown timer from pause. /// public virtual void Resume() { if (!IsRunning || !IsPaused) { return; } StartTimer(remainingAtPauseTime); IsPaused = false; Resumed?.Invoke(); } /// /// Emits the current running status of the timer. /// public virtual void EmitStatus() { if (!this.IsValidState()) { return; } if (IsRunning) { StillRunning?.Invoke(); } else { NotRunning?.Invoke(); } } /// /// Emits the elapsed time of the timer. /// public virtual void EmitElapsedTime() { if (!this.IsValidState()) { return; } ElapsedTimeEmitted?.Invoke(ElapsedTime); } /// /// Emits the remaining time of the timer. /// public virtual void EmitRemainingTime() { if (!this.IsValidState()) { return; } RemainingTimeEmitted?.Invoke(RemainingTime); } protected virtual void OnEnable() { SetInternalStates(); if (BeginOnEnable) { Begin(); } } protected virtual void OnDisable() { Cancel(); } /// /// Starts the countdown timer. /// /// The time to delay until the invoke is executed. protected virtual void StartTimer(float invokeTime) { SetInternalStates(); Invoke(nameof(Complete), invokeTime); } /// /// Executed when the countdown is complete. /// protected virtual void Complete() { currentTime = StartTime + beginTime; IsRunning = false; Completed?.Invoke(); } /// /// Stores current for calculations. /// protected virtual void SetInternalStates() { beginTime = Time.time; currentTime = Time.time; } /// /// Called after has been changed. /// protected virtual void OnAfterStartTimeChange() { SetInternalStates(); } } }