8. Converting the Game Framework to Run on Windows
To get our projects running both on
the phone and on Windows, we will need to create a version of this
library that works in Windows, too.
This turns out to be a simple process for the most
part, and allows us to transfer the phone's projects into Windows
without too much effort.
In this section we will look at the steps involved in getting GameFramework working in both environments.
8.1. Storing and Retrieving Game Settings
As mentioned in the "Isolated Storage" section a moment ago, there is no IsolatedStorageSettings
class in the Windows version of XNA, so we will need to find an
alternative method for Windows to use for storing settings in the GameFramework.SettingsManager class.
This functionality is not too tricky to replicate.
First of all, when compiling for Windows, the class will provide a
dictionary into which string values can be written. Two new functions
are then provided that allow the contents of this dictionary to be
translated into an XML document and written to disk, or to be read back
from the XML and placed back into the dictionary.
The API for the class can remain identical across the
two platforms, removing any need for the games using the class to have
to cater separately for each target environment.
First there is the settings dictionary. This dictionary is declared as a class-level variable, as shown in Listing 6,
set to compile only under Windows. Alongside it we declare a file name
to use when reading and writing the settings. No path is specified, so
this will be created in the same directory as the game's executable.
Example 6. Creating a Dictionary to store the game's settings
#if WINDOWS
// Declare a dictionary into which all of our settings will be written
private Dictionary<string, string> _settings = new Dictionary<string, string>();
// The name of the file in which the settings will be stored
private const string FileName = "Settings.dat";
#endif
|
Following on from this is a minor change to the class
constructor. So that any previously stored settings are available as
soon as the class is queried, they must be reloaded. The constructor
handles it to ensure that values are always available to the class. Listing 7 shows the new constructor; we will look at the LoadSettings function shortly.
Example 7. The modified SettingsManager class constructor
internal SettingsManager(GameHost game)
{
// Store the game reference
_game = game;
#if WINDOWS
// Load any existing stored settings
LoadSettings();
#endif
}
|
The next change that the class needs relates to
putting values into the dictionary and getting them back out again.
Although there are multiple overloads of SetValue and GetValue, only one overload for each function actually interacts with the settings, so this is the only one that we need to modify.
SetValue is enhanced as shown in Listing 8
so that it will work for both Windows Phone 7 games and Windows games.
The Windows code is virtually identical to that of Windows Phone 7,
except that it uses the class's _settings dictionary instead of IsolatedStorageSettings.
Example 8. Creating or updating settings values
public void SetValue(string settingName, string value)
{
// Convert the setting name to lower case so that names are case-insensitive
settingName = settingName.ToLower();
#if WINDOWS_PHONE
// Does a setting with this name already exist?
if (IsolatedStorageSettings.ApplicationSettings.Contains(settingName))
{
// Yes, so update its value
IsolatedStorageSettings.ApplicationSettings[settingName] = value;
}
else
{
// No, so add it
IsolatedStorageSettings.ApplicationSettings.Add(settingName, value);
}
#else
// Does this setting already exist in the dictionary?
if (_settings.ContainsKey(settingName))
{
// Update the setting's value
_settings[settingName] = value;
}
else
{
// Add the value
_settings.Add(settingName, value);
}
// Save the settings
SaveSettings();
#endif
}
|
One final change is present in this piece of code in addition to the change of dictionary, however: it makes a call to SaveSettings
each time a value is updated. This ensures that the settings are always
retained from the moment they are modified, mirroring the behavior on
the phone. We will look at the SaveSettings function shortly.
The code for GetValue is similarly modified, reading from either the _settings dictionary or IsolatedStorageSettings as appropriate. This is repeated, too, for the ClearValues and DeleteValue functions, both of which also call SaveSettings to ensure that their changes are immediately reflected to the data file on disk.
Finally we arrive at SaveSettings and LoadSettings,
whose responsibilities are to ensure that the data is stored for later
use, and then read back in for subsequent interrogation. They operate
using an XML file, into which all the defined values are placed.
The code within these functions is not particularly
interesting, using exactly the same techniques that we used for saving
and restoring the high scores inside the HighScores class, so please take a look in the project's source code if you want to see how they are implemented.
With these changes in place, the class operates
identically (from the client's perspective) under both environments. If
you scan through the code for the class, the conditional compilation
statements scattered throughout do make the code harder to read, but the
overall functionality is worth that increase in complexity if you want
to be able to target both platforms.
8.2. Application Life Cycle Events
Although in Windows Phone 7 we need to deal with the Launching, Closing, Deactivated, and Activated events, in Windows, we don't particularly need to worry about any of these as there is no application life cycle as such.
It is likely that you will have taken advantage of the Launching
event to initialize your game, however, so it is important that its
event still fires when running under Windows so that the initialization
still completes properly.
Instead of setting up the event handlers, the GameHost class constructor simply calls into the virtual GameLaunching
function when running under Windows. This is the same path that a newly
launched game would take on the phone, resulting in the same startup
behavior.
All the rest of the life cycle code can be excluded from this class as it is not needed in Windows.
8.3. High Scores
A small and simple change is required to the HighScores class to instruct it to read and write its score data to a file using the System.IO
namespace instead of using isolated storage. As the class already
stores its content in an XML document, no additional modifications are
necessary.
8.4. Everything Else
There are no other changes required to the game framework. The rest of the project works identically in both environments.
The biggest changes that you will find you need to
make when getting your games running for Windows will almost certainly
be in the area of the organization of your display (taking the Windows
display orientation and screen resolutions into account) and input
(using Mouse and Keyboard instead of TouchPanel). You should find that the rest of your game will work as expected without any changes being required.