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