Hand holding stopwatch

Timing Patterns- Defy those Chronological Challenges

Do you struggle with timing in your game? Ever feel like you might be repeating yourself? Expand your game-dev toolbox with timing patterns!

Sometimes as programmers we run into the same problems time and time again. Sometimes we run into other programmers having the same problems again and again on websites like StackOverflow. In order to deal with these repeatable problems, we come up with repeatable answers called “Patterns.”

Patterns were made famous by The Gang of Four, which Wikipedia tells me was either a group of Chinese communists, a band from the UK, or some software engineers. They created a bunch of general design patterns for software development. There are already a bajillion articles out there on these patterns, however, so I figured that in this post I’d introduce you to some very specific ones- namely timing patterns.

“Timing Patterns” may seem oddly specific, but I see people running into the problems addressed by these patterns on StackOverflow again and again and again.

You will learn:

  1. How to create reliable, consistent timers
  2. How to create unobtrusive cooldowns
  3. How to create flexible interpolations

Timing Patterns

What do first person shooters, massively multiplayer games, and simulators all have in common? They almost always feature mechanics which are functions of time. Granted, these functions are often very different (shooting bullets, ability cooldowns, resource gathering), but if you isolate the timing component they start to become very similar.

For example, I may want to give a weapon a rate of fire in a FPS, or I may want to give a player a super-ability which can only be used once a minute. They look very different, but both of these are examples of the Cooldown pattern which we’ll learn about later.

The interesting thing is from a coding perspective is that these patterns are examples of common functionality that “wraps” very uncommon functionality. This means when we instantiate a timing pattern, we’ll often pass in a lambda or anonymous class which gets invoked by the pattern at the appropriate time.

The Timer Pattern

Often in our games we want to ensure that something happens repeatedly at a fixed interval. Admittedly this is the simplest of the timing patterns, but it forms the base of the ones to come.

At first this sounds really easy to do, we simply check the time that has elapsed since our code last ran and once a certain amount of time has elapsed, we call it again:

public static void main(String... args) {
  long interval = 3000; // 3000ms == 3s

  Timer timer = new Timer();
  timer.scheduleAtFixedRate(new TimerTask() {
    @Override public void run() {
      System.out.println("Hello World!");
    }
  }, 0, interval);
}
java.util.Timer

I cheated and used Java’s own Timer class! Roughly every three seconds it prints “Hello World”- we could easily use it to regulate game loops, run animations, or schedule regularly occuring events (like spawning enemies). Unfortunately, like all easy things, this class introduces some complications:

First, it uses its own thread. This isn’t a bad thing per se, but it introduces another layer of complexity within our code (especially if your game requires a lot of these timers).Our code becomes harder to debug and we won’t always know the order things will happen in our code.

Second, it isn’t guaranteed to be accurate. This can be a problem for mechanics involving movement or physics since little imprecisions can add up over time.

We can address the first of these concerns by implementing our own version of the Timer:

public class TimingPatterns {
    public static void main(String... args) {
        try {
            new TimingPatterns().gameLoop();
        } catch (InterruptedException e) {
            System.err.println(e);
        }
    }

    private void gameLoop() throws InterruptedException {
        long interval = 3000000; // 3,000,000ns == 3s

        TimerPattern timer = new TimerPattern(interval);

        while(true) {
            Thread.sleep((long)(Math.random() * 1000)); // Simulate random interval in rendering cycles
            timer.update();
        }
    }

    private class TimerPattern {
        private long startTime;
        private long interval;
        private long current;

        public TimerPattern(long interval) {
            this.startTime = System.nanoTime();
            this.interval = interval;
            this.current = 0;
        }

        public void update() {
            long now = System.nanoTime();
            this.current += now - startTime;
            startTime = now;

            if(this.current / 1000 >= this.interval) {
                this.current = 0;
                System.out.println("Hello World!");
            }
        }
    }
}
TimerPattern

Notice how we are discarding the “remainder” by reseting the timer to 0 when the interval is complete. Not only do these milliseconds add up over time, but we run the risk of actually skipping intervals if a resource intensive process (e.g. AI, rendering, loading resources) stops our Timer from being updated for any significant amount of time. Fortunately, we can make our timer a little smarter to account for this:

    private class TimerPattern {
        private long startTime;
        private long interval;
        private long current;

        public TimerPattern(long interval) {
            this.startTime = System.nanoTime();
            this.interval = interval;
            this.current = 0;
        }

        public void update() {
            long now = System.nanoTime();
            this.current += now - startTime;
            startTime = now;

            while(this.current / 1000 >= this.interval) {
                this.current -= this.interval * 1000;
                System.out.println("Hello World!");
            }
        }
    }

We’re pretty close to having our pattern, but there’s one final problem- our timer is based off of System.nanoTime() which is using our computer’s clock to figure out the current time. It is often more helpful to determine how much time has elapsed outside of our timer and provide it a “virtual time” if you will:

  • It allows us to pause the timer simply by not updating it- then when we resume it will pick up right where it left off.
  • It allows us to pass the same difference in time to all of our timers, keeping them in sync
  • It allows us to easily speed up or slow down time by manipulating the time elapsed before we tell the Timer class.
  • It makes it a heckuva lot easier to unit test our code.

Fortunately, this isn’t hard. We can do it as follows:

    private class TimerPattern {
        private long interval;
        private long current;

        public TimerPattern(long interval) {
            this.interval = interval;
            this.current = 0;
        }

        public void update(float delta) {
            this.current += delta;

            while(this.current >= this.interval) {
                this.current -= this.interval;
                System.out.println("Hello World!");
            }
        }
    }
Virtual Time

Finally, we want to decouple our game logic from our timer class. We can do this by defining a Action interface which represents the action we want to be executed by the timer for each interval:

public class TimerPattern {
    private long interval;
    private long current;
    private Action action;

    public TimerPattern(long interval, Action action) {
        this.interval = interval;
        this.current = 0;
        this.action = action;
    }

    public void update(float delta) {
        this.current += delta;

        while(this.current >= this.interval) {
            this.current -= this.interval;
            this.action.execute();
        }
    }

    interface Action {
        void execute();
    }
}
TimerPattern.java

With Java 8, we can pass in a lambda when we create the timer in our main loop:

    private void gameLoop() throws InterruptedException {
        long interval = 3000; // 3,000ms == 3s

        TimerPattern timer = new TimerPattern(interval, () -> System.out.println("Hello World!"));

        while(true) {
            Thread.sleep((long)(Math.random() * 10000)); // Simulate random interval in rendering cycles
            timer.update( getDeltaTime() ); // Many game frameworks provide a method like this for us
        }
    }
Game Loop

Bam! We have a pattern!

The Cooldown Pattern

The cooldown pattern acts as a gate which stops an action from reoccurring until a certain amount of time has elapsed. This is a really helpful game design pattern because you can use it to balance out abilities and prevent spamming (particularly important in multiplayer games). Should some sort of ability have limited use, but you don’t want a resource mechanic? Throw on a cooldown! Need to limit a weapon’s rate of fire? Throw on a very short cooldown.

We can take our TimerPattern concepts and tweak them a little bit to produce the pattern below:

package tech.otter.patterns.timing;

/**
 * @author John Lynn <john@otter.tech>
 * @date 5/6/17
 */
public class Cooldown {
    private float duration;
    private float remainder;
    private Action action;

    public Cooldown(float duration, Action action) {
        this.duration = duration;
        this.action = action;
        this.remainder = 0;
    }

    // Decrement the cooldown until it reaches 0
    public void update(float delta) {
        if(this.remainder > 0)
            this.remainder = this.remainder - delta > 0 ? this.remainder - delta : 0;
    }

    /**
     * Executes the cooldown action and sets the cooldown (but only if the cooldown is not in effect).
     */
    public void execute() {
        if(this.remainder == 0) {
            this.remainder = this.duration;
            this.action.execute();
        }
    }

    /**
     * Get the remaining length of the cooldown; helpful for UI elements.
     * @return Remaining cooldown time in seconds.
     */
    public float getRemainder() {
        return this.remainder;
    }

    interface Action {
        void execute();
    }
}

Our Cooldown class takes in an interval and an action that can be invoked. Once the action is invoked, the cooldown is set. Until the cooldown time elapses (by calling Cooldown#update()), you can call Cooldown#execute() as many times as you want and nothing will happen.

While this example illustrates the pattern fairly well, it’s easy to imagine some nice enhancements:

Sometimes we want an “alternative” action to execute if the cooldown is not complete. For example, if the cooldown is long we might want to print a message saying “X seconds remaining” or to play an audio clip (which itself may need to be wrapped up in a cooldown pattern).

Other times we may want to not ignore a user’s action, but queue it up so that when the cooldown expires, that action will be executed (potentially resetting the cooldown).

The Interpolation Pattern

What if we want to to transition between two values as a function of time? This may be as simple as adjusting an alpha value from 0% to 100% (i.e. fade in) or moving an element from one side of the screen to another. This behavior is more subtle than it sounds, so I’ve started with a unit test that probes the expected behavior for “Fade In”:

package tech.otter.patterns.timing;

import org.junit.Assert;
import org.junit.Test;

/**
 * @author John Lynn <john@otter.tech>
 * @date 5/6/17
 */
public class InterpolationTests {
    public final Interpolation.Action<Float> LINEAR_FLOAT = (start, end, percentage) -> start + (end-start) * percentage;

    @Test
    public void testLinearProgress_zeroToHundred() {
        Float start = 0.00f;
        Float end = 1.00f;
        Float duration = 3f;
        // Create the interpolation
        Interpolation<Float> alphaFadeIn = new Interpolation<>(start, end, duration, LINEAR_FLOAT);

        // Should start at 0
        Assert.assertEquals("Should start at 0", start, alphaFadeIn.execute());

        // Advance time
        alphaFadeIn.update(1.5f);

        // Should reflect that time
        Assert.assertEquals("Should be half way through- 0.50f", new Float(end / 2), alphaFadeIn.execute());

        // Advance time to end
        alphaFadeIn.update(1.5f);

        // Should end at 1
        Assert.assertEquals("Should be 1.0", end, alphaFadeIn.execute());

        // Advance time further
        alphaFadeIn.update(5.0f);

        // Should remain at 1
        Assert.assertEquals("Should still be 1.0", end, alphaFadeIn.execute());
    }
}

Notice that all the “game logic” is contained in the unit test. We specified that we wanted to adjust floating point values, we defined an algorithm for linear interpolation of these values, and the duration that we wanted the interpolation to take place over.

What the pattern provides us in this case is a framework to run this code- we have to do a lot of definition up front, but after all we care about is telling the Interpolation timing pattern that time has passed and pulling values from it.

Support this up-front flexibility in Java requires a tricky combination of generics and lambdas (or anonymous classes, if you want to be old school):

package tech.otter.patterns.timing;

/**
 * @author John Lynn <john@otter.tech>
 * @date 5/6/17
 */
public class Interpolation<T> {
    private Action<T> action;
    private T start, end;
    private float duration;
    private float current;

    public Interpolation(T start, T end, float duration, Action<T> action) {
        this.start = start;
        this.end = end;
        this.duration = duration;
        this.action = action;
    }

    public T execute() {
        return action.execute(start, end, current/duration);
    }

    public void update(float delta) {
        if(this.current < this.duration)
            this.current = this.current + delta > this.duration ? this.duration : this.current + delta;
    }

    interface Action<N> {
        N execute(N start, N end, float percentage);
    }
}

Pro tip: This pattern/formula distinction that we’ve created between the Interpolation class and the lambda we inject into it makes it much easier to do unit testing. You can write one set of unit tests for your pattern, and another set of unit tests which focus just on your formula.

Conclusion

In conclusion, Timing Patterns are repeatable answers to temporal problems created by time-driven mechanics. They allow us to create genuinely reusable boilerplate code that is isolated from our game-specific code.

I cannot stress enough how important this concept is in general- not just in games. If you understand how to identify common problems and the patterns that solve them, it becomes much easier to break larger problems down into manageable problems that you’ve already solved.

That being said, we only covered three game programming patterns, and it’s up to you to get out there and catch them all. If you’re interested in catching more from us, let me know in the comments section below, and subscribe to the mailing list to get notified of future posts!

Save