Over a million developers have joined DZone.

Windows Phone 8: Games

· Mobile Zone

Learn how to Deliver Better Mobile Apps Faster with Continuous Quality by managing the complexities of testing multiple devices and scenarios with this whitepaper from Perfecto Mobile.

I was recently asked to write an article for our upcoming 30-to-Launch series about using RoaminData in a Windows 8 game. Here’s that article and a link to the code.

Every game is unique, and carries with it a unique set of game data to save and load at various points of the games lifecycle. In the past, most gamers – especially casual gamers – had only one device on which their favorite game was installed. Data storage for those games was simpler for developers to manage in that the game data could be saved locally in a variety of formats, and loaded when the game started or at the user’s request, and was all self-contained within the game itself. Today, with people owning multiple devices both at work and at home, game experiences are more challenging to manage as users want to be able to share game data between different computers without having to store that data on some external device like a thumb drive or portable hard drive. Although there are a variety of custom code solutions that could be developed to support the sharing of game data between computers, WinRT provides a set of classes that facilitates this type of data sharing automagically – it’s called the Windows 8 Roaming Data Store. In this article, we’ll look at a simple game implemented for WinRT that uses the Roaming Data Store that enables a player to stop gameplay on one machine and resume it on a second.

Enter the Game

For this article, we will be using a very basic WinRT game based on the popular game Bulls and Cows, often referred to as Mastermind, to illustrate how game data can be saved and shared using the different features of the Roaming Application Data Store in WinRT.

Figure 1 – Sample Mastermind Game

Gameplay is very straightforward – the computer draws a random set of 4 colored boxes from a set of 6 different color choices to form a “code”. The player, often referred to as the code breaker”, provides a series of “guesses” to the solution by selecting a combination of 4 colors via the large buttons on the bottom that the game engine will test against the solution. The player will receive one red indicator for each color the player selects that represents the proper color at the proper position in the solution, and one white indicator for each color the player selects that is part of the solution but NOT in the proper location. In the sample above, the player was able to solve the game in 7 tries using deductive reasoning based on the red and white indicators for each of their guesses. For our game, we want to create an environment where the user can save off their current game state, and restore it on a different computer at some time in the future. To understand how to achieve this, you first have to understand how the Roaming Application Data Store works on WinRT.

Roaming Application Data Store on WinRT

The Roaming Application Data Store is part of the larger WinRT Application Data Store that provides data synchronization between computers sharing a common application. The data is synchronized through the use of a shared cloud storage area attached to a user’s Microsoft account.

Figure 2 – Synchronizing Data via the Roaming Data settings

The Application Data Store is comprised of three separate data storage areas:

  • Local Settings - data that is stored and scoped locally to this single instance of an installed application
  • Roaming Settings - data that is shared between devices on which the user has installed a particular application
  • Temporary Settings - transient data created by the application, stored and scoped locally to a single machine, and could be deleted by the OS at any time

Each of these three data stores can be further broken down into App Files and App Settings.

App Settings

App Settings are stored in the registry, exposed to the developer as a set of simple Key/Value pairs into which any valid WinRT data type can be stored (if binary types are needed, the developer must use App Files instead of App Settings). App Settings support the concept of Containers, into which sets of related App Settings can be organized and stored together. A default container is used when none is explicitly specified, or developers can create their own containers up to 32 levels deep to better organize their application settings. Composite Settings can also be used to support atomic updates of related settings to ensure data consistency during concurrent access and roaming.

App Files

The Files interface gives the developer a way to store serialized objects, data files, or other complex objects using the familiar File patterns. Under the covers, App Files are stored on the file system in a hidden folder on the user’s computer, located beneath the application’s installation directory. Files can be listed, created, deleted and updated using the classes in the System.IO namespace.

Both App Settings and App Files are automatically synchronized between machines on which the application is installed. By simply writing to the App Settings or App Files within the Roaming Data store, data synchronization is automatic.

Using the Roaming Application Data Store in the game

NOTE: You can download the code sample that goes with this post from CodePlex at the following URL:

http://win8roamingstorage.codeplex.com/

The first step to incorporating roaming data was to get the game up and running with all the basic features and decide on what bits of state I needed to save. I wanted to make sure that I had something simple to serialize and deserialize from storage, and I also wanted to experiment with both App Settings and App Files to see which ones worked better for different situations.

Saving Game State

Since my game logic was stored in a separate set of classes from the application UI, and I had used the MVVM pattern for managing application state for data binding, I had everything I needed in one place – the ViewModel. I was tempted to just save off the entire MainViewModel itself – all in one chunk – but decided instead to save off pieces of it to save space (see Observations: Quota below). My MainViewModel does a lot more than just hold state, so I selected just the bits I needed to persist into roaming storage. So, after every move that was submitted by the player, I called out to the following method:

private async void SaveGameState()
{
    StorageHelper.SetGameInProgress();
    await StorageHelper.SaveObjectToRoamingFolder(STR_Gamejson, _game);
    await StorageHelper.SaveObjectToRoamingFolder(STR_Movesjson, Moves);
    StorageHelper.PutObjectToSetting<string>("MoveSlotOne", MoveSlotOne);
    StorageHelper.PutObjectToSetting<string>("MoveSlotTwo", MoveSlotTwo);
    StorageHelper.PutObjectToSetting<string>("MoveSlotThree", MoveSlotThree);
    StorageHelper.PutObjectToSetting<string>("MoveSlotFour", MoveSlotFour);
}

This method uses a helper class I created to save App Files and App Settings into the roaming data store.

First, we tackle saving a file to the Roaming Folder. I’ve got two examples in my source code for this – first is the Game object that tracks the current state of the game including the current solution, history of moves, number of moves remaining, etc. The second is the PlayerMoveViewModel objects stored in the Moves collection. These are wrappers around the GameMove objects that contain UI-friendly representations of the Move data as well as the corresponding MoveResults (i.e. # of white and red indicators). The method used here is SaveObjectToRoamingFolder on my StorageHelper class:

public async static void SaveObjectToRoamingFolder(string filename, object o)
{
    var appData = ApplicationData.Current;
    string jsonData = await JsonConvert.SerializeObjectAsync(o);
    StorageFile sampleFile = await appData.RoamingFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(sampleFile, jsonData);
}

I’m using Json.NET for my JSON serialization needs as I’ve used it before, it’s fast, and easy to install via NuGet

. By using JSON, I minimize the size of each stored file to approx. 1KB for both the Moves collection and the Game object itself.

Next we tackle the four buttons the player was using to enter their guesses. I still feel that storing these settings are not specifically required from a usability perspective, but I thought it would add a nice touch to the game to have the board look exactly as it had before the player left it on their previous machine. To store these four settings, I decided to use the App Settings data store to see how that would work out. I extended my helper class with the PutObjectToSetting<T>() method to make this easy:

public static void PutObjectToSetting<T>(string key, T value)
{
    var appData = ApplicationData.Current;
    appData.RoamingSettings.Values[key] = value;
}

Short and sweet, this method simply attaches to the current RoamingSettings object and inserts a value according to a specific key if none exists, or updates the value of that key if already present. I’ve recently learned that people are calling this an “upsert”, although I’m not sure how I feel about that term :-)

Loading Game State

Saving the game state was really easy – after every move, the game would save off the current state of the board and the 4 input buttons to the Roaming Store. Retrieving the data was a bit more challenging as I had identified 3 use cases for loading data:

  • Restarting the game on the local machine
  • Restarting the game on the secondary machine
  • Updating the currently running game on the local machine with data from the secondary machine.

The first two use cases are the primary use cases I identified for this game. I wanted to make sure that I could effectually stop the game on the local machine and restore it back to where I came from on the remote machine. I also wanted to consider the situation where I might pause a game on one machine and pick it up on another, only to come back to the first machine later. This was a bit more work, but ultimately boiled down to some additional code at launch time, and an event to register for while the app was running.

MVVM is a great pattern for separating UI logic from Application logic, but sometimes – as in the case of initially launching the application, there can be some overlap. My sample application uses the MvvmLight Preview library from Laurent Bugnion at GalaSoft (also available via a convenient NuGet package) to make this easier to manage. When my application launches, I use the MvvmLight Messenger object to send a notification to the UI that the user needs to be asked if they want to load the existing game, or start a new one when saved game state is found. I didn’t want to do this in the UI, which would have simplified the code, as I felt it broke the MVVM pattern and I wanted to keep to the pattern as much as possible.

So, in the constructor for the MainViewModel object, I register a listener for the GameBoardReadyMessage to check for existing game state and alert the UI if necessary. Otherwise, we trigger the beginning of a new game and let the user start entering data.

Messenger.Default.Register<GameBoardReadyMessage>(this, (message) =>
{
    if (StorageHelper.GameInProgress)
    {
        var msg = new AskForGameRestoreMessage();
        Messenger.Default.Send<AskForGameRestoreMessage>(msg);
    }
    else
    {
        StartNewGame();
    }
});

When the Main Page launches, it send a GameBoardReadyMessage when it’s ready to be loaded, and registers to listen for AskForGameRestoreMessage messages to arrive from the MainViewModel. When AskForGameRestoreMessage is triggered, the code-behind in the MainPage will prompt the user to confirm that they want to load the existing game (LoadSavedGameMessage) or start a new one (StartNewGameMessage). Depending on the answer, additional messages are fired to alert the ViewModel of the user’s selection. The path we want to follow here is the LoadSavedGameMessage path in the MainViewModel, as implemented in the LoadSavedGame method:

private async void LoadSavedGame(ApplicationData appData)
{
    try
    {
        CurrentGame = await StorageHelper.GetObjectFromRoamingFolder<Game>(appData, STR_Gamejson);
        CurrentGame.OnFailure += _game_OnFailure;
        CurrentGame.OnVictory += _game_OnVictory;
        Moves.Clear();
        var moves = await StorageHelper.GetObjectFromRoamingFolder<ObservableCollection<PlayerMoveViewModel>&t;(appData, STR_Movesjson);
 
        foreach (var move in moves)
        {
            Moves.Add(move);
        }
 
        MoveSlotOne = StorageHelper.GetObjectFromSetting<string>(appData, "MoveSlotOne");
        MoveSlotTwo = StorageHelper.GetObjectFromSetting<string>(appData, "MoveSlotTwo");
        MoveSlotThree = StorageHelper.GetObjectFromSetting<string>(appData, "MoveSlotThree");
        MoveSlotFour = StorageHelper.GetObjectFromSetting<string>(appData, "MoveSlotFour");
    }
    catch (Exception ex)
    {
        // show error message
        ClearSavedGameState();
        Messenger.Default.Send<ErrorLoadingGameMessage>(new ErrorLoadingGameMessage(ex));
        // start new game
        StartNewGame();
    }
}

In this code sample, we’re attempting to pull out all of the saved game state from both App Files and App Settings and reconstitute the game in progress. If anything goes wrong, we let the user know via an alert and just start a new game. The methods I added to the StorageHelper class do the reverse of the work we did to save the object to storage, and instead pull the data back out:

public async static Task<T> GetObjectFromRoamingFolder<T>(ApplicationData appData, string filename)
{
    StorageFile sampleFile = await appData.RoamingFolder.GetFileAsync(filename);
    string jsonData = await FileIO.ReadTextAsync(sampleFile);
    var o = await JsonConvert.DeserializeObjectAsync<T>(jsonData);
    return o;
}

GetObjectFromRoamingFolder<T> takes the data from the Roaming Store and uses Json.NET to turn that data back into the object of our choice (note that this method has to be marked as async because the underlying access to the RoamingFolder requires await).

public static T GetObjectFromSetting<T>(ApplicationData appData, string setting)
{
    var raw = appData.RoamingSettings.Values[setting];
    T obj = (T)raw;
    return obj;
}

Lastly, the GetObjectFromSetting<T>() method pulls back a value from the RoamingSettings collection and casts it into the proper type before returning.

Other things to consider

There are a few specifics related to the Roaming Data store that need to be considered before using its capabilities in your application. I didn’t account for all of them in my sample, but I wanted to list the more important ones here for you to consider when implementing Roaming Data in your game:

Quotas

Unlike data stored in the Local Data area, Roaming Data is governed by a storage quota

, as defined by the RoamingStorageQuota property of the ApplicationData object. This quota provides a maximum size for data that can be synchronized in the cloud via the Roaming Data store. As of this writing, the current RoamingStoreQuota value is 100KB. If this limit is exceeded, the application will continue to run but the data will NOT be synchronized to the cloud for access by other installations. For game data, this is an important consideration as you design your game. You’ll need to optimize the amount of data that is stored, and the serialization formats used, to ensure that you stay within this limit or risk shutting down the automatic synchronization.

Versioning

Games, just like traditional applications, evolve over time to take on new features, fix bugs, and improve gameplay. You should consider a versioning strategy for your Roaming App data just like you’d consider a versioning strategy for the game itself. The ApplicationData class provides a Version property as well as a SetVersionAsync() method to allow you to set a version on your data, and check in when loading the application data. If the version number of the stored data is less than the version your application is expecting, the app can either reject the saved game state in favor of a new game, or you can provide an update strategy to convert the old saved game data to the new format. Current guidance is to use sequential and incrementing version numbers, starting at 1, for your Application and ApplicationData versioning needs.

DataChanged event

Depending on how your game is intended to be used, there might be a situation where there is more than one concurrently running instance of your game. In this situation, the data synchronized via the cloud could be different or even older, than the data you’re working with locally. This could create a data collision of sorts, where the application will need to make a decision on how to proceed. To aid you in dealing with these collisions, the Roaming Data store provides a simple event to which you can connect an event handler to deal with this situation – the DataChanged event:

public event TypedEventHandler<ApplicationData, Object> DataChanged

By subscribing to this event, your application will be notified when the game state on another computer has changed, and you can respond accordingly. In my sample application, I respond to this event by calling into LoadSavedGame, passing in the provided ApplicationData object to refresh the game state and the UI.

Please note: The DataChanged event is NOT intended to be used to facilitate player moves in a turn based game. Not only is the game data synchronized for a single Microsoft account, the rate at which the updates occurs is not guaranteed to be timely enough for acceptable game play. There are other techniques, such as Sockets, that provide a much better alternative to creating networked turn-based games and applications.

Summary

This was a really fun project for me to do. I had never written a game at all before, in any shape or form, so I was concerned about that aspect of it as much as the strategies I’d use for loading and storing game state. I’ve posted all my code on CodePlex at the following URL for you to download and inspect at your leisure:

http://win8roamingstorage.codeplex.com/

Please keep in mind that this is not a “complete” game, in that it is not something I’d consider ready for the Windows Store (at least not yet) but it will be fun to continue working on it to get there.

More detail about the Roaming Application Settings, and the ApplicationData object can be found all over the web. Here are a list of additional articles, references and code samples to help you get started.

 

 

Do you know Why Apps Succeed? Perfecto Mobile analyzed over 1,000 responses to their Digial Quality Strategies survey and aim to answer the question, "Why do apps succeed?" in this exclusive report.

Topics:

Published at DZone with permission of Chris Koenig, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}