A Better Game Loop

Source code: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry21.aspx

While working on my game, I found that especially when in full screen mode, if there are a lot of things in motion, the frames per second can decrease significantly. Now some of that I'm sure is because it is Alpha code and contains tons of debugging information. Still, in general, it is good to base your updates on the time since the last update, that way, your game will run at a consistent speed even if the frame rate is slower on a particular system, or based on how much you have going on.

So to improve our game loop, we can keep track of when we last called Storyboard.Begin and then when Completed fires, we can subtract this from the current time to get our elapsed time.

To make things a bit more reusable, we can do like we did with the KeyHandler class and create a new GameLoop class in the SLG101Utilities library.

Breaking Change: Code updated for breaking change in Silverlight 1.1 Alpha refresh where all storyboards now need to be named.

The GameLoop class will look like this:

 

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightGames101.Utilities
{
    public class GameLoop
    {
        Canvas targetCanvas = null;
        Storyboard storyboard;
        DateTime lastUpdateTime = DateTime.MinValue;
        TimeSpan elapsedTime;
        public delegate void UpdateDelegate(TimeSpan ElapsedTime);
        public event UpdateDelegate Update;
        public void Attach(Canvas canvas)
        {
            targetCanvas = canvas;
            storyboard = new Storyboard();
storyboard.SetValue<string>(Storyboard.NameProperty, "gameloop"); canvas.Resources.Add(storyboard); lastUpdateTime = DateTime.Now; storyboard.Completed += new EventHandler(storyboard_Completed); storyboard.Begin(); } public void Detach(Canvas canvas) { storyboard.Stop(); canvas.Resources.Remove(storyboard); } void storyboard_Completed(object sender, EventArgs e) { elapsedTime = DateTime.Now - lastUpdateTime; lastUpdateTime = DateTime.Now; if (Update!=null) Update(elapsedTime); storyboard.Begin(); } } }

And the GameLoop object will fire an Update event which can then be handled by the game canvas.

We need to make some changes to the Page class to use this GameLoop instead of the one it was using. First, change the declaration of the gameLoop field to look like this:

GameLoop gameLoop = new GameLoop();

Then remove the gameLoop related code from the Page_Loaded method and replace it with the following:

gameLoop.Attach(this);
gameLoop.Update += new GameLoop.UpdateDelegate(gameLoop_Update);

And then, replace the gameLoop_Completed method with the following:

void gameLoop_Update(TimeSpan ElapsedTime)
{
    if (keyHandler.IsKeyPressed(Key.A))
    {
        ship.RotationAngle -= rotationSpeed * ElapsedTime.TotalSeconds;
    }
    if (keyHandler.IsKeyPressed(Key.D))
    {
        ship.RotationAngle += rotationSpeed * ElapsedTime.TotalSeconds;
    }
}

And since we are now multiplying the rotationSpeed by the total seconds since the last update, and the frame rate is about 60 frames per second, change the rotationSpeed field's value from 3 to 180. This will give us a rotation speed about the same as what we had before.

So this is a bit cleaner and a lot more reusable, and it gives the added benefit of keeping the game moving at a constant speed.

Published Monday, May 21, 2007 7:04 PM by Bill Reiss

Comments

# re: A Better Game Loop

Great blog. I am interested in some silverlight game development and hope to learn a lot from your posts.

Look for some questions from me in the forums in the upcoming days :)

Wednesday, May 23, 2007 5:44 PM by Abdullah

# re: A Better Game Loop

Thanks, I hope it can be of some good use. I look forward to hearing your questions.

Wednesday, May 23, 2007 8:58 PM by Bill Reiss

# re: A Better Game Loop

when writing a next blog? :)

Thursday, May 31, 2007 4:39 AM by xg.luxv

# re: A Better Game Loop

In the next couple of days, real life has gotten in the way a little bit, but I've started working on it.

Thanks,

Bill

Thursday, May 31, 2007 7:02 AM by Bill Reiss

# re: A Better Game Loop

I'm also very interested in seeing your progress and learning from your examples.  Thanks for your posts so far, I'll be tuning in to what you post here.

Friday, June 01, 2007 4:07 PM by andrew

# re: A Better Game Loop

Great! Thanks for this - was just evaluating Silverlight vs Flash for game development and was looking for an easy way to implment a nice game loop.

Thanks much! (will be a regular reader so keep up the great work!)

Monday, July 23, 2007 12:47 PM by fgauer

# re: A Better Game Loop

I've implemented a bunch of this code in some experiments I'm doing and still notice a bit of "stuttering". i.e. the animation is not totally smooth, but about once a second it stops and jumps a bit. Any idea what this might be?

Friday, December 14, 2007 2:55 PM by bit101

# re: A Better Game Loop

Yes I've noticed this too, my best guess is that it's either because it's alpha code and is maybe creating more garbage than it should, or it could be because the frame rate is out of sync with the screen refresh rate. We'll have to see if this clears up with subsequent releases of Silverlight 2.0.

Friday, December 14, 2007 3:01 PM by Bill Reiss