Smart Timers

Every game needs some kind of timers to control gameplay related events. A timer works kind of like a stop watch in real life. There are different ways to keep track of time, this method some nice advantages that will make your life easier! To start with lets look at this example that uses a timer to track how long it’s been since the player was last damaged.

static float timeSinceDamaged = 0;
timeSinceDamaged += deltaTime;

if (wasDamaged)
    timeSinceDamaged = 0;

That is a very simple way to keep a timer is by just initializinga float and accumulating the delta time every frame. However it’s not very clean because larger games can have hundreds of timers and it would be ideal to eliminate code like this all over the place. Floating pointinaccuracyis another problem that eventually crops up when the time becomes very large. After running for a few hours the precision loss can start to become a big issue. Smart timers are a way ofsimplifyingcode, optimizing, and controllingthe floating pointinaccuracyat the same time.

struct SmartTimer
  SmartTimer() : 
    startTime(invalidTime) {}

  SmartTimer(float currentTime) : 
    startTime(totalTime - long(conversion*currentTime)) {}

  void Set()
    { startTime = totalTime; }

  void Set(float currentTime)
    { startTime = totalTime - long(conversion*currentTime); }

  operator float() const			
    { return IsSet()? (totalTime - startTime)/conversion : 0; }

  void UnSet()
    { startTime = invalidTime; }

  bool IsSet() const
    { return startTime != invalidTime; }

  static void UpdateGlobal(long timeDelta)
    { totalTime += timeDelta; }


  long startTime;
  static long totalTime;
  static const long invalidTime = LONG_MAX;
  static const float conversion;

long SmartTimer::totalTime = 0;
const float SmartTimer::conversion = 10000;

Smart timersencapsulatetimerfunctionalityinto a struct that holds only alongso itusesexactlythe sameamountof memory as a float. The constructor sets it to an invalid statewhere the timer is effectivelydisabled. A simple call to Set() causes the timer to start at 0 or if a value is passed in it will start the timer with that instead.There’s no need to manually increment this timerbecausea single call to the UpdateGlobal() function is enough to update every timer in the game. A timer can be stopped by calling Invalidate() and that state can also be checked by calling IsValid(). This is desirable because we can control whether a timer is running and check it’s time without using any extra memory. stopwatch2

The issue of floating point accuracy is also addressed by storing the timeas 100’s of nanoseconds(10^-4seconds) which means the precision is fixed. This will probably be a high enough accuracy for most things but if necessary the code can easily bemodified by changing the conversion factor.You may also want to changethe storage typeto extend the amount of time it can run before wrapping becomes an issue. The values used inthis example can run for about 2 days with total accumulatedinaccuracyof only a few seconds whenupdated at 60 fps and it can run for weeks without overflowing. In practice you will want to reset the timers whenever possible such as when a new game begins, but for simulations it may be useful to have something that runs for days.

Another option is to store the timesdoubles which is somewhat simpler by eliminating the need for a conversion factor. The downside is that it uses twice as much memory and will eventually lose precision over time rather then providing consistent precision like an integer type does. Converting from double to float may be slower or faster then the integer conversion depending on the processor.

Smart timers have been an invaluable tool that I’ve used in almost every project I’ve been a part of. Please feel free to copy or modify my smart timer class for your own projects. My open source game engine uses a more complex version that incorporates interpolation for those interested inlearning more.

This entry was posted in Game Dev and tagged , . Bookmark the permalink.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.