using System; using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Game1 { /// /// An object that invokes a delegate at a specific time interval, optionally repeating until invalidated. /// public class Timer { /// /// Specifies which delegate needs to be called. /// private enum TickEvent { Tick, TickWithData, TickWithTimer, TickWithTimerAndData } // we recycle timers that have expired so that we can create only as few as needed private static readonly List expiredTimers = new List(); private static readonly List activeTimers = new List(); // a temp list is used during the update so that timer delegates can add/remove timers // without breaking the foreach loop private static readonly List tempTimers = new List(); // the delegates and type specifier so we know what to call when the timer ticks over private TickEvent tickEvent; private Action tick; private Action tickWithData; private Action tickWithTimer; private Action tickWithTimerAndData; // optional data to be passed to the delegate private object tickData; // whether or not the timer expired private bool expired; /// /// Gets the interval (in seconds) at which the timer calls its action. /// public float TickLength { get; private set; } /// /// Gets the timer's current elapsed time. /// public float CurrentTime { get; private set; } /// /// Gets a value indicating whether or not the timer will repeat its action. /// public bool Repeats { get; private set; } private Timer() { } /// /// Expires the timer on the next update. /// public void Invalidate() { expired = true; } /// /// Creates a new non-repeating timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction) { return CreateTimer(tickLength, tickAction, false); } /// /// Creates a new timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Whether or not the timer should repeat. /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, bool repeats) { if (tickAction == null) throw new ArgumentNullException("tickAction"); Timer t = GetTimer(tickLength, repeats, null); t.tick = tickAction; t.tickEvent = TickEvent.Tick; return t; } /// /// Creates a new non-repeating timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction) { return CreateTimer(tickLength, tickAction, false); } /// /// Creates a new timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Whether or not the timer should repeat /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, bool repeats) { if (tickAction == null) throw new ArgumentNullException("tickAction"); Timer t = GetTimer(tickLength, repeats, null); t.tickWithTimer = tickAction; t.tickEvent = TickEvent.TickWithTimer; return t; } /// /// Creates a new non-repeating timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Extra data to be passed to the timer's action. /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, object timerData) { return CreateTimer(tickLength, tickAction, timerData, false); } /// /// Creates a new timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Extra data to be passed to the timer's action. /// Whether or not the timer should repeat /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, object timerData, bool repeats) { if (tickAction == null) throw new ArgumentNullException("tickAction"); Timer t = GetTimer(tickLength, repeats, timerData); t.tickWithData = tickAction; t.tickEvent = TickEvent.TickWithData; return t; } /// /// Creates a new non-repeating timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Extra data to be passed to the timer's action. /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, object timerData) { return CreateTimer(tickLength, tickAction, timerData, false); } /// /// Creates a new timer and adds it to the system. /// /// The number of seconds before the timer executes the action. /// The action to execute. /// Extra data to be passed to the timer's action. /// Whether or not the timer should repeat /// The Timer object. public static Timer CreateTimer(float tickLength, Action tickAction, object timerData, bool repeats) { if (tickAction == null) throw new ArgumentNullException("tickAction"); Timer t = GetTimer(tickLength, repeats, timerData); t.tickWithTimerAndData = tickAction; t.tickEvent = TickEvent.TickWithTimerAndData; return t; } /// /// Updates the timer system and all the timers currently active. /// /// The current game timestep. public static void Update(GameTime gameTime) { // add the active timers to the tempTimers list tempTimers.Clear(); tempTimers.AddRange(activeTimers); foreach (var t in tempTimers) { // skip any timer that is flagged as expired already (by the Invalidate method) if (!t.expired) { // increment the timer t.CurrentTime += (float)gameTime.ElapsedGameTime.TotalSeconds; // if the timer has passed its tick length, fire off the proper action if (t.CurrentTime >= t.TickLength) { switch (t.tickEvent) { case TickEvent.Tick: t.tick(); break; case TickEvent.TickWithData: t.tickWithData(t.tickData); break; case TickEvent.TickWithTimer: t.tickWithTimer(t); break; case TickEvent.TickWithTimerAndData: t.tickWithTimerAndData(t, t.tickData); break; } // reduce the CurrentTime in case we want to repeat t.CurrentTime -= t.TickLength; // if the timer doesn't repeat, flag it as expired if (!t.Repeats) t.expired = true; } } // if a timer is expired, move it from the active list to the expired list if (t.expired) { activeTimers.Remove(t); expiredTimers.Add(t); } } } // a private helper for getting or creating a new timer. private static Timer GetTimer(float tickLength, bool repeats, object data) { Timer t = null; // if we have some expired timers, we can recycle one if (expiredTimers.Count > 0) { t = expiredTimers[expiredTimers.Count - 1]; expiredTimers.RemoveAt(expiredTimers.Count - 1); } // if not, create a new timer if (t == null) t = new Timer(); // set up the defaults and given values t.TickLength = tickLength; t.tickData = data; t.Repeats = repeats; t.CurrentTime = 0f; t.tick = null; t.tickWithData = null; t.tickWithTimer = null; t.tickWithTimerAndData = null; t.expired = false; // add the timer to the active list activeTimers.Add(t); // and return it back to the caller return t; } } }