#region License /* Copyright (c) 2010-2014 Danko Kozar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion License using System; using eDriven.Core.Events; using eDriven.Core.Managers; using UnityEngine; using Event=eDriven.Core.Events.Event; using MulticastDelegate=eDriven.Core.Events.MulticastDelegate; namespace eDriven.Core.Util { /// /// Timer class /// /// Coded by Danko Kozar public class Timer : EventDispatcher { #if DEBUG public new static bool DebugMode; #endif #region Events // ReSharper disable InconsistentNaming public const string START = "start"; public const string PAUSE = "pause"; public const string STOP = "stop"; public const string RESET = "reset"; public const string TICK = "tick"; public const string COMPLETE = "complete"; // ReSharper restore InconsistentNaming private MulticastDelegate _startHandler; /// /// The handler which fires when the timer is started /// public MulticastDelegate StartHandler { get { if (null == _startHandler) _startHandler = new MulticastDelegate(this, START); return _startHandler; } set { _startHandler = value; } } private MulticastDelegate _stopHandler; /// /// The handler which fires when the timer is stopped /// public MulticastDelegate StopHandler { get { if (null == _stopHandler) _stopHandler = new MulticastDelegate(this, STOP); return _stopHandler; } set { _stopHandler = value; } } private MulticastDelegate _resetHandler; /// /// The handler which fires when the timer has been reset /// public MulticastDelegate ResetHandler { get { if (null == _resetHandler) _resetHandler = new MulticastDelegate(this, RESET); return _resetHandler; } set { _resetHandler = value; } } private MulticastDelegate _tickHandler; /// /// The handler which fires on each timer tick /// public MulticastDelegate Tick { get { if (null == _tickHandler) _tickHandler = new MulticastDelegate(this, TICK); return _tickHandler; } set { _tickHandler = value; } } private MulticastDelegate _completeHandler; /// /// The handler which fires when the timer is complete (on the last tick) /// public MulticastDelegate Complete { get { if (null == _completeHandler) _completeHandler = new MulticastDelegate(this, COMPLETE); return _completeHandler; } set { _completeHandler = value; } } #endregion #region Properties /// /// Should the timer tick on start, or after the first delay /// public bool TickOnStart; private float _delay = 1f; /// /// Delay time in seconds /// (seconds because Unity deals with seconds rather than milliseconds) /// public float Delay { get { return _delay; } set { if (value != _delay) { if (_delay <= 0) throw new TimerException(TimerException.DelayError); _delay = value; // if messing with delay, we should reset the timer!!! :) // if not, strange effects happen (in calculating tick in the OnUpdate method, line "if (_time > _delay * _count) // step ") Reset(); } } } private float _repeatCount; /// /// Tick in seconds /// public float RepeatCount { get { return _repeatCount; } set { if (value != _repeatCount) { if (_repeatCount < 0) throw new TimerException(TimerException.DelayError); _repeatCount = value; } CheckRepeatCount(); } } private DateTime _lastTickTime; /// /// The time the timer had the last tick /// public DateTime LastTickTime { get { return _lastTickTime; } } /// /// The flag indicating if the timer is running /// public bool IsRunning { get; private set; } #endregion #region Members private float _time; private int _count; #endregion #region Constructor /// /// Constructor /// public Timer() { } /// /// Constructor /// /// public Timer(float delay) : this() { _delay = delay; } /// /// Constructor /// /// /// public Timer(float delay, int repeatCount) : this(delay) { _repeatCount = repeatCount; } #endregion #region IDisposable /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public override void Dispose() { base.Dispose(); SystemManager.Instance.UpdateSignal.Disconnect(UpdateSlot); } #endregion #region Methods /// /// Starts the timer /// public void Start() { Start(false); } /// /// Starts the timer /// /// Should the time be reset public void Start(bool reset) { if (!IsRunning) SystemManager.Instance.UpdateSignal.Connect(UpdateSlot); //, 0, false); // Subscribe to update signal IsRunning = true; if (reset) Reset(); // dispatch start event if (HasEventListener(START)) DispatchEvent(new Event(START)); // if tick on start, dispatch tick event if (TickOnStart && !_paused) DoTick(); _paused = false; } private bool _paused; /// /// Stops the timer /// public void Pause() { // Unsubscribe from update signal SystemManager.Instance.UpdateSignal.Disconnect(UpdateSlot); IsRunning = false; _paused = true; // dispatch stop event if (HasEventListener(PAUSE)) DispatchEvent(new Event(PAUSE)); } /// /// Stops the timer /// public void Stop() { // Unsubscribe from update signal SystemManager.Instance.UpdateSignal.Disconnect(UpdateSlot); IsRunning = false; Reset(); // dispatch stop event if (HasEventListener(STOP)) DispatchEvent(new Event(STOP)); } /// /// Resets time and the tick count /// public void Reset() { //Active = false; /** * Unsubscribe from SystemManager Update * */ _time = 0; _count = 0; if (HasEventListener(RESET)) DispatchEvent(new Event(RESET)); } /// /// Resets time, but not the tick count /// public void Defer() { _time = 0; } #endregion #region Private methods private void DoTick() { _lastTickTime = DateTime.Now; if (HasEventListener(TICK)) DispatchEvent(new Event(TICK)); } private void CheckRepeatCount() { if (_repeatCount > 0 && _count >= _repeatCount){ Stop(); if (HasEventListener(COMPLETE)) DispatchEvent(new Event(COMPLETE)); } } #endregion #region Implementation of ISlot /// /// The slot which is being executed on each update /// /// public void UpdateSlot(params object[] parameters) { _time += Time.deltaTime; // the logic: maintain delay * count greater than time if (_time > _delay * (_count + 1)) // step { _count++; #if DEBUG if (DebugMode) Debug.Log("Timer tick."); #endif DoTick(); CheckRepeatCount(); } } #endregion } }