Game timing in Silverlight 5

Originally posted to Shawn Hargreaves Blog on MSDN, Monday, October 17, 2011

One thing missing from the new 3D functionality in Silverlight 5 is an equivalent of the XNA Game class.  I previously wrote about how to provide XNA style Update and Draw methods in a WinForms environment.  What is the Silverlight equivalent of that?

After installing the Silverlight Toolkit, create a new project using the Silverlight 3D Application template.  Open up MainPage.xaml, and note how this declares a DrawingSurface object and hooks its Draw event.  Now open MainPage.xaml.cs, and observe the implementation of the myDrawingSurface_Draw event handler method. After calling scene.Draw(), this calls e.InvalidateSurface(), which tells Silverlight this object needs to be redrawn.  Normally Silverlight would draw the object once at startup, then only redraw it when something changed about the scene (perhaps because the object was resized, or some other shape moved over the top of it).  But when the draw handler invalidates the surface, that causes it to be drawn again, which invalidates it again, which causes it to be drawn again, which { rinse, lather, repeat }.  This effectively creates an infinite loop, with the surface constantly being redrawn, just like we want for a game loop.

Note also the UserControl_Loaded method in MainPage.xaml.cs, which is hooked up to the UserControl.Loaded event via MainPage.xaml.  This is the Silverlight equivalent of the XNA Initialize and LoadContent methods.

Well and good, but how do we build an actual game on top of this system?  Check out the Silverlight port of the XNA Platformer starter kit (in c:/Program Files (x86)/Microsoft SDKs/Silverlight/v5.0/Toolkit/Sep11/Source/Sample source code.zip/Xna).  Again look at the myDrawingSurface_Draw method in MainPage.xaml.cs.  This is very simple:

    private void myDrawingSurface_Draw(object sender, DrawEventArgs e)
    {
        platformerGame.Update(e);
        platformerGame.Draw(e);

        e.InvalidateSurface();
    }

What we have here is a variable timestep game loop.  Thanks to the InvalidateSurface call, it will loop forever, calling Update and then Draw as fast as possible.

If you prefer to use a fixed timestep system, you can make one like so:

    readonly TimeSpan TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 60);
    readonly TimeSpan MaxElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 10);

    TimeSpan accumulatedTime;


    private void myDrawingSurface_Draw(object sender, DrawEventArgs e)
    {
        TimeSpan elapsedTime = e.DeltaTime;

        if (elapsedTime > MaxElapsedTime)
        {
            elapsedTime = MaxElapsedTime;
        }

        accumulatedTime += elapsedTime;

        while (accumulatedTime >= TargetElapsedTime)
        {
            Update();
            accumulatedTime -= TargetElapsedTime;
        }

        Draw();
        e.InvalidateSurface();
    }
Blog index   -   Back to my homepage