Logo
programming4us
programming4us
programming4us
programming4us
Home
programming4us
XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
Windows Phone

XNA Game Studio 4.0 : XNA Game Studio Storage (part 2) - Getting a Device

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
8/10/2012 4:23:51 PM

Getting a Device

Getting the device to save your data to is quite error prone. What makes this situation so easy to get wrong is how the user chooses where to save the data. It is expected that all Xbox games enable the user to choose where to save data, so the API has to enable him or her to do so. The problem arises because the system requires the game to continue to run (and to continue to draw) in order to display the Guide and enable the user to choose the device, but the API to pick the device blocks whatever thread on which it is running. If the thread happens to be the same one drawing, your game hangs. What’s even worse though, if you have only a single device on the system, the Guide does not show at all. This means you can write code that hangs your game without even knowing it, which is what many people unfortunately do.

The API to get the device follows the common .NET async pattern, which is a hint to developers that this API needs to be performed asynchronously. With that small preamble out of the way, let’s implement data storage for this example. First, declare a variable for your device:

StorageDevice storageDevice;

Now, you might wonder why you are going to store the device for this example when you didn’t at first with the phone example. This is because obtaining the device can possibly force a UI popup to appear, and you don’t want to ask the user multiple times, “Hey, where do you want to store this data?” So long as the device remains valid, you should continue to use it.

Now, create the device. Because this should be done at startup, do the loading of the data if it exists then as well. Add the following code to your game’s Initialize method:

// Load the data
StorageDevice.BeginShowSelector(new AsyncCallback(
    (result) =>
    {
        storageDevice = StorageDevice.EndShowSelector(result);
        if (storageDevice != null)
        {
            storageDevice.BeginOpenContainer("GameData", new AsyncCallback(
                (openResult) =>
                {
                    using (StorageContainer file =
                        storageDevice.EndOpenContainer(openResult))
                    {
                        using (BinaryReader reader = new
                        BinaryReader(file.OpenFile("pointData")))
                        {
                            for (int i = 0; i < reader.BaseStream.Length / 8; i++)
                            {
                                points.Add(new Vector2(reader.ReadSingle(),
                                    reader.ReadSingle()));
                            }
                        }
                    }
                }
            ), null);
        }
    }
), null);

					  

This was certainly much more in depth (and complicated looking) than the isolated storage version! Looks are a little deceiving though, because the majority of the complication is handling the async pattern that is used to both get the device and the container. Note that when you use the AsyncCallback (such as this example), the callback happens on a separate thread from the one on which you called the method. This enables the code to work if the Guide pops up because the main thread is not blocked. After you have a container, the code is identical to the isolated storage code you wrote previously.

Again, you might ask yourself why you’re doing this in the constructor for this example, when during the phone example you did it in the OnActivating override. Unlike the phone, which doesn’t have the concept of the game not the focus (since it is killed), the Xbox 360 does. When your game is not focused, it gets the Deactivated event, and when it regains focus it gets the Activated event. Showing the Guide deactivates the game while it is up, and then reactivates it when it goes away. So essentially, if you use that override, you get stuck in an endless loop of creating the device.

Now, if you run the game, it doesn’t actually run—you get an exception. The exception text is quite descriptive, telling you that you need a GamerServicesComponent. Now add the following to your components collection in your game’s Initialize method:

Components.Add(new GamerServicesComponent(this));

Now when you run the example, it shows the Guide if you have more than one device available like you see in Figure 4. If you have a only single device available, it is automatically chosen for you.

Figure 4. Choosing your storage device

Note

Notice also how you check whether the device returned is null. This is because the user can easily press the B button on the dialog and cancel choosing a device. In a real game, you want to send a warning about this and confirm that a user wants to continue without saving. Here, continue without saving.


Also, notice that the first parameter is a string in BeginOpenContainer calls. This is the friendly name of the container that users see when they look at the containers in the dashboard (as seen in Figure 3). This is, of course, the name of the container you open, so use the same string to get the same container again.

A common use for the containers is to separate data logically. For example, you might have each saved game stored in its own container. This gives the user the capability to manage the save game from the system user interface without being in the game.

Before getting into the details of the API, let’s finish the example. Save the data before you leave the game, but this time use the following code for the OnExiting override:

protected override void OnExiting(object sender, EventArgs args)
{
    // Save our data
    if (storageDevice != null)
    {
        storageDevice.BeginOpenContainer("GameData", new AsyncCallback(
            (openResult) =>
            {
                using (StorageContainer file =
                    storageDevice.EndOpenContainer(openResult))
                {
                    using (BinaryWriter writer =
                        new BinaryWriter(file.CreateFile("pointData")))
                    {
                        foreach (Vector2 v in points)
                        {
                            writer.Write(v.X);
                            writer.Write(v.Y);
                        }
                    }
                }
            }
        ), null);
    }
    base.OnExiting(sender, args);
}

					  

This is similar to before, in that you open the same container if your storage device isn’t null, and then use the same code to write out the points you used earlier. Running the game now enables you to place points on the screen, exit, and run the game again to see them show back up.

Looking at the API

Now that you have the basic functionality down, let’s look at the API a bit more in depth now. The StorageDevice has the two static methods you already used as well as a single event called DeviceChanged. Proper handling of this event can be crucial for your game.

Imagine that your player starts the game and chooses to save on the memory unit. Halfway through the game, the user pulls the memory unit out for some reason. Now you can no longer save, and worse yet, your game might crash! The DeviceChanged event is fired when any device is changed. To update this example to handle this case, add the following code to the end of your Initialize method:

StorageDevice.DeviceChanged += new EventHandler<EventArgs>(
    (o, e) =>
    {
        // The device changed, make sure my current device is still valid
        if (storageDevice != null)
        {
            if (!storageDevice.IsConnected)
            {
                // My device was disconnected, set it to null
                storageDevice = null;
                // Warn the user that we won't be saving now
                Guide.BeginShowMessageBox("No Longer Saving",
                    "The device you were using to save no longer exists " +
                    "so saving automatically has been disabled.",
                    new string[] { "Ok" }, 0, MessageBoxIcon.Warning,
                    new AsyncCallback((result) =>
                        {
                            Guide.EndShowMessageBox(result);
                        }
                    ), null);
            }
        }
    }
    );

					  

When the event is fired, make sure that you already have a device selected and whether it is still connected via the IsConnected property. If it is no longer connected, set your device to null (because your code already handles that scenario) and inform the user that you are turning off automatically saving. You can handle this in other ways; for example, you can allow the user to choose a new device instead. The user should never have an unexpected loss of data though, so at a minimum you need to warn him or her, which is what this example did.

The BeginShowSelector method actually has four overloads as well. There are two pairs of similar overloads; one in each pair takes a PlayerIndex to specify which user selects the device, while the other does not. In the overload used in this example, any user can select the device when the Guide pops up, and the data is stored in such a way that any user on the console can see the data. If you use the similar overload that included the PlayerIndex as the first parameter, a few things happen.

First, only the user who matches the player index is allowed to select the device. This can easily lead your players to consider your game has locked up if you aren’t careful. For example, if you always assume that the first player is signed in and use PlayerIndex.One as the parameter to this method, but the only player signed in to the console is player two, he or she is never able to get out of this dialog without signing in to the first player.

Note

Virtually all games have a Push Start to Begin screen to detect which player is actually controlling the game.


When using a PlayerIndex, only the player can see the data. Much like how each game’s data is segregated from any other game’s data, all of the player data is separate as well.

The other two overloads each contain two new integer parameters: sizeInBytes and directoryCount. The first one is the total size of the data you plan on writing to the device, and the second is how many directories you will create. If you know this data before you create the device, using these parameters enable the user to select a device that doesn’t have enough free space on it. If you do not use these parameters, the system lets you select any device.

The StorageDevice also has a few instance members. It has three properties, including the IsConnected property you saw previously (which as the name implies, tells you if it’s connected). You can also use the TotalSpace property and FreeSpace property to get information about how much data the device can hold. It also has a DeleteContainer method, which takes in the name you passed in when opening it.

As mentioned earlier, the StorageContainer object is similar to the IsolatedStorageFile object, and they contain basically the same methods, so you can look back at the IsolatedStorage section to see those if you like. The container does contain two properties that the isolated storage file does not, namely the read-only DisplayName, which is what you used to create it with, as well as the StorageDevice that was used to create it.

Other -----------------
- XNA Game Studio 4.0 : Storage - Isolated Storage
- Using Media in XNA Game Studio : Visualizations
- Using Media in XNA Game Studio : Video
- XNA Game Studio 4.0 : Dynamic Sound Effects - Recording Audio with a Microphone, Generating Dynamic Sound Effects
- XNA Game Studio 4.0 : Playing Sound Effects (part 2) - Microsoft Cross-Platform Audio Creations Tool
- XNA Game Studio 4.0 : Playing Sound Effects (part 1) - Using SoundEffect for Audio Playback
- Windows Phone 7 : Using MVVM and Performing Unit Testing
- Windows Phone 7 : Implementing MVVM on Windows Phone by Using MVVMLight
- Windows Phone 7 : In the Cloud - Creating a Feed Reader
- Windows Phone 7 : In the Cloud - Interacting with WCF
 
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
 
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server