On Microsoft.Xna.Framework.GamerServices.Guide.IsVisible

Sam Hart

2009-02-18 10:54:38

There's something I've heard a lot over the years. It's a common complaint that Win32 developers express when they first come to an Open Source development environment (such as Linux with its myriad of Free Software/Open Source development tools). This common complaint is that the development documentation is somehow lacking compared to the Win32 development documentation. Generally, they point to MSDN as the gold standard and bemoan Linux's lack of something similar. This complaint is even one of the motivating factors for the LSB project (which, I used to be a part of).

Whenever I have heard this complaint in the past, it has always made me laugh. I've laughed because the development documentation for Linux and its Free Software/Open Source ilk tends to be very good... you just have to know where to look for it. You have to be comfortable reading man pages, browsing info documents, snooping through /usr/doc, etcetera, etcetera. True, it's not all available in one web site, but more often than not it's more convenient to get at in the end (much of it can be accessed quickly from the command line).

Well, after an ordeal I've had today trying to solve a simple, and common, problem with my XNA-based video game using MSDN, I will laugh even harder when I encounter Win32-weenies with this complaint in the future...

XNA's Lovely Guide Class


Without going into too much detail, XNA is a C#-based framework that allows you to quickly create video games (and related technologies) for the various Microsoft platforms including Windows, the Xbox 360, and the Zune. On some of these platforms you have an interface which is called the "Guide" that operates outside of your game and provides the user access to system-wide information. You cannot really control this guide inside of your application(1). If the user presses the guide button on their controller, for example, the guide will pop-up and intercept all user inputs. This is a design feature which primarily makes sense on the Xbox 360 where the guide provides access to the Dashboard.

What this means to your code is:


The end result is when the guide pops up, your code needs to stop what it's doing and wait until the guide goes away. If you are in a gameplay loop, your game needs to pause (so your player doesn't die, for example). If you are in the middle of a complicated animation or cut-scene, perhaps your animation or cut-scene should be paused until the game gets focus again.

XNA provides a way to determine whether the guide is active or not in the Gamer Services class Guide. Specifically, the member Guide.IsVisible tells you whether or not the guide is visible (active). This means you can simply poll Guide.IsVisible every Update(..) to verify that the guide isn't active before performing your gameplay loop logic. Sounds simple enough, right?

MSDN's Not-So Helpful Advice


When the guide is active, the game probably should be paused somehow. So coupling Guide.IsVisible with other pause-code seems logical.

In fact, this is exactly what is suggested in the MSDN article How To: Pause a Game. In this article, they suggest using the following segment of code inside your Update(..) to handle both user pause requests as well as guide events:


// Pause if the Guide is up, or if the user has paused
if ((paused == false) && (Guide.IsVisible == false))
{
base.Update(gameTime);

....
}



If you run their sample code, it seems to work. You are able to pause/unpause the game manually, exit the game successfully, etcetera and so on. However, if you actually watch the Guide.IsVisible property you notice something is wrong.

When the game starts up, Guide.IsVisible is false- as it should be. When you press the guide button on your controller and activate the guide, Guide.IsVisible becomes true- as it should be. However, closing the guide on screen, and thus returning control back to your game, does not reset Guide.IsVisible back to false. Instead, Guide.IsVisible stays true for the rest of the application's run.

Furthermore, if you google around for using Guide.IsVisible to pause your XNA game when the guide activates, you will find many variants on this theme:


if(!Guide.IsVisible)
{
base.Update(gameTime);
....
}



However, all of these variants and the original MSDN article are wrong. This is not the way to use Guide.IsVisible. Let me tell you why.

Guide Is Part Of Gamer Services


As I mentioned above, the Guide class is a part of something called Gamer Services. More specifically, it is managed by the GamerServicesComponent. Persons intimately familiar with the way XNA works will likely already see the problem, but for those who don't, allow me to explain a bit of the XNA internals.

A typical XNA application consists of multiple entities called "components". These components allow you to divide up the work that must be done every tick(2) by your application. You can think of these components as the building blocks for your application.

Each component must have an Update(..) method which is called every tick. This Update(..) contains component-specific logic. If the component's Update(..) is not called, then its per-tick logic will not run (obviously).

Well, note that Gamer Services is a component. This means if it is not run, it wont update the various members it manages, which includes Guide.IsVisible.

Now look back at the code:


if(!Guide.IsVisible)
{
base.Update(gameTime);
....
}



Once Guide.IsVisible is set to true because the guide has been activated, we never call base.Update(..) again. This means that Gamer Services' Update(..) is never called again! This means that, no matter what happens with the guide, Guide.IsVisible is never updated again!

The Real Solution


The end result is that you need to ensure that your Gamer Services get updated each tick even when the guide is active. I'm certain there's plenty of ways to do this, but the way I chose was to first define a local instance of GamerServicesComponent, and then to ensure that local instance's Update(..) is called.


// Somewhere up where we define properties or fields
public GamerServicesComponent GamerServices;

....

// Then down in our Update(..) method
if (Guide.IsVisible)
{
GamerServices.Update(gameTime);
}
else
{
base.Update(gameTime);
}



Long story short, MSDN is misleading and wrong, and their example code for using Guide.IsVisible will lead you astray.

I will be submitting a bug report on this, and hopefully it will be fixed (so, if you're reading this at some point in the future when the example code has been corrected, you know who to thank).

Notes