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();
}
}
}