Game State Management in MonoGame on Windows 8
/I’ve been trying to make sense of how Windows 8 and MonoGame interact when the application changes state. I think I’ve figured it out.
There seem to be broadly two things that can happen to your game when it is running. It can get deactivated (the user finds something better to do) or it can get “snapped” to the side of the screen. If you are going to sell the program in the marketplace it needs to handle both these situations.
protected override void OnActivated(object sender, EventArgs args) { addMessage("activated"); base.OnActivated(sender, args); } protected override void OnDeactivated(object sender,
EventArgs args) { addMessage("deactivated"); base.OnDeactivated(sender, args); } protected override void OnExiting(object sender,
EventArgs args) { addMessage("exiting"); base.OnExiting(sender, args); }
To handle the activation issues you override the event methods above. I’ve just put some logging code in so that I can work out what is going on. The events are fired when you would expect them to be. When the game starts you get an “onActivated” message and when anything happens to interrupt your game (for example the user minimises it or switches to another application) you get a “onDeactivated” message. At the very end, when the user quits your game you get the “onExiting” message. The activated and deactivated message come in matched pairs. The only slight trickiness is that a game will get an activated message at when it starts and when someone returns to it, but you can use a variable to keep track of this.
When your game is deactivated it should probably pause the game (because the user will not be able to interact with it). When your game is reactivated you can either resume the game or stay in pause mode and give the player a few seconds to compose themselves before continuing. When the game is exited your game should store any persistent game state and the first time it is activated your game should load the state.
The next thing you need to do is handle changes to the application view. These occur when the user “snaps” you to the side window or when the orientation/size of the screen changes. The first thing you need to do is bind an event handler to the event which fires when the application view changes:
ApplicationViewChanged +=
(sender, args) => ViewChanged(args.ViewState);
Your event handler will have to deal with changes in orientation and also “snapping” to the edge of the screen.
private void ViewChanged(ApplicationViewState viewState) { switch (viewState) { case ApplicationViewState.Filled: addMessage("filled"); break; case ApplicationViewState.FullScreenLandscape: addMessage("fullLand"); break; case ApplicationViewState.FullScreenPortrait: addMessage("fullPort"); break; case ApplicationViewState.Snapped: addMessage("snapped"); break; } }This is my test method. It just sends a message out for the different states.
When you get snapped your game could display a “Mini-screen” in the snapped area. Above you can see what a snapped game looks like. The cornflower blue area on the right is the snapped area. When the game is unsnapped it will get a message to indicate the orientation of the screen. My games just pause when they are snapped.
One thing to remember is that when you get snapped the size of the screen changes.You need to change the size of the screen so that things are still drawn correctly in the snap panel area. You can grab the new values of the viewport size to do this.
cheeseRectangle = new Rectangle(0, 0,
_graphics.GraphicsDevice.Viewport.Width,
_graphics.GraphicsDevice.Viewport.Height);
The above snippet resizes a cheese drawing rectangle to fit the whole screen. I could use this to make sure that my cheese is always displayed full size (although when the program is snapped this will make for a rather long and thin display).
Having got this far you are pretty much sorted. There is just one more thing you need to know, and that is the sequence of the messages. The log sequence at the top of this post will help here. The important thing to note is that you get viewport changed messages after you have been deactivated. If you think about it, this makes sense. The user can start to drag your application screen and then either put it back, or drop it into the snap area. It is important that the application is stopped when the user begins to perform such an action. So if your program is snapped you will get the sequence:
- deactivated
- snapped
- activated
You never get the view changed messages in isolation. This even the case when you use the Windows + . command to snap the game from the keyboard.