21. Migration Guide: From the Game Class to Silverlight / XNA (Mango, C# / VB)

This document explains why and how to move your Windows Phone game to an architecture that integrates Silverlight with the XNA Framework.


WHY MOVE TO SILVERLIGHT / XNA?


While you can continue to use the Game class in XNA Game Studio 4.0 Refresh to build games for Windows Phone, moving to an application that integrates Silverlight with XNA Framework for your game brings a number of benefits. You get Silverlight’s navigation model for free, your game can leverage Silverlight UI elements, and a number of previously blocked scenarios are now possible, such as using a WebBrowser control to perform Open Authentication with social network integration.


THERE IS NO GAME


Moving to this new model does have some trade-offs—namely, the Game class and related functionality, such asGameComponents, are not present. The rest of this guide attempts to answer the common questions about how to migrate to the new application model. It also acts as an educational tool for starting in the new model to draw some parallels back to your existing knowledge.


WHERE DO I PUT WHAT USED TO BE IN MY GAME CONSTRUCTOR?


Where do put the things that used to be in your Game constructor? The answers vary based on the code. Following is a short breakdown based on what exists in our templates:

  • Construction of the SharedGraphicsDeviceManager (which replaces the GraphicsDeviceManager in the Silverlight/XNA model) is done in a file, App.xaml, so you don’t have to write that code.
  • Construction of the template ContentManager occurs in a file, App.xaml.cs, though you can create new instances ofContentManager as you wish. In our template, the app itself acts as the IServiceProvider.
  • Setting up the target elapsed time is done on the GameTimer instances for a particular page. For an example, see the template code later in “Where do I put what used to be in Update and Draw?”
  • Turning off fixed time step, which was handled by the IsFixedTimestep property on Game, is now done by setting theUpdateInterval of a GameTimer to TimeSpan.Zero.
  • Resolution, by default, will be set as 480×800 in portrait mode or, if you change your Silverlight pages to be landscape, 800×480. You can change the resolution either in App.xaml, if you want to use one resolution for the entire game, or you can set the resolution in code on any page prior to calling SetSharingMode(true).


WHERE DO I PUT WHAT USED TO BE IN LOADCONTENT?

When migrating to the integrated Silverlight/XNA Framework model, there is no primary LoadContent method. In general you can load content any time you need; however, you need to ensure that you load graphical content only when the application is in XNA Framework rendering mode (by calling the SetSharingMode method and passing in true). If the application is in the Silverlight rendering mode, you will get exceptions if you try to load graphical content. All non-graphical content can be loaded at any time once you have a ContentManager.

In our template code, we point out that you can load content in the OnNavigatedTo method of the page, after enabling Game Studio rendering. This is a suggestion, because it is a place where you can safely load graphical content; however, it is not a requirement.


WHERE DO I PUT WHAT USED TO BE IN UPDATE AND DRAW?


We now provide a simple GameTimer object to handle your update and draw loop. Following is the GamePage from the Windows Phone Rich Graphics Application template that provides a similar update-draw loop to the Game object:

C#

public partial class GamePage : PhoneApplicationPage
{
ContentManager content;
GameTimer timer;
SpriteBatch spriteBatch;

public GamePage()
{
InitializeComponent();

// Get the content manager from the application
content = (Application.Current as App).Content;

// Create a timer for this page
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Set sharing mode of the graphics device to turn on XNA Framework rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);

// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(
SharedGraphicsDeviceManager.Current.GraphicsDevice);

// TODO: use this.content to load your game content here

// Start the timer
timer.Start();

base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Stop the timer
timer.Stop();

// Set sharing mode of the graphics device to turn off XNA Framework rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);

base.OnNavigatedFrom(e);
}

/// <summary>
/// Allows the page to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
// TODO: Add your update logic here
}

/// <summary>
/// Allows the page to draw itself
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(
Microsoft.Xna.Framework.Color.CornflowerBlue);

// TODO: Add your drawing code here
}

}
VB
Partial Public Class GamePage
Inherits PhoneApplicationPage
Private content As ContentManager
Private timer As GameTimer
Private spriteBatch As SpriteBatch

Public Sub New()
InitializeComponent()

' Get the content manager from the application
content = (TryCast(Application.Current, App)).Content

' Create a timer for this page
timer = New GameTimer()
timer.UpdateInterval = TimeSpan.FromTicks(333333)
AddHandler timer.Update, AddressOf OnUpdate
AddHandler timer.Draw, AddressOf OnDraw
End Sub

Protected Overrides Sub OnNavigatedTo(ByVal e As NavigationEventArgs)
' Set sharing mode of the graphics device to turn on XNA Framework rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(True)

'Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = New SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice)

' TODO: use this.content to load your game content here

' Start the timer
timer.Start()

MyBase.OnNavigatedTo(e)
End Sub

Protected Overrides Sub OnNavigatedFrom(ByVal e As NavigationEventArgs)
' Stop the timer
timer.Stop()

' Set sharing mode of the graphics device to turn off XNA Framework rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(False)

MyBase.OnNavigatedFrom(e)
End Sub

''' <summary>
''' Allows the page to run logic such as updating the world,
''' checking for collisions, gathering input, and playing audio.
''' </summary>
Private Sub OnUpdate(ByVal sender As Object, ByVal e As GameTimerEventArgs)
' TODO: Add your update logic here
End Sub

''' <summary>
''' Allows the page to draw itself
''' </summary>
Private Sub OnDraw(ByVal sender As Object, ByVal e As GameTimerEventArgs)

SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue)

' TODO: Add your drawing code here
End Sub
End Class

The benefit of using GameTimer in these ways is that you can have as many running as you want, which means you can useGameTimer for anything from your main game loop to a timer that controls spawning enemies. Additionally, since GameTimeruses events, you can have multiple methods registered for drawing, which can make it easier to write separated drawing methods for different parts of your game.


WHERE DO I PUT WHAT USED TO BE FIELDS IN MY GAME CLASS?


This depends on the usage for the fields. In the Silverlight application model, you split your game or application into multiple pages, each one with a set of fields and methods for interacting at that point. For data that needs to be maintained and accessible throughout the entire application, you’ll want to use the App class in App.xaml.cs, created by the template. App can be retrieved at any point by using Application.Current, a static property, and casting it to your App type, as demonstrated in our template for retrieving the ContentManager:

C#

content = (Application.Current as App).Content
VB

content = (TryCast(Application.Current, App)).Content


HOW DO I UPDATE GAME LOGIC THAT USES GAMETIME?


One of the first things likely to cause trouble when migrating game logic code is the use of the GameTime class for update logic. Because GameTime is closely tied to the Game class, in the Silverlight/XNA Framework integrated model, you now haveGameTimerEventArgs, which contains the same timing information as GameTime. If you have game logic that uses GameTimetoday, you can easily make that code portable between the Game class and GameTimer by changing the usage from something like this:

C#

public void Update(GameTime gameTime) {}
VB

Public Sub Update(ByVal gameTime As GameTime) {}
End Sub
to instead directly take in the elapsed and total time as TimeSpans, which can be retrieved from both GameTime andGameTimerEventArgs:

C#

public void Update(TimeSpan elapsedTime, TimeSpan totalTime) {}
VB

Public Sub Update(ByVal elapsedTime As TimeSpan, ByVal totalTime As TimeSpan)
End Sub

This gives your game logic a common base that can be used from either app model, giving you greater portability between the two application models.

Note that “game logic” refers to extra classes that perform update logic; your primary update method will still take in either aGameTime object (in the Game class model) or a GameTimeEventArgs (in the Silverlight/XNA Framework model).


WHAT DO I DO WITH MY GAME COMPONENTS?


Many games built on the Game class also use GameComponents for some or all of their game logic. Unfortunately those types are tied to the Game class, so like GameTimer, they are not used in the integrated Silverlight/XNA Framework model. There are a few solutions to this problem.

One solution is to remove the GameComponent base type and simply make all methods public. Then your pages can simply create a list of the objects and use them as needed. This has the advantage of being lower cost, because it doesn’t require a lot of reimplementation of code, but it does mean your pages may have to do more work in order to leverage them.

Another solution is to modify your types such that you can pass them a GameTimer, and they will subscribe to the Update andDraw events. This will enable you to have objects that hook into an existing update/draw loop, but it does make those types tied to the Silverlight/XNA Framework model which can be a problem if you want to share those types with a Game-based game for Windows or Xbox 360.

The last solution is to reimplement the base types and interfaces for the GameComponent system. This requires more understanding of how GameComponent works, but that understanding would enable you to share components between your standard Game-class based games and integrated Silverlight/XNA Framework games.

There is also a new sample that provides an example of how one could architect a game component system in the Silverlight/XNA Framework model. The sample is not 100% compatible with the default GameComponent model (due to the removal of the Game and GameTime types), but you can use this new system in games based on either Silverlight/XNA Framework or the Game class.



No comments:

Post a Comment