Parcourir la source

Check-in of migrated MonoGame 3.8.1 project

SimonDarksideJ il y a 2 ans
commit
19d8102a94
78 fichiers modifiés avec 5102 ajouts et 0 suppressions
  1. 289 0
      .gitignore
  2. 36 0
      ContentProject/.config/dotnet-tools.json
  3. 10 0
      ContentProject/Content/Content.csproj
  4. 77 0
      ContentProject/Content/Content.mgcb
  5. BIN
      ContentProject/Content/background.png
  6. BIN
      ContentProject/Content/blank.png
  7. BIN
      ContentProject/Content/blueships1.png
  8. 15 0
      ContentProject/Content/gamefont.spritefont
  9. BIN
      ContentProject/Content/gradient.png
  10. 15 0
      ContentProject/Content/menufont.spritefont
  11. 14 0
      ContentProject/ContentProject.csproj
  12. 15 0
      GameStateManagement/Content/Content.mgcb
  13. 357 0
      GameStateManagement/GameScreen.cs
  14. 21 0
      GameStateManagement/GameStateManagement.projitems
  15. 12 0
      GameStateManagement/GameStateManagement.shproj
  16. 49 0
      GameStateManagement/IScreenFactory.cs
  17. 96 0
      GameStateManagement/InputAction.cs
  18. 200 0
      GameStateManagement/InputState.cs
  19. 443 0
      GameStateManagement/ScreenManager.cs
  20. 154 0
      GameStateManagementSample.sln
  21. 36 0
      Platforms/Android/.config/dotnet-tools.json
  22. 34 0
      Platforms/Android/Activity1.cs
  23. 43 0
      Platforms/Android/Android.csproj
  24. 6 0
      Platforms/Android/AndroidManifest.xml
  25. BIN
      Platforms/Android/Resources/Drawable/Icon.png
  26. 4 0
      Platforms/Android/Resources/Values/Strings.xml
  27. 36 0
      Platforms/DesktopGL/.config/dotnet-tools.json
  28. 55 0
      Platforms/DesktopGL/DesktopGL.csproj
  29. BIN
      Platforms/DesktopGL/Icon.bmp
  30. BIN
      Platforms/DesktopGL/Icon.ico
  31. 3 0
      Platforms/DesktopGL/Program.cs
  32. 43 0
      Platforms/DesktopGL/app.manifest
  33. 36 0
      Platforms/UWP/.config/dotnet-tools.json
  34. 7 0
      Platforms/UWP/App.xaml
  35. 100 0
      Platforms/UWP/App.xaml.cs
  36. BIN
      Platforms/UWP/Assets/LockScreenLogo.scale-200.png
  37. BIN
      Platforms/UWP/Assets/SplashScreen.scale-200.png
  38. BIN
      Platforms/UWP/Assets/Square150x150Logo.scale-200.png
  39. BIN
      Platforms/UWP/Assets/Square44x44Logo.scale-200.png
  40. BIN
      Platforms/UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
  41. BIN
      Platforms/UWP/Assets/StoreLogo.png
  42. BIN
      Platforms/UWP/Assets/Wide310x150Logo.scale-200.png
  43. 11 0
      Platforms/UWP/GamePage.xaml
  44. 31 0
      Platforms/UWP/GamePage.xaml.cs
  45. 49 0
      Platforms/UWP/Package.appxmanifest
  46. 29 0
      Platforms/UWP/Properties/AssemblyInfo.cs
  47. 31 0
      Platforms/UWP/Properties/Default.rd.xml
  48. 202 0
      Platforms/UWP/UWP.csproj
  49. 36 0
      Platforms/WindowsDX/.config/dotnet-tools.json
  50. BIN
      Platforms/WindowsDX/Icon.ico
  51. 3 0
      Platforms/WindowsDX/Program.cs
  52. 44 0
      Platforms/WindowsDX/WindowsDX.csproj
  53. 43 0
      Platforms/WindowsDX/app.manifest
  54. 36 0
      Platforms/iOS/.config/dotnet-tools.json
  55. BIN
      Platforms/iOS/Default.png
  56. 6 0
      Platforms/iOS/Entitlements.plist
  57. BIN
      Platforms/iOS/GameThumbnail.png
  58. 34 0
      Platforms/iOS/Info.plist
  59. 27 0
      Platforms/iOS/LaunchScreen.storyboard
  60. 31 0
      Platforms/iOS/Program.cs
  61. 44 0
      Platforms/iOS/iOS.csproj
  62. 37 0
      README.md
  63. 133 0
      SampleCode/Game.cs
  64. 44 0
      SampleCode/ScreenFactory.cs
  65. 114 0
      SampleCode/Screens/BackgroundScreen.cs
  66. 191 0
      SampleCode/Screens/Button.cs
  67. 272 0
      SampleCode/Screens/GameplayScreen.cs
  68. 163 0
      SampleCode/Screens/LoadingScreen.cs
  69. 98 0
      SampleCode/Screens/MainMenuScreen.cs
  70. 192 0
      SampleCode/Screens/MenuEntry.cs
  71. 267 0
      SampleCode/Screens/MenuScreen.cs
  72. 186 0
      SampleCode/Screens/MessageBoxScreen.cs
  73. 149 0
      SampleCode/Screens/OptionsMenuScreen.cs
  74. 79 0
      SampleCode/Screens/PauseMenuScreen.cs
  75. 66 0
      SampleCode/Screens/PhoneMainMenuScreen.cs
  76. 149 0
      SampleCode/Screens/PhoneMenuScreen.cs
  77. 57 0
      SampleCode/Screens/PhonePauseScreen.cs
  78. 42 0
      SampleCode/Screens/PlayerIndexEventArgs.cs

+ 289 - 0
.gitignore

@@ -0,0 +1,289 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+.vscode/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs

+ 36 - 0
ContentProject/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

+ 10 - 0
ContentProject/Content/Content.csproj

@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303">
+      <PrivateAssets>All</PrivateAssets>
+    </PackageReference>
+  </ItemGroup>
+</Project>

+ 77 - 0
ContentProject/Content/Content.mgcb

@@ -0,0 +1,77 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin
+/intermediateDir:obj
+/platform:Windows
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+#---------------------------------- Content ---------------------------------#
+
+#begin background.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:background.png
+
+#begin blank.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:blank.png
+
+#begin blueships1.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:blueships1.png
+
+#begin gamefont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:PremultiplyAlpha=True
+/processorParam:TextureFormat=Compressed
+/build:gamefont.spritefont
+
+#begin gradient.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:gradient.png
+
+#begin menufont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:PremultiplyAlpha=True
+/processorParam:TextureFormat=Compressed
+/build:menufont.spritefont
+

BIN
ContentProject/Content/background.png


BIN
ContentProject/Content/blank.png


BIN
ContentProject/Content/blueships1.png


+ 15 - 0
ContentProject/Content/gamefont.spritefont

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+	<Asset Type="Graphics:FontDescription">
+		<FontName>Segoe UI</FontName>
+		<Size>32</Size>
+		<Spacing>2</Spacing>
+		<Style>Bold</Style>
+		<CharacterRegions>
+			<CharacterRegion>
+				<Start>&#32;</Start>
+				<End>&#126;</End>
+			</CharacterRegion>
+		</CharacterRegions>
+	</Asset>
+</XnaContent>

BIN
ContentProject/Content/gradient.png


+ 15 - 0
ContentProject/Content/menufont.spritefont

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+	<Asset Type="Graphics:FontDescription">
+		<FontName>Arial</FontName>
+		<Size>20</Size>
+		<Spacing>2</Spacing>
+		<Style>Regular</Style>
+		<CharacterRegions>
+			<CharacterRegion>
+				<Start>&#32;</Start>
+				<End>&#126;</End>
+			</CharacterRegion>
+		</CharacterRegions>
+	</Asset>
+</XnaContent>

+ 14 - 0
ContentProject/ContentProject.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+    <None Remove="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+  </ItemGroup>
+</Project>

+ 15 - 0
GameStateManagement/Content/Content.mgcb

@@ -0,0 +1,15 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:Windows
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+#---------------------------------- Content ---------------------------------#
+

+ 357 - 0
GameStateManagement/GameScreen.cs

@@ -0,0 +1,357 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using System.IO;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Enum describes the screen transition state.
+    /// </summary>
+    public enum ScreenState
+    {
+        TransitionOn,
+        Active,
+        TransitionOff,
+        Hidden,
+    }
+
+
+    /// <summary>
+    /// A screen is a single layer that has update and draw logic, and which
+    /// can be combined with other layers to build up a complex menu system.
+    /// For instance the main menu, the options menu, the "are you sure you
+    /// want to quit" message box, and the main game itself are all implemented
+    /// as screens.
+    /// </summary>
+    public abstract class GameScreen
+    {
+        /// <summary>
+        /// Normally when one screen is brought up over the top of another,
+        /// the first screen will transition off to make room for the new
+        /// one. This property indicates whether the screen is only a small
+        /// popup, in which case screens underneath it do not need to bother
+        /// transitioning off.
+        /// </summary>
+        public bool IsPopup
+        {
+            get { return isPopup; }
+            protected set { isPopup = value; }
+        }
+
+        bool isPopup = false;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition on when it is activated.
+        /// </summary>
+        public TimeSpan TransitionOnTime
+        {
+            get { return transitionOnTime; }
+            protected set { transitionOnTime = value; }
+        }
+
+        TimeSpan transitionOnTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition off when it is deactivated.
+        /// </summary>
+        public TimeSpan TransitionOffTime
+        {
+            get { return transitionOffTime; }
+            protected set { transitionOffTime = value; }
+        }
+
+        TimeSpan transitionOffTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Gets the current position of the screen transition, ranging
+        /// from zero (fully active, no transition) to one (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public float TransitionPosition
+        {
+            get { return transitionPosition; }
+            protected set { transitionPosition = value; }
+        }
+
+        float transitionPosition = 1;
+
+
+        /// <summary>
+        /// Gets the current alpha of the screen transition, ranging
+        /// from 1 (fully active, no transition) to 0 (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public float TransitionAlpha
+        {
+            get { return 1f - TransitionPosition; }
+        }
+
+
+        /// <summary>
+        /// Gets the current screen transition state.
+        /// </summary>
+        public ScreenState ScreenState
+        {
+            get { return screenState; }
+            protected set { screenState = value; }
+        }
+
+        ScreenState screenState = ScreenState.TransitionOn;
+
+
+        /// <summary>
+        /// There are two possible reasons why a screen might be transitioning
+        /// off. It could be temporarily going away to make room for another
+        /// screen that is on top of it, or it could be going away for good.
+        /// This property indicates whether the screen is exiting for real:
+        /// if set, the screen will automatically remove itself as soon as the
+        /// transition finishes.
+        /// </summary>
+        public bool IsExiting
+        {
+            get { return isExiting; }
+            protected internal set { isExiting = value; }
+        }
+
+        bool isExiting = false;
+
+
+        /// <summary>
+        /// Checks whether this screen is active and can respond to user input.
+        /// </summary>
+        public bool IsActive
+        {
+            get
+            {
+                return !otherScreenHasFocus &&
+                       (screenState == ScreenState.TransitionOn ||
+                        screenState == ScreenState.Active);
+            }
+        }
+
+        bool otherScreenHasFocus;
+
+
+        /// <summary>
+        /// Gets the manager that this screen belongs to.
+        /// </summary>
+        public ScreenManager ScreenManager
+        {
+            get { return screenManager; }
+            internal set { screenManager = value; }
+        }
+
+        ScreenManager screenManager;
+
+
+        /// <summary>
+        /// Gets the index of the player who is currently controlling this screen,
+        /// or null if it is accepting input from any player. This is used to lock
+        /// the game to a specific player profile. The main menu responds to input
+        /// from any connected gamepad, but whichever player makes a selection from
+        /// this menu is given control over all subsequent screens, so other gamepads
+        /// are inactive until the controlling player returns to the main menu.
+        /// </summary>
+        public PlayerIndex? ControllingPlayer
+        {
+            get { return controllingPlayer; }
+            internal set { controllingPlayer = value; }
+        }
+
+        PlayerIndex? controllingPlayer;
+
+
+        /// <summary>
+        /// Gets the gestures the screen is interested in. Screens should be as specific
+        /// as possible with gestures to increase the accuracy of the gesture engine.
+        /// For example, most menus only need Tap or perhaps Tap and VerticalDrag to operate.
+        /// These gestures are handled by the ScreenManager when screens change and
+        /// all gestures are placed in the InputState passed to the HandleInput method.
+        /// </summary>
+        public GestureType EnabledGestures
+        {
+            get { return enabledGestures; }
+            protected set
+            {
+                enabledGestures = value;
+
+                // the screen manager handles this during screen changes, but
+                // if this screen is active and the gesture types are changing,
+                // we have to update the TouchPanel ourself.
+                if (ScreenState == ScreenState.Active)
+                {
+                    TouchPanel.EnabledGestures = value;
+                }
+            }
+        }
+
+        GestureType enabledGestures = GestureType.None;
+
+        /// <summary>
+        /// Gets whether or not this screen is serializable. If this is true,
+        /// the screen will be recorded into the screen manager's state and
+        /// its Serialize and Deserialize methods will be called as appropriate.
+        /// If this is false, the screen will be ignored during serialization.
+        /// By default, all screens are assumed to be serializable.
+        /// </summary>
+        public bool IsSerializable
+        {
+            get { return isSerializable; }
+            protected set { isSerializable = value; }
+        }
+
+        bool isSerializable = true;
+        
+        
+        /// <summary>
+        /// Activates the screen. Called when the screen is added to the screen manager or if the game resumes
+        /// from being paused or tombstoned.
+        /// </summary>
+        /// <param name="instancePreserved">
+        /// True if the game was preserved during deactivation, false if the screen is just being added or if the game was tombstoned.
+        /// On Xbox and Windows this will always be false.
+        /// </param>
+        public virtual void Activate(bool instancePreserved) { }
+
+        
+        /// <summary>
+        /// Deactivates the screen. Called when the game is being deactivated due to pausing or tombstoning.
+        /// </summary>
+        public virtual void Deactivate() { }
+
+
+        /// <summary>
+        /// Unload content for the screen. Called when the screen is removed from the screen manager.
+        /// </summary>
+        public virtual void Unload() { }
+
+
+        /// <summary>
+        /// Allows the screen to run logic, such as updating the transition position.
+        /// Unlike HandleInput, this method is called regardless of whether the screen
+        /// is active, hidden, or in the middle of a transition.
+        /// </summary>
+        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
+        {
+            this.otherScreenHasFocus = otherScreenHasFocus;
+
+            if (isExiting)
+            {
+                // If the screen is going away to die, it should transition off.
+                screenState = ScreenState.TransitionOff;
+
+                if (!UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // When the transition finishes, remove the screen.
+                    ScreenManager.RemoveScreen(this);
+                }
+            }
+            else if (coveredByOtherScreen)
+            {
+                // If the screen is covered by another, it should transition off.
+                if (UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOff;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Hidden;
+                }
+            }
+            else
+            {
+                // Otherwise the screen should transition on and become active.
+                if (UpdateTransition(gameTime, transitionOnTime, -1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOn;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Active;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for updating the screen transition position.
+        /// </summary>
+        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
+        {
+            // How much should we move by?
+            float transitionDelta;
+
+            if (time == TimeSpan.Zero)
+                transitionDelta = 1;
+            else
+                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / time.TotalMilliseconds);
+
+            // Update the transition position.
+            transitionPosition += transitionDelta * direction;
+
+            // Did we reach the end of the transition?
+            if (((direction < 0) && (transitionPosition <= 0)) ||
+                ((direction > 0) && (transitionPosition >= 1)))
+            {
+                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
+                return false;
+            }
+
+            // Otherwise we are still busy transitioning.
+            return true;
+        }
+
+
+        /// <summary>
+        /// Allows the screen to handle user input. Unlike Update, this method
+        /// is only called when the screen is active, and not when some other
+        /// screen has taken the focus.
+        /// </summary>
+        public virtual void HandleInput(GameTime gameTime, InputState input) { }
+
+
+        /// <summary>
+        /// This is called when the screen should draw itself.
+        /// </summary>
+        public virtual void Draw(GameTime gameTime) { }
+
+
+        /// <summary>
+        /// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
+        /// instantly kills the screen, this method respects the transition timings
+        /// and will give the screen a chance to gradually transition off.
+        /// </summary>
+        public void ExitScreen()
+        {
+            if (TransitionOffTime == TimeSpan.Zero)
+            {
+                // If the screen has a zero transition time, remove it immediately.
+                ScreenManager.RemoveScreen(this);
+            }
+            else
+            {
+                // Otherwise flag that it should transition off and then exit.
+                isExiting = true;
+            }
+        }
+    }
+}

+ 21 - 0
GameStateManagement/GameStateManagement.projitems

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+    <HasSharedItems>true</HasSharedItems>
+    <SharedGUID>6efba8a9-a407-4029-bdd0-651d22a87386</SharedGUID>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration">
+    <Import_RootNamespace>GameStateManagement</Import_RootNamespace>
+  </PropertyGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="$(MSBuildThisFileDirectory)Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildThisFileDirectory)GameScreen.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)InputAction.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)InputState.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)IScreenFactory.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)ScreenManager.cs" />
+  </ItemGroup>
+</Project>

+ 12 - 0
GameStateManagement/GameStateManagement.shproj

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>a0cdbd0e-3a41-42b7-9425-a838bc2c7383</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
+  <PropertyGroup />
+  <Import Project="GameStateManagement.projitems" Label="Shared" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
+</Project>

+ 49 - 0
GameStateManagement/IScreenFactory.cs

@@ -0,0 +1,49 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// IScreenFactory.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Defines an object that can create a screen when given its type.
+    /// 
+    /// The ScreenManager attempts to handle tombstoning on Windows Phone by creating an XML
+    /// document that has a list of the screens currently in the manager. When the game is
+    /// reactivated, the ScreenManager needs to create instances of those screens. However
+    /// since there is no restriction that a particular GameScreen subclass has a parameterless
+    /// constructor, there is no way the ScreenManager alone could create those instances.
+    /// 
+    /// IScreenFactory fills this gap by providing an interface the game should implement to
+    /// act as a translation from type to instance. The ScreenManager locates the IScreenFactory
+    /// from the Game.Services collection and passes each screen type to the factory, expecting
+    /// to get the correct GameScreen out.
+    /// 
+    /// If your game screens all have parameterless constructors, the minimal implementation of
+    /// this interface would look like this:
+    /// 
+    /// return Activator.CreateInstance(screenType) as GameScreen;
+    /// 
+    /// If you have screens with constructors that take arguments, you will need to ensure that
+    /// you can read these arguments from storage or generate new ones, then construct the screen
+    /// based on the type.
+    /// 
+    /// The ScreenFactory type in the sample game has the minimal implementation along with some
+    /// extra comments showing a potentially more complex example of how to implement IScreenFactory.
+    /// </summary>
+    public interface IScreenFactory
+    {
+        /// <summary>
+        /// Creates a GameScreen from the given type.
+        /// </summary>
+        /// <param name="screenType">The type of screen to create.</param>
+        /// <returns>The newly created screen.</returns>
+        GameScreen CreateScreen(Type screenType);
+    }
+}

+ 96 - 0
GameStateManagement/InputAction.cs

@@ -0,0 +1,96 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// InputAction.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Defines an action that is designated by some set of buttons and/or keys.
+    /// 
+    /// The way actions work is that you define a set of buttons and keys that trigger the action. You can
+    /// then evaluate the action against an InputState which will test to see if any of the buttons or keys
+    /// are pressed by a player. You can also set a flag that indicates if the action only occurs once when
+    /// the buttons/keys are first pressed or whether the action should occur each frame.
+    /// 
+    /// Using this InputAction class means that you can configure new actions based on keys and buttons
+    /// without having to directly modify the InputState type. This means more customization by your games
+    /// without having to change the core classes of Game State Management.
+    /// </summary>
+    public class InputAction
+    {
+        private readonly Buttons[] buttons;
+        private readonly Keys[] keys;
+        private readonly bool newPressOnly;
+
+        // These delegate types map to the methods on InputState. We use these to simplify the evalute method
+        // by allowing us to map the appropriate delegates and invoke them, rather than having two separate code paths.
+        private delegate bool ButtonPress(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex player);
+        private delegate bool KeyPress(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex player);
+
+        /// <summary>
+        /// Initializes a new InputAction.
+        /// </summary>
+        /// <param name="buttons">An array of buttons that can trigger the action.</param>
+        /// <param name="keys">An array of keys that can trigger the action.</param>
+        /// <param name="newPressOnly">Whether the action only occurs on the first press of one of the buttons/keys, 
+        /// false if it occurs each frame one of the buttons/keys is down.</param>
+        public InputAction(Buttons[] buttons, Keys[] keys, bool newPressOnly)
+        {
+            // Store the buttons and keys. If the arrays are null, we create a 0 length array so we don't
+            // have to do null checks in the Evaluate method
+            this.buttons = buttons != null ? buttons.Clone() as Buttons[] : new Buttons[0];
+            this.keys = keys != null ? keys.Clone() as Keys[] : new Keys[0];
+
+            this.newPressOnly = newPressOnly;
+        }
+
+        /// <summary>
+        /// Evaluates the action against a given InputState.
+        /// </summary>
+        /// <param name="state">The InputState to test for the action.</param>
+        /// <param name="controllingPlayer">The player to test, or null to allow any player.</param>
+        /// <param name="player">If controllingPlayer is null, this is the player that performed the action.</param>
+        /// <returns>True if the action occurred, false otherwise.</returns>
+        public bool Evaluate(InputState state, PlayerIndex? controllingPlayer, out PlayerIndex player)
+        {
+            // Figure out which delegate methods to map from the state which takes care of our "newPressOnly" logic
+            ButtonPress buttonTest;
+            KeyPress keyTest;
+            if (newPressOnly)
+            {
+                buttonTest = state.IsNewButtonPress;
+                keyTest = state.IsNewKeyPress;
+            }
+            else
+            {
+                buttonTest = state.IsButtonPressed;
+                keyTest = state.IsKeyPressed;
+            }
+
+            // Now we simply need to invoke the appropriate methods for each button and key in our collections
+            foreach (Buttons button in buttons)
+            {
+                if (buttonTest(button, controllingPlayer, out player))
+                    return true;
+            }
+            foreach (Keys key in keys)
+            {
+                if (keyTest(key, controllingPlayer, out player))
+                    return true;
+            }
+
+            // If we got here, the action is not matched
+            player = PlayerIndex.One;
+            return false;
+        }
+    }
+}

+ 200 - 0
GameStateManagement/InputState.cs

@@ -0,0 +1,200 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// InputState.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Helper for reading input from keyboard, gamepad, and touch input. This class 
+    /// tracks both the current and previous state of the input devices, and implements 
+    /// query methods for high level input actions such as "move up through the menu"
+    /// or "pause the game".
+    /// </summary>
+    public class InputState
+    {
+        public const int MaxInputs = 4;
+
+        public readonly KeyboardState[] CurrentKeyboardStates;
+        public readonly GamePadState[] CurrentGamePadStates;
+
+        public readonly KeyboardState[] LastKeyboardStates;
+        public readonly GamePadState[] LastGamePadStates;
+
+        public readonly bool[] GamePadWasConnected;
+
+        public TouchCollection TouchState;
+
+        public readonly List<GestureSample> Gestures = new List<GestureSample>();
+        
+
+        /// <summary>
+        /// Constructs a new input state.
+        /// </summary>
+        public InputState()
+        {
+            CurrentKeyboardStates = new KeyboardState[MaxInputs];
+            CurrentGamePadStates = new GamePadState[MaxInputs];
+
+            LastKeyboardStates = new KeyboardState[MaxInputs];
+            LastGamePadStates = new GamePadState[MaxInputs];
+
+            GamePadWasConnected = new bool[MaxInputs];
+        }
+
+        /// <summary>
+        /// Reads the latest state user input.
+        /// </summary>
+        public void Update()
+        {
+            for (int i = 0; i < MaxInputs; i++)
+            {
+                LastKeyboardStates[i] = CurrentKeyboardStates[i];
+                LastGamePadStates[i] = CurrentGamePadStates[i];
+
+                CurrentKeyboardStates[i] = Keyboard.GetState();
+                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
+
+                // Keep track of whether a gamepad has ever been
+                // connected, so we can detect if it is unplugged.
+                if (CurrentGamePadStates[i].IsConnected)
+                {
+                    GamePadWasConnected[i] = true;
+                }
+            }
+
+            // Get the raw touch state from the TouchPanel
+            TouchState = TouchPanel.GetState();
+
+            // Read in any detected gestures into our list for the screens to later process
+            Gestures.Clear();
+            while (TouchPanel.IsGestureAvailable)
+            {
+                Gestures.Add(TouchPanel.ReadGesture());
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a key was pressed during this update. The
+        /// controllingPlayer parameter specifies which player to read input for.
+        /// If this is null, it will accept input from any player. When a keypress
+        /// is detected, the output playerIndex reports which player pressed it.
+        /// </summary>
+        public bool IsKeyPressed(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+        {
+            if (controllingPlayer.HasValue)
+            {
+                // Read input from the specified player.
+                playerIndex = controllingPlayer.Value;
+
+                int i = (int)playerIndex;
+
+                return CurrentKeyboardStates[i].IsKeyDown(key);
+            }
+            else
+            {
+                // Accept input from any player.
+                return (IsKeyPressed(key, PlayerIndex.One, out playerIndex) ||
+                        IsKeyPressed(key, PlayerIndex.Two, out playerIndex) ||
+                        IsKeyPressed(key, PlayerIndex.Three, out playerIndex) ||
+                        IsKeyPressed(key, PlayerIndex.Four, out playerIndex));
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a button was pressed during this update.
+        /// The controllingPlayer parameter specifies which player to read input for.
+        /// If this is null, it will accept input from any player. When a button press
+        /// is detected, the output playerIndex reports which player pressed it.
+        /// </summary>
+        public bool IsButtonPressed(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+        {
+            if (controllingPlayer.HasValue)
+            {
+                // Read input from the specified player.
+                playerIndex = controllingPlayer.Value;
+
+                int i = (int)playerIndex;
+
+                return CurrentGamePadStates[i].IsButtonDown(button);
+            }
+            else
+            {
+                // Accept input from any player.
+                return (IsButtonPressed(button, PlayerIndex.One, out playerIndex) ||
+                        IsButtonPressed(button, PlayerIndex.Two, out playerIndex) ||
+                        IsButtonPressed(button, PlayerIndex.Three, out playerIndex) ||
+                        IsButtonPressed(button, PlayerIndex.Four, out playerIndex));
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a key was newly pressed during this update. The
+        /// controllingPlayer parameter specifies which player to read input for.
+        /// If this is null, it will accept input from any player. When a keypress
+        /// is detected, the output playerIndex reports which player pressed it.
+        /// </summary>
+        public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+        {
+            if (controllingPlayer.HasValue)
+            {
+                // Read input from the specified player.
+                playerIndex = controllingPlayer.Value;
+
+                int i = (int)playerIndex;
+
+                return (CurrentKeyboardStates[i].IsKeyDown(key) &&
+                        LastKeyboardStates[i].IsKeyUp(key));
+            }
+            else
+            {
+                // Accept input from any player.
+                return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
+                        IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
+                        IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
+                        IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a button was newly pressed during this update.
+        /// The controllingPlayer parameter specifies which player to read input for.
+        /// If this is null, it will accept input from any player. When a button press
+        /// is detected, the output playerIndex reports which player pressed it.
+        /// </summary>
+        public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+        {
+            if (controllingPlayer.HasValue)
+            {
+                // Read input from the specified player.
+                playerIndex = controllingPlayer.Value;
+
+                int i = (int)playerIndex;
+
+                return (CurrentGamePadStates[i].IsButtonDown(button) &&
+                        LastGamePadStates[i].IsButtonUp(button));
+            }
+            else
+            {
+                // Accept input from any player.
+                return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
+                        IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
+                        IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
+                        IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
+            }
+        }
+    }
+}

+ 443 - 0
GameStateManagement/ScreenManager.cs

@@ -0,0 +1,443 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ScreenManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using System.Collections.Generic;
+using System.Diagnostics;
+#endregion
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// The screen manager is a component which manages one or more GameScreen
+    /// instances. It maintains a stack of screens, calls their Update and Draw
+    /// methods at the appropriate times, and automatically routes input to the
+    /// topmost active screen.
+    /// </summary>
+    public class ScreenManager : DrawableGameComponent
+    {
+        #region Fields
+
+        private const string StateFilename = "ScreenManagerState.xml";
+
+        List<GameScreen> screens = new List<GameScreen>();
+        List<GameScreen> tempScreensList = new List<GameScreen>();
+
+        InputState input = new InputState();
+
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+        Texture2D blankTexture;
+
+        bool isInitialized;
+
+        bool traceEnabled;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// A default SpriteBatch shared by all the screens. This saves
+        /// each screen having to bother creating their own local instance.
+        /// </summary>
+        public SpriteBatch SpriteBatch
+        {
+            get { return spriteBatch; }
+        }
+
+
+        /// <summary>
+        /// A default font shared by all the screens. This saves
+        /// each screen having to bother loading their own local copy.
+        /// </summary>
+        public SpriteFont Font
+        {
+            get { return font; }
+        }
+
+
+        /// <summary>
+        /// If true, the manager prints out a list of all the screens
+        /// each time it is updated. This can be useful for making sure
+        /// everything is being added and removed at the right times.
+        /// </summary>
+        public bool TraceEnabled
+        {
+            get { return traceEnabled; }
+            set { traceEnabled = value; }
+        }
+
+
+        /// <summary>
+        /// Gets a blank texture that can be used by the screens.
+        /// </summary>
+        public Texture2D BlankTexture
+        {
+            get { return blankTexture; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new screen manager component.
+        /// </summary>
+        public ScreenManager(Game game)
+            : base(game)
+        {
+            // we must set EnabledGestures before we can query for them, but
+            // we don't assume the game wants to read them.
+            TouchPanel.EnabledGestures = GestureType.None;
+        }
+
+
+        /// <summary>
+        /// Initializes the screen manager component.
+        /// </summary>
+        public override void Initialize()
+        {
+            base.Initialize();
+
+            isInitialized = true;
+        }
+
+
+        /// <summary>
+        /// Load your graphics content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // Load content belonging to the screen manager.
+            ContentManager content = Game.Content;
+
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+            font = content.Load<SpriteFont>("menufont");
+            blankTexture = content.Load<Texture2D>("blank");
+
+            // Tell each of the screens to load their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.Activate(false);
+            }
+        }
+
+
+        /// <summary>
+        /// Unload your graphics content.
+        /// </summary>
+        protected override void UnloadContent()
+        {
+            // Tell each of the screens to unload their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.Unload();
+            }
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Allows each screen to run logic.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            // Read the keyboard and gamepad.
+            input.Update();
+
+            // Make a copy of the master screen list, to avoid confusion if
+            // the process of updating one screen adds or removes others.
+            tempScreensList.Clear();
+
+            foreach (GameScreen screen in screens)
+                tempScreensList.Add(screen);
+
+            bool otherScreenHasFocus = !Game.IsActive;
+            bool coveredByOtherScreen = false;
+
+            // Loop as long as there are screens waiting to be updated.
+            while (tempScreensList.Count > 0)
+            {
+                // Pop the topmost screen off the waiting list.
+                GameScreen screen = tempScreensList[tempScreensList.Count - 1];
+
+                tempScreensList.RemoveAt(tempScreensList.Count - 1);
+
+                // Update the screen.
+                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+                if (screen.ScreenState == ScreenState.TransitionOn ||
+                    screen.ScreenState == ScreenState.Active)
+                {
+                    // If this is the first active screen we came across,
+                    // give it a chance to handle input.
+                    if (!otherScreenHasFocus)
+                    {
+                        screen.HandleInput(gameTime, input);
+
+                        otherScreenHasFocus = true;
+                    }
+
+                    // If this is an active non-popup, inform any subsequent
+                    // screens that they are covered by it.
+                    if (!screen.IsPopup)
+                        coveredByOtherScreen = true;
+                }
+            }
+
+            // Print debug trace?
+            if (traceEnabled)
+                TraceScreens();
+        }
+
+
+        /// <summary>
+        /// Prints a list of all the screens, for debugging.
+        /// </summary>
+        void TraceScreens()
+        {
+            List<string> screenNames = new List<string>();
+
+            foreach (GameScreen screen in screens)
+                screenNames.Add(screen.GetType().Name);
+
+            Debug.WriteLine(string.Join(", ", screenNames.ToArray()));
+        }
+
+
+        /// <summary>
+        /// Tells each screen to draw itself.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            foreach (GameScreen screen in screens)
+            {
+                if (screen.ScreenState == ScreenState.Hidden)
+                    continue;
+
+                screen.Draw(gameTime);
+            }
+        }
+
+
+        #endregion
+
+        #region Public Methods
+
+
+        /// <summary>
+        /// Adds a new screen to the screen manager.
+        /// </summary>
+        public void AddScreen(GameScreen screen, PlayerIndex? controllingPlayer)
+        {
+            screen.ControllingPlayer = controllingPlayer;
+            screen.ScreenManager = this;
+            screen.IsExiting = false;
+
+            // If we have a graphics device, tell the screen to load content.
+            if (isInitialized)
+            {
+                screen.Activate(false);
+            }
+
+            screens.Add(screen);
+
+            // update the TouchPanel to respond to gestures this screen is interested in
+            TouchPanel.EnabledGestures = screen.EnabledGestures;
+        }
+
+
+        /// <summary>
+        /// Removes a screen from the screen manager. You should normally
+        /// use GameScreen.ExitScreen instead of calling this directly, so
+        /// the screen can gradually transition off rather than just being
+        /// instantly removed.
+        /// </summary>
+        public void RemoveScreen(GameScreen screen)
+        {
+            // If we have a graphics device, tell the screen to unload content.
+            if (isInitialized)
+            {
+                screen.Unload();
+            }
+
+            screens.Remove(screen);
+            tempScreensList.Remove(screen);
+
+            // if there is a screen still in the manager, update TouchPanel
+            // to respond to gestures that screen is interested in.
+            if (screens.Count > 0)
+            {
+                TouchPanel.EnabledGestures = screens[screens.Count - 1].EnabledGestures;
+            }
+        }
+
+
+        /// <summary>
+        /// Expose an array holding all the screens. We return a copy rather
+        /// than the real master list, because screens should only ever be added
+        /// or removed using the AddScreen and RemoveScreen methods.
+        /// </summary>
+        public GameScreen[] GetScreens()
+        {
+            return screens.ToArray();
+        }
+
+
+        /// <summary>
+        /// Helper draws a translucent black fullscreen sprite, used for fading
+        /// screens in and out, and for darkening the background behind popups.
+        /// </summary>
+        public void FadeBackBufferToBlack(float alpha)
+        {
+            spriteBatch.Begin();
+            spriteBatch.Draw(blankTexture, GraphicsDevice.Viewport.Bounds, Color.Black * alpha);
+            spriteBatch.End();
+        }
+
+        /// <summary>
+        /// Informs the screen manager to serialize its state to disk.
+        /// </summary>
+        public void Deactivate()
+        {
+#if !WINDOWS_PHONE
+            return;
+#else
+            // Open up isolated storage
+            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+            {
+                // Create an XML document to hold the list of screen types currently in the stack
+                XDocument doc = new XDocument();
+                XElement root = new XElement("ScreenManager");
+                doc.Add(root);
+
+                // Make a copy of the master screen list, to avoid confusion if
+                // the process of deactivating one screen adds or removes others.
+                tempScreensList.Clear();
+                foreach (GameScreen screen in screens)
+                    tempScreensList.Add(screen);
+
+                // Iterate the screens to store in our XML file and deactivate them
+                foreach (GameScreen screen in tempScreensList)
+                {
+                    // Only add the screen to our XML if it is serializable
+                    if (screen.IsSerializable)
+                    {
+                        // We store the screen's controlling player so we can rehydrate that value
+                        string playerValue = screen.ControllingPlayer.HasValue
+                            ? screen.ControllingPlayer.Value.ToString()
+                            : "";
+
+                        root.Add(new XElement(
+                            "GameScreen",
+                            new XAttribute("Type", screen.GetType().AssemblyQualifiedName),
+                            new XAttribute("ControllingPlayer", playerValue)));
+                    }
+
+                    // Deactivate the screen regardless of whether we serialized it
+                    screen.Deactivate();
+                }
+
+                // Save the document
+                using (IsolatedStorageFileStream stream = storage.CreateFile(StateFilename))
+                {
+                    doc.Save(stream);
+                }
+            }
+#endif
+        }
+
+        public bool Activate(bool instancePreserved)
+        {
+#if !WINDOWS_PHONE
+            return false;
+#else
+            // If the game instance was preserved, the game wasn't dehydrated so our screens still exist.
+            // We just need to activate them and we're ready to go.
+            if (instancePreserved)
+            {
+                // Make a copy of the master screen list, to avoid confusion if
+                // the process of activating one screen adds or removes others.
+                tempScreensList.Clear();
+
+                foreach (GameScreen screen in screens)
+                    tempScreensList.Add(screen);
+
+                foreach (GameScreen screen in tempScreensList)
+                    screen.Activate(true);
+            }
+
+            // Otherwise we need to refer to our saved file and reconstruct the screens that were present
+            // when the game was deactivated.
+            else
+            {
+                // Try to get the screen factory from the services, which is required to recreate the screens
+                IScreenFactory screenFactory = Game.Services.GetService(typeof(IScreenFactory)) as IScreenFactory;
+                if (screenFactory == null)
+                {
+                    throw new InvalidOperationException(
+                        "Game.Services must contain an IScreenFactory in order to activate the ScreenManager.");
+                }
+
+                // Open up isolated storage
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    // Check for the file; if it doesn't exist we can't restore state
+                    if (!storage.FileExists(StateFilename))
+                        return false;
+
+                    // Read the state file so we can build up our screens
+                    using (IsolatedStorageFileStream stream = storage.OpenFile(StateFilename, FileMode.Open))
+                    {
+                        XDocument doc = XDocument.Load(stream);
+
+                        // Iterate the document to recreate the screen stack
+                        foreach (XElement screenElem in doc.Root.Elements("GameScreen"))
+                        {
+                            // Use the factory to create the screen
+                            Type screenType = Type.GetType(screenElem.Attribute("Type").Value);
+                            GameScreen screen = screenFactory.CreateScreen(screenType);
+
+                            // Rehydrate the controlling player for the screen
+                            PlayerIndex? controllingPlayer = screenElem.Attribute("ControllingPlayer").Value != ""
+                                ? (PlayerIndex)Enum.Parse(typeof(PlayerIndex), screenElem.Attribute("ControllingPlayer").Value, true)
+                                : (PlayerIndex?)null;
+                            screen.ControllingPlayer = controllingPlayer;
+
+                            // Add the screen to the screens list and activate the screen
+                            screen.ScreenManager = this;
+                            screens.Add(screen);
+                            screen.Activate(false);
+
+                            // update the TouchPanel to respond to gestures this screen is interested in
+                            TouchPanel.EnabledGestures = screen.EnabledGestures;
+                        }
+                    }
+                }
+            }
+
+            return true;
+#endif
+        }
+
+        #endregion
+    }
+}

+ 154 - 0
GameStateManagementSample.sln

@@ -0,0 +1,154 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platforms", "Platforms", "{52EF64A7-A188-4585-89F3-6F293557D548}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopGL", "Platforms\DesktopGL\DesktopGL.csproj", "{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsDX", "Platforms\WindowsDX\WindowsDX.csproj", "{AEDFC5E9-9E8E-451C-B850-A978CC00036C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Android", "Platforms\Android\Android.csproj", "{2408A857-9BCE-495D-B09D-AA286CF02DA0}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "GameStateManagement", "GameStateManagement\GameStateManagement.shproj", "{A0CDBD0E-3A41-42B7-9425-A838BC2C7383}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS", "Platforms\iOS\iOS.csproj", "{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWP", "Platforms\UWP\UWP.csproj", "{39FC5F66-4830-40C6-B905-545C19619622}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|ARM = Debug|ARM
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|ARM = Release|ARM
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|ARM.Build.0 = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|x64.Build.0 = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Debug|x86.Build.0 = Debug|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|ARM.ActiveCfg = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|ARM.Build.0 = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|x64.ActiveCfg = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|x64.Build.0 = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|x86.ActiveCfg = Release|Any CPU
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA}.Release|x86.Build.0 = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|ARM.Build.0 = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|x64.Build.0 = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Debug|x86.Build.0 = Debug|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|ARM.ActiveCfg = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|ARM.Build.0 = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|x64.ActiveCfg = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|x64.Build.0 = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|x86.ActiveCfg = Release|Any CPU
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C}.Release|x86.Build.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|ARM.Build.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|ARM.Deploy.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x64.Build.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x64.Deploy.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x86.Build.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Debug|x86.Deploy.0 = Debug|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|ARM.ActiveCfg = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|ARM.Build.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|ARM.Deploy.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x64.ActiveCfg = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x64.Build.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x64.Deploy.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x86.ActiveCfg = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x86.Build.0 = Release|Any CPU
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0}.Release|x86.Deploy.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|ARM.Build.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|ARM.Deploy.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x64.Build.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x64.Deploy.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x86.Build.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Debug|x86.Deploy.0 = Debug|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|ARM.ActiveCfg = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|ARM.Build.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|ARM.Deploy.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x64.ActiveCfg = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x64.Build.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x64.Deploy.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x86.ActiveCfg = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x86.Build.0 = Release|Any CPU
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A}.Release|x86.Deploy.0 = Release|Any CPU
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|Any CPU.ActiveCfg = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|Any CPU.Build.0 = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|Any CPU.Deploy.0 = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|ARM.ActiveCfg = Debug|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|ARM.Build.0 = Debug|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|ARM.Deploy.0 = Debug|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x64.ActiveCfg = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x64.Build.0 = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x64.Deploy.0 = Debug|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x86.ActiveCfg = Debug|x86
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x86.Build.0 = Debug|x86
+		{39FC5F66-4830-40C6-B905-545C19619622}.Debug|x86.Deploy.0 = Debug|x86
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|Any CPU.ActiveCfg = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|Any CPU.Build.0 = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|Any CPU.Deploy.0 = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|ARM.ActiveCfg = Release|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|ARM.Build.0 = Release|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|ARM.Deploy.0 = Release|ARM
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x64.ActiveCfg = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x64.Build.0 = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x64.Deploy.0 = Release|x64
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x86.ActiveCfg = Release|x86
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x86.Build.0 = Release|x86
+		{39FC5F66-4830-40C6-B905-545C19619622}.Release|x86.Deploy.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{3A50E38A-BA0F-471C-BC28-AA1F30AE72FA} = {52EF64A7-A188-4585-89F3-6F293557D548}
+		{AEDFC5E9-9E8E-451C-B850-A978CC00036C} = {52EF64A7-A188-4585-89F3-6F293557D548}
+		{2408A857-9BCE-495D-B09D-AA286CF02DA0} = {52EF64A7-A188-4585-89F3-6F293557D548}
+		{8AAF0BFB-6D6A-41D0-9846-923145B0F33A} = {52EF64A7-A188-4585-89F3-6F293557D548}
+		{39FC5F66-4830-40C6-B905-545C19619622} = {52EF64A7-A188-4585-89F3-6F293557D548}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {96D47988-E35C-4AC4-BAE3-884F8AEB6784}
+	EndGlobalSection
+	GlobalSection(SharedMSBuildProjectFiles) = preSolution
+		GameStateManagement\GameStateManagement.projitems*{2408a857-9bce-495d-b09d-aa286cf02da0}*SharedItemsImports = 5
+		GameStateManagement\GameStateManagement.projitems*{39fc5f66-4830-40c6-b905-545c19619622}*SharedItemsImports = 4
+		GameStateManagement\GameStateManagement.projitems*{3a50e38a-ba0f-471c-bc28-aa1f30ae72fa}*SharedItemsImports = 5
+		GameStateManagement\GameStateManagement.projitems*{8aaf0bfb-6d6a-41d0-9846-923145b0f33a}*SharedItemsImports = 5
+		GameStateManagement\GameStateManagement.projitems*{a0cdbd0e-3a41-42b7-9425-a838bc2c7383}*SharedItemsImports = 13
+		GameStateManagement\GameStateManagement.projitems*{aedfc5e9-9e8e-451c-b850-a978cc00036c}*SharedItemsImports = 5
+	EndGlobalSection
+EndGlobal

+ 36 - 0
Platforms/Android/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

+ 34 - 0
Platforms/Android/Activity1.cs

@@ -0,0 +1,34 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Android.Views;
+using Microsoft.Xna.Framework;
+
+namespace Android
+{
+    [Activity(
+        Label = "@string/app_name",
+        MainLauncher = true,
+        Icon = "@drawable/icon",
+        AlwaysRetainTaskState = true,
+        LaunchMode = LaunchMode.SingleInstance,
+        ScreenOrientation = ScreenOrientation.FullUser,
+        ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
+    )]
+    public class Activity1 : AndroidGameActivity
+    {
+        private GameStateManagementSample.GameStateManagementGame _game;
+        private View _view;
+
+        protected override void OnCreate(Bundle bundle)
+        {
+            base.OnCreate(bundle);
+
+            _game = new GameStateManagementSample.GameStateManagementGame();
+            _view = _game.Services.GetService(typeof(View)) as View;
+
+            SetContentView(_view);
+            _game.Run();
+        }
+    }
+}

+ 43 - 0
Platforms/Android/Android.csproj

@@ -0,0 +1,43 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0-android</TargetFramework>
+    <SupportedOSPlatformVersion>23</SupportedOSPlatformVersion>
+    <OutputType>Exe</OutputType>
+    <ApplicationId>com.companyname.Android</ApplicationId>
+    <ApplicationVersion>1</ApplicationVersion>
+    <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\..\SampleCode\Game.cs" Link="Game.cs" />
+    <Compile Include="..\..\SampleCode\ScreenFactory.cs" Link="ScreenFactory.cs" />
+    <Compile Include="..\..\SampleCode\Screens\BackgroundScreen.cs" Link="Screens\BackgroundScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\Button.cs" Link="Screens\Button.cs" />
+    <Compile Include="..\..\SampleCode\Screens\GameplayScreen.cs" Link="Screens\GameplayScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\LoadingScreen.cs" Link="Screens\LoadingScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MainMenuScreen.cs" Link="Screens\MainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuEntry.cs" Link="Screens\MenuEntry.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuScreen.cs" Link="Screens\MenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MessageBoxScreen.cs" Link="Screens\MessageBoxScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\OptionsMenuScreen.cs" Link="Screens\OptionsMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PauseMenuScreen.cs" Link="Screens\PauseMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMainMenuScreen.cs" Link="Screens\PhoneMainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMenuScreen.cs" Link="Screens\PhoneMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhonePauseScreen.cs" Link="Screens\PhonePauseScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PlayerIndexEventArgs.cs" Link="Screens\PlayerIndexEventArgs.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+    <PackageReference Include="MonoGame.Framework.Android" Version="3.8.1.303" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\ContentProject\Content\Content.mgcb" Link="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Screens\" />
+  </ItemGroup>    
+  <Target Name="RestoreDotnetTools" BeforeTargets="Restore">
+    <Message Text="Restoring dotnet tools" Importance="High" />
+    <Exec Command="dotnet tool restore" />
+  </Target>    
+  <Import Project="..\..\GameStateManagement\GameStateManagement.projitems" Label="Shared" />
+</Project>

+ 6 - 0
Platforms/Android/AndroidManifest.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Android.Android" android:versionCode="1" android:versionName="1.0">
+  <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="31" />
+  <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+  <application android:label="Android"></application>
+</manifest>

BIN
Platforms/Android/Resources/Drawable/Icon.png


+ 4 - 0
Platforms/Android/Resources/Values/Strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="app_name">Android</string>
+</resources>

+ 36 - 0
Platforms/DesktopGL/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

+ 55 - 0
Platforms/DesktopGL/DesktopGL.csproj

@@ -0,0 +1,55 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+    <ApplicationIcon>Icon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <ItemGroup>
+    <None Remove="Content\Content.mgcb" />
+    <None Remove="Icon.ico" />
+    <None Remove="Icon.bmp" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\..\SampleCode\Game.cs" Link="Game.cs" />
+    <Compile Include="..\..\SampleCode\ScreenFactory.cs" Link="ScreenFactory.cs" />
+    <Compile Include="..\..\SampleCode\Screens\BackgroundScreen.cs" Link="Screens\BackgroundScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\Button.cs" Link="Screens\Button.cs" />
+    <Compile Include="..\..\SampleCode\Screens\GameplayScreen.cs" Link="Screens\GameplayScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\LoadingScreen.cs" Link="Screens\LoadingScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MainMenuScreen.cs" Link="Screens\MainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuEntry.cs" Link="Screens\MenuEntry.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuScreen.cs" Link="Screens\MenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MessageBoxScreen.cs" Link="Screens\MessageBoxScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\OptionsMenuScreen.cs" Link="Screens\OptionsMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PauseMenuScreen.cs" Link="Screens\PauseMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMainMenuScreen.cs" Link="Screens\PhoneMainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMenuScreen.cs" Link="Screens\PhoneMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhonePauseScreen.cs" Link="Screens\PhonePauseScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PlayerIndexEventArgs.cs" Link="Screens\PlayerIndexEventArgs.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Icon.ico" />
+    <EmbeddedResource Include="Icon.bmp" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\ContentProject\Content\Content.mgcb" Link="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Screens\" />
+  </ItemGroup>
+  <Target Name="RestoreDotnetTools" BeforeTargets="Restore">
+    <Message Text="Restoring dotnet tools" Importance="High" />
+    <Exec Command="dotnet tool restore" />
+  </Target>
+  <Import Project="..\..\GameStateManagement\GameStateManagement.projitems" Label="Shared" />
+</Project>

BIN
Platforms/DesktopGL/Icon.bmp


BIN
Platforms/DesktopGL/Icon.ico


+ 3 - 0
Platforms/DesktopGL/Program.cs

@@ -0,0 +1,3 @@
+
+using var game = new GameStateManagementSample.GameStateManagementGame();
+game.Run();

+ 43 - 0
Platforms/DesktopGL/app.manifest

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="DesktopGL"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
+    </windowsSettings>
+  </application>
+
+</assembly>

+ 36 - 0
Platforms/UWP/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

+ 7 - 0
Platforms/UWP/App.xaml

@@ -0,0 +1,7 @@
+<Application
+    x:Class="UWP.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:UwpGame">
+
+</Application>

+ 100 - 0
Platforms/UWP/App.xaml.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace UWP
+{
+    /// <summary>
+    /// Provides application-specific behavior to supplement the default Application class.
+    /// </summary>
+    sealed partial class App : Application
+    {
+        /// <summary>
+        /// Initializes the singleton application object.  This is the first line of authored code
+        /// executed, and as such is the logical equivalent of main() or WinMain().
+        /// </summary>
+        public App()
+        {
+            this.InitializeComponent();
+            this.Suspending += OnSuspending;
+        }
+
+        /// <summary>
+        /// Invoked when the application is launched normally by the end user.  Other entry points
+        /// will be used such as when the application is launched to open a specific file.
+        /// </summary>
+        /// <param name="e">Details about the launch request and process.</param>
+        protected override void OnLaunched(LaunchActivatedEventArgs e)
+        {
+            Frame rootFrame = Window.Current.Content as Frame;
+
+            // Do not repeat app initialization when the Window already has content,
+            // just ensure that the window is active
+            if (rootFrame == null)
+            {
+                // Create a Frame to act as the navigation context and navigate to the first page
+                rootFrame = new Frame();
+
+                rootFrame.NavigationFailed += OnNavigationFailed;
+
+                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
+                {
+                    //TODO: Load state from previously suspended application
+                }
+
+                // Place the frame in the current Window
+                Window.Current.Content = rootFrame;
+            }
+
+            if (e.PrelaunchActivated == false)
+            {
+                if (rootFrame.Content == null)
+                {
+                    // When the navigation stack isn't restored navigate to the first page,
+                    // configuring the new page by passing required information as a navigation
+                    // parameter
+                    rootFrame.Navigate(typeof(GamePage), e.Arguments);
+                }
+                // Ensure the current window is active
+                Window.Current.Activate();
+            }
+        }
+
+        /// <summary>
+        /// Invoked when Navigation to a certain page fails
+        /// </summary>
+        /// <param name="sender">The Frame which failed navigation</param>
+        /// <param name="e">Details about the navigation failure</param>
+        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+        {
+            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+        }
+
+        /// <summary>
+        /// Invoked when application execution is being suspended.  Application state is saved
+        /// without knowing whether the application will be terminated or resumed with the contents
+        /// of memory still intact.
+        /// </summary>
+        /// <param name="sender">The source of the suspend request.</param>
+        /// <param name="e">Details about the suspend request.</param>
+        private void OnSuspending(object sender, SuspendingEventArgs e)
+        {
+            var deferral = e.SuspendingOperation.GetDeferral();
+            //TODO: Save application state and stop any background activity
+            deferral.Complete();
+        }
+    }
+}

BIN
Platforms/UWP/Assets/LockScreenLogo.scale-200.png


BIN
Platforms/UWP/Assets/SplashScreen.scale-200.png


BIN
Platforms/UWP/Assets/Square150x150Logo.scale-200.png


BIN
Platforms/UWP/Assets/Square44x44Logo.scale-200.png


BIN
Platforms/UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png


BIN
Platforms/UWP/Assets/StoreLogo.png


BIN
Platforms/UWP/Assets/Wide310x150Logo.scale-200.png


+ 11 - 0
Platforms/UWP/GamePage.xaml

@@ -0,0 +1,11 @@
+<Page
+    x:Class="UWP.GamePage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:UwpGame"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+
+    <SwapChainPanel x:Name="swapChainPanel"/>
+</Page>

+ 31 - 0
Platforms/UWP/GamePage.xaml.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace UWP
+{
+    public sealed partial class GamePage : Page
+    {
+        readonly GameStateManagementSample.GameStateManagementGame _game;
+
+        public GamePage()
+        {
+            this.InitializeComponent();
+
+            // Create the game.
+            var launchArguments = string.Empty;
+            _game = MonoGame.Framework.XamlGame<GameStateManagementSample.GameStateManagementGame>.Create(launchArguments, Window.Current.CoreWindow, swapChainPanel);
+        }
+    }
+}

+ 49 - 0
Platforms/UWP/Package.appxmanifest

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  IgnorableNamespaces="uap mp">
+
+  <Identity
+    Name="4dfa9039-3fac-47ef-aca5-bc6f8564f920"
+    Publisher="CN=User"
+    Version="1.0.0.0" />
+
+  <mp:PhoneIdentity PhoneProductId="4dfa9039-3fac-47ef-aca5-bc6f8564f920" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
+
+  <Properties>
+    <DisplayName>UWP</DisplayName>
+    <PublisherDisplayName>User</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.16299.0" MaxVersionTested="10.0.19041.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate"/>
+  </Resources>
+
+  <Applications>
+    <Application Id="App"
+      Executable="UWP.exe"
+      EntryPoint="UWP.App">
+      <uap:VisualElements
+        DisplayName="UWP"
+        Square150x150Logo="Assets\Square150x150Logo.png"
+        Square44x44Logo="Assets\Square44x44Logo.png"
+        Description="UWP"
+        BackgroundColor="transparent">
+        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
+        <uap:SplashScreen Image="Assets\SplashScreen.png" />
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+</Package>

+ 29 - 0
Platforms/UWP/Properties/AssemblyInfo.cs

@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("UWP")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UWP")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ComVisible(false)]

+ 31 - 0
Platforms/UWP/Properties/Default.rd.xml

@@ -0,0 +1,31 @@
+<!--
+    This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
+    developers. However, you can modify these parameters to modify the behavior of the .NET Native
+    optimizer.
+
+    Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
+
+    To fully enable reflection for App1.MyClass and all of its public/private members
+    <Type Name="App1.MyClass" Dynamic="Required All"/>
+
+    To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
+    <TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
+
+    Using the Namespace directive to apply reflection policy to all the types in a particular namespace
+    <Namespace Name="DataClasses.ViewModels" Seralize="All" />
+-->
+
+<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
+  <Application>
+    <!--
+      An Assembly element with Name="*Application*" applies to all assemblies in
+      the application package. The asterisks are not wildcards.
+    -->
+    <Assembly Name="*Application*" Dynamic="Required All" />
+    
+    
+    <!-- Add your application specific runtime directives here. -->
+
+
+  </Application>
+</Directives>

+ 202 - 0
Platforms/UWP/UWP.csproj

@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>AppContainerExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>UWP</RootNamespace>
+    <AssemblyName>UWP</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
+    <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
+    <TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
+    <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ProjectGuid>{39FC5F66-4830-40C6-B905-545C19619622}</ProjectGuid>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>full</DebugType>
+    <PlatformTarget>ARM</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>ARM</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>bin\WindowsUniversal\$(Platform)\$(Configuration)\</OutputPath>
+    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
+    <Optimize>true</Optimize>
+    <NoWarn>;2008</NoWarn>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <ErrorReport>prompt</ErrorReport>
+    <Prefer32Bit>true</Prefer32Bit>
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
+  </PropertyGroup>
+  <PropertyGroup>
+    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\..\SampleCode\Game.cs">
+      <Link>Game.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\ScreenFactory.cs">
+      <Link>ScreenFactory.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\BackgroundScreen.cs">
+      <Link>Screens\BackgroundScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\Button.cs">
+      <Link>Screens\Button.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\GameplayScreen.cs">
+      <Link>Screens\GameplayScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\LoadingScreen.cs">
+      <Link>Screens\LoadingScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\MainMenuScreen.cs">
+      <Link>Screens\MainMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\MenuEntry.cs">
+      <Link>Screens\MenuEntry.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\MenuScreen.cs">
+      <Link>Screens\MenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\MessageBoxScreen.cs">
+      <Link>Screens\MessageBoxScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\OptionsMenuScreen.cs">
+      <Link>Screens\OptionsMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\PauseMenuScreen.cs">
+      <Link>Screens\PauseMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\PhoneMainMenuScreen.cs">
+      <Link>Screens\PhoneMainMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\PhoneMenuScreen.cs">
+      <Link>Screens\PhoneMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\PhonePauseScreen.cs">
+      <Link>Screens\PhonePauseScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\SampleCode\Screens\PlayerIndexEventArgs.cs">
+      <Link>Screens\PlayerIndexEventArgs.cs</Link>
+    </Compile>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="GamePage.xaml.cs">
+      <DependentUpon>GamePage.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <AppxManifest Include="Package.appxmanifest">
+      <SubType>Designer</SubType>
+    </AppxManifest>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Properties\Default.rd.xml" />
+    <Content Include="Assets\LockScreenLogo.scale-200.png" />
+    <Content Include="Assets\SplashScreen.scale-200.png" />
+    <Content Include="Assets\Square150x150Logo.scale-200.png" />
+    <Content Include="Assets\Square44x44Logo.scale-200.png" />
+    <Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
+    <Content Include="Assets\StoreLogo.png" />
+    <Content Include="Assets\Wide310x150Logo.scale-200.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Page Include="GamePage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
+      <Version>6.2.13</Version>
+    </PackageReference>
+    <PackageReference Include="MonoGame.Framework.WindowsUniversal" Version="3.8.1.303" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\ContentProject\Content\Content.mgcb">
+      <Link>Content\Content.mgcb</Link>
+    </MonoGameContentReference>
+  </ItemGroup>
+  <Import Project="..\..\GameStateManagement\GameStateManagement.projitems" Label="Shared" />
+  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
+    <VisualStudioVersion>14.0</VisualStudioVersion>
+  </PropertyGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <Target Name="RestoreDotnetTools" BeforeTargets="Restore">
+    <Message Text="Restoring dotnet tools" Importance="High" />
+    <Exec Command="dotnet tool restore" />
+  </Target>
+</Project>

+ 36 - 0
Platforms/WindowsDX/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

BIN
Platforms/WindowsDX/Icon.ico


+ 3 - 0
Platforms/WindowsDX/Program.cs

@@ -0,0 +1,3 @@
+
+using var game = new GameStateManagementSample.GameStateManagementGame();
+game.Run();

+ 44 - 0
Platforms/WindowsDX/WindowsDX.csproj

@@ -0,0 +1,44 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0-windows</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <UseWindowsForms>true</UseWindowsForms>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+    <ApplicationIcon>Icon.ico</ApplicationIcon>
+  </PropertyGroup>
+    <ItemGroup>
+    <Compile Include="..\..\SampleCode\Game.cs" Link="Game.cs" />
+    <Compile Include="..\..\SampleCode\ScreenFactory.cs" Link="ScreenFactory.cs" />
+    <Compile Include="..\..\SampleCode\Screens\BackgroundScreen.cs" Link="Screens\BackgroundScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\Button.cs" Link="Screens\Button.cs" />
+    <Compile Include="..\..\SampleCode\Screens\GameplayScreen.cs" Link="Screens\GameplayScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\LoadingScreen.cs" Link="Screens\LoadingScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MainMenuScreen.cs" Link="Screens\MainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuEntry.cs" Link="Screens\MenuEntry.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuScreen.cs" Link="Screens\MenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MessageBoxScreen.cs" Link="Screens\MessageBoxScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\OptionsMenuScreen.cs" Link="Screens\OptionsMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PauseMenuScreen.cs" Link="Screens\PauseMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMainMenuScreen.cs" Link="Screens\PhoneMainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMenuScreen.cs" Link="Screens\PhoneMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhonePauseScreen.cs" Link="Screens\PhonePauseScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PlayerIndexEventArgs.cs" Link="Screens\PlayerIndexEventArgs.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\ContentProject\Content\Content.mgcb" Link="Content\Content.mgcb" />
+  </ItemGroup>  
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.1.303" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+  </ItemGroup>  
+  <Target Name="RestoreDotnetTools" BeforeTargets="Restore">
+    <Message Text="Restoring dotnet tools" Importance="High" />
+    <Exec Command="dotnet tool restore" />
+  </Target>  
+  <Import Project="..\..\GameStateManagement\GameStateManagement.projitems" Label="Shared" />
+</Project>

+ 43 - 0
Platforms/WindowsDX/app.manifest

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="WindowsDX"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
+    </windowsSettings>
+  </application>
+
+</assembly>

+ 36 - 0
Platforms/iOS/.config/dotnet-tools.json

@@ -0,0 +1,36 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-mgcb": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb"
+      ]
+    },
+    "dotnet-mgcb-editor": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor"
+      ]
+    },
+    "dotnet-mgcb-editor-linux": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-linux"
+      ]
+    },
+    "dotnet-mgcb-editor-windows": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-windows"
+      ]
+    },
+    "dotnet-mgcb-editor-mac": {
+      "version": "3.8.1.303",
+      "commands": [
+        "mgcb-editor-mac"
+      ]
+    }
+  }
+}

BIN
Platforms/iOS/Default.png


+ 6 - 0
Platforms/iOS/Entitlements.plist

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+</dict>
+</plist>

BIN
Platforms/iOS/GameThumbnail.png


+ 34 - 0
Platforms/iOS/Info.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDisplayName</key>
+	<string>iOS</string>
+	<key>CFBundleIconFiles</key>
+	<array>
+		<string>GameThumbnail.png</string>
+	</array>
+	<key>CFBundleIdentifier</key>
+	<string>project.MonoGame.iOS</string>
+	<key>MinimumOSVersion</key>
+	<string>11.0</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>CFBundleName</key>
+	<string>iOS</string>
+	<key>UIRequiresFullScreen</key>
+	<true/>
+	<key>UIStatusBarHidden</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIDeviceFamily</key>
+    <array>
+        <integer>1</integer>
+        <integer>2</integer>
+    </array>
+</dict>
+</plist>

+ 27 - 0
Platforms/iOS/LaunchScreen.storyboard

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS" />
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530" />
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb" />
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok" />
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600" />
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" />
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder" />
+            </objects>
+            <point key="canvasLocation" x="53" y="375" />
+        </scene>
+    </scenes>
+</document>

+ 31 - 0
Platforms/iOS/Program.cs

@@ -0,0 +1,31 @@
+using System;
+using Foundation;
+using UIKit;
+
+namespace iOS
+{
+    [Register("AppDelegate")]
+    class Program : UIApplicationDelegate
+    {
+        private static GameStateManagementSample.GameStateManagementGame game;
+
+        internal static void RunGame()
+        {
+            game = new GameStateManagementSample.GameStateManagementGame();
+            game.Run();
+        }
+
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        static void Main(string[] args)
+        {
+            UIApplication.Main(args, null, typeof(Program));
+        }
+
+        public override void FinishedLaunching(UIApplication app)
+        {
+            RunGame();
+        }
+    }
+}

+ 44 - 0
Platforms/iOS/iOS.csproj

@@ -0,0 +1,44 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0-ios</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <SupportedOSPlatformVersion>11.2</SupportedOSPlatformVersion>
+    <CodesignKey>iPhone Developer</CodesignKey>
+  </PropertyGroup>
+  <ItemGroup>
+    <None Remove="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\..\SampleCode\Game.cs" Link="Game.cs" />
+    <Compile Include="..\..\SampleCode\ScreenFactory.cs" Link="ScreenFactory.cs" />
+    <Compile Include="..\..\SampleCode\Screens\BackgroundScreen.cs" Link="Screens\BackgroundScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\Button.cs" Link="Screens\Button.cs" />
+    <Compile Include="..\..\SampleCode\Screens\GameplayScreen.cs" Link="Screens\GameplayScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\LoadingScreen.cs" Link="Screens\LoadingScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MainMenuScreen.cs" Link="Screens\MainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuEntry.cs" Link="Screens\MenuEntry.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MenuScreen.cs" Link="Screens\MenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\MessageBoxScreen.cs" Link="Screens\MessageBoxScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\OptionsMenuScreen.cs" Link="Screens\OptionsMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PauseMenuScreen.cs" Link="Screens\PauseMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMainMenuScreen.cs" Link="Screens\PhoneMainMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhoneMenuScreen.cs" Link="Screens\PhoneMenuScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PhonePauseScreen.cs" Link="Screens\PhonePauseScreen.cs" />
+    <Compile Include="..\..\SampleCode\Screens\PlayerIndexEventArgs.cs" Link="Screens\PlayerIndexEventArgs.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <MonoGameContentReference Include="..\..\ContentProject\Content\Content.mgcb" Link="Content\Content.mgcb" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
+    <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.1.303" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Screens\" />
+  </ItemGroup>
+  <Target Name="RestoreDotnetTools" BeforeTargets="Restore">
+    <Message Text="Restoring dotnet tools" Importance="High" />
+    <Exec Command="dotnet tool restore" />
+  </Target>
+  <Import Project="..\..\GameStateManagement\GameStateManagement.projitems" Label="Shared" />
+</Project>

+ 37 - 0
README.md

@@ -0,0 +1,37 @@
+# Game State Management (Mango, C#/VB)
+
+|Area|Submitted|Type|
+|-|-|-|
+|Games: Gameplay, User Experience|5/24/2011|Code Sample|
+
+## Description
+
+This sample shows how to manage the transitions among menus and gameplay states.
+
+## Sample Overview
+
+### Updated to MonoGame 3.8.1
+
+This sample provides a simple game screen management system, ready to be used as a starting point for games on Windows Phone, Xbox 360, or Windows. Additionally the sample includes example games for Windows Phone as well as Windows/Xbox 360, showing how you can build different, targeted experiences for each platform while using the same base code. Each experience is different to emphasize the versatility of the core library to create different experiences.
+
+The ScreenManager class is a reusable component that maintains a stack of one or more GameScreen instances. It coordinates the transitions from one screen to another, and takes care of routing user input to whichever screen is on top of the stack.
+
+Each screen class (including the actual gameplay, which is just another screen) derives from GameScreen. This provides Update, HandleInput, and Draw methods, plus some logic for managing the transition state. GameScreen doesn't actually implement any transition rendering effects, however: it merely provides information such as "you are currently 30% of the way through transitioning off," leaving it up to the derived screen classes to do something sensible with that information in their drawing code. This makes it easy for screens to implement different visual effects on top of the same underlying transition infrastructure.
+
+The ScreenManager works with an IScreenFactory to store away a list of the current screens and bring them back when the game resumes. Each GameScreen has methods for Activate and Deactivate that can be used to retrieve and store additional state about the game so you can bring the user back exactly where they were.
+
+> All content and source code downloaded from this page are bound to the Microsoft Permissive License (Ms-PL).
+
+## Downloads for the original XNA downloads below
+
+Download | Size | Description
+---|---|---|
+[GSMSample_4_0_Mango](https://github.com/simondarksidej/XNAGameStudio/tree/archive/Samples/GSMSample_4_0_Mango) | 0.16MB | Source code and assets for the Game State Management Sample (XNA Game Studio 4.0 Refresh - Mango).
+[GSMSample_4_0_Mango.zip](https://github.com/simondarksidej/XNAGameStudioZips/raw/zips/GSMSample_4_0_Mango.zip) | 0.16MB | Source code and assets for the Game State Management Sample (XNA Game Studio 4.0 Refresh - Mango).
+[GSMSample_4_0_Mango_VB](https://github.com/simondarksidej/XNAGameStudio/tree/archive/Samples/GSMSample_4_0_Mango_VB) | 0.16MB | Source code and assets for the Visual Basic version of the Game State Management Sample (XNA Game Studio 4.0 Refresh - Mango).
+[GSMSample_4_0_Mango_VB.zip](https://github.com/simondarksidej/XNAGameStudioZips/raw/zips/GSMSample_4_0_Mango_VB.zip) | 0.16MB | Source code and assets for the Visual Basic version of the Game State Management Sample (XNA Game Studio 4.0 Refresh - Mango).
+[GSMSample_4_0_PHONE](https://github.com/simondarksidej/XNAGameStudio/tree/archive/Samples/GSMSample_4_0_PHONE) | 0.12MB | Source code and assets for the Windows Phone version of the Game State Management Sample (XNA Game Studio 4.0).
+[GSMSample_4_0_PHONE.zip](https://github.com/simondarksidej/XNAGameStudioZips/raw/zips/GSMSample_4_0_PHONE.zip) | 0.12MB | Source code and assets for the Windows Phone version of the Game State Management Sample (XNA Game Studio 4.0).
+[GSMSample_4_0_WIN_XBOX](https://github.com/simondarksidej/XNAGameStudio/tree/archive/Samples/GSMSample_4_0_WIN_XBOX) | 0.15MB | Source code and assets for the Windows/Xbox 360 version of the Game State Management Sample (XNA Game Studio 4.0).
+[GSMSample_4_0_WIN_XBOX.zip](https://github.com/simondarksidej/XNAGameStudioZips/raw/zips/GSMSample_4_0_WIN_XBOX.zip) | 0.15MB | Source code and assets for the Windows/Xbox 360 version of the Game State Management Sample (XNA Game Studio 4.0).
+||||

+ 133 - 0
SampleCode/Game.cs

@@ -0,0 +1,133 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Game.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Sample showing how to manage different game states, with transitions
+    /// between menu screens, a loading screen, the game itself, and a pause
+    /// menu. This main game class is extremely simple: all the interesting
+    /// stuff happens in the ScreenManager component.
+    /// </summary>
+    public class GameStateManagementGame : Microsoft.Xna.Framework.Game
+    {
+        GraphicsDeviceManager graphics;
+        ScreenManager screenManager;
+        ScreenFactory screenFactory;
+
+        /// <summary>
+        /// The main game constructor.
+        /// </summary>
+        public GameStateManagementGame()
+        {
+            Content.RootDirectory = "Content";
+
+            graphics = new GraphicsDeviceManager(this);
+            TargetElapsedTime = TimeSpan.FromTicks(333333);
+
+#if WINDOWS_PHONE
+            graphics.IsFullScreen = true;
+
+            // Choose whether you want a landscape or portait game by using one of the two helper functions.
+            InitializeLandscapeGraphics();
+            // InitializePortraitGraphics();
+#endif
+
+            // Create the screen factory and add it to the Services
+            screenFactory = new ScreenFactory();
+            Services.AddService(typeof(IScreenFactory), screenFactory);
+
+            // Create the screen manager component.
+            screenManager = new ScreenManager(this);
+            Components.Add(screenManager);
+
+#if WINDOWS_PHONE
+            // Hook events on the PhoneApplicationService so we're notified of the application's life cycle
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.Launching += 
+                new EventHandler<Microsoft.Phone.Shell.LaunchingEventArgs>(GameLaunching);
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.Activated += 
+                new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(GameActivated);
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.Deactivated += 
+                new EventHandler<Microsoft.Phone.Shell.DeactivatedEventArgs>(GameDeactivated);
+#else
+            // On Windows and Xbox we just add the initial screens
+            AddInitialScreens();
+#endif
+        }
+
+        private void AddInitialScreens()
+        {
+            // Activate the first screens.
+            screenManager.AddScreen(new BackgroundScreen(), null);
+
+            // We have different menus for Windows Phone to take advantage of the touch interface
+#if WINDOWS_PHONE
+            screenManager.AddScreen(new PhoneMainMenuScreen(), null);
+#else
+            screenManager.AddScreen(new MainMenuScreen(), null);
+#endif
+        }
+
+        /// <summary>
+        /// This is called when the game should draw itself.
+        /// </summary>
+        protected override void Draw(GameTime gameTime)
+        {
+            graphics.GraphicsDevice.Clear(Color.Black);
+
+            // The real drawing happens inside the screen manager component.
+            base.Draw(gameTime);
+        }
+
+#if WINDOWS_PHONE
+        /// <summary>
+        /// Helper method to the initialize the game to be a portrait game.
+        /// </summary>
+        private void InitializePortraitGraphics()
+        {
+            graphics.PreferredBackBufferWidth = 480;
+            graphics.PreferredBackBufferHeight = 800;
+        }
+
+        /// <summary>
+        /// Helper method to initialize the game to be a landscape game.
+        /// </summary>
+        private void InitializeLandscapeGraphics()
+        {
+            graphics.PreferredBackBufferWidth = 800;
+            graphics.PreferredBackBufferHeight = 480;
+        }
+
+        void GameLaunching(object sender, Microsoft.Phone.Shell.LaunchingEventArgs e)
+        {
+            AddInitialScreens();
+        }
+
+        void GameActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
+        {
+            // Try to deserialize the screen manager
+            if (!screenManager.Activate(e.IsApplicationInstancePreserved))
+            {
+                // If the screen manager fails to deserialize, add the initial screens
+                AddInitialScreens();
+            }
+        }
+
+        void GameDeactivated(object sender, Microsoft.Phone.Shell.DeactivatedEventArgs e)
+        {
+            // Serialize the screen manager when the game deactivated
+            screenManager.Deactivate();
+        }
+#endif
+    }
+}

+ 44 - 0
SampleCode/ScreenFactory.cs

@@ -0,0 +1,44 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ScreenFactory.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using GameStateManagement;
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Our game's implementation of IScreenFactory which can handle creating the screens
+    /// when resuming from being tombstoned.
+    /// </summary>
+    public class ScreenFactory : IScreenFactory
+    {
+        public GameScreen CreateScreen(Type screenType)
+        {
+            // All of our screens have empty constructors so we can just use Activator
+            return Activator.CreateInstance(screenType) as GameScreen;
+
+            // If we had more complex screens that had constructors or needed properties set,
+            // we could do that before handing the screen back to the ScreenManager. For example
+            // you might have something like this:
+            //
+            // if (screenType == typeof(MySuperGameScreen))
+            // {
+            //     bool value = GetFirstParameter();
+            //     float value2 = GetSecondParameter();
+            //     MySuperGameScreen screen = new MySuperGameScreen(value, value2);
+            //     return screen;
+            // }
+            //
+            // This lets you still take advantage of constructor arguments yet participate in the
+            // serialization process of the screen manager. Of course you need to save out those
+            // values when deactivating and read them back, but that means either IsolatedStorage or
+            // using the PhoneApplicationService.Current.State dictionary.
+        }
+    }
+}

+ 114 - 0
SampleCode/Screens/BackgroundScreen.cs

@@ -0,0 +1,114 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BackgroundScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// The background screen sits behind all the other menu screens.
+    /// It draws a background image that remains fixed in place regardless
+    /// of whatever transitions the screens on top of it may be doing.
+    /// </summary>
+    class BackgroundScreen : GameScreen
+    {
+        #region Fields
+
+        ContentManager content;
+        Texture2D backgroundTexture;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public BackgroundScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen. The background texture is quite
+        /// big, so we use our own local ContentManager to load it. This allows us
+        /// to unload before going from the menus into the game itself, wheras if we
+        /// used the shared ContentManager provided by the Game class, the content
+        /// would remain loaded forever.
+        /// </summary>
+        public override void Activate(bool instancePreserved)
+        {
+            if (!instancePreserved)
+            {
+                if (content == null)
+                    content = new ContentManager(ScreenManager.Game.Services, "Content");
+
+                backgroundTexture = content.Load<Texture2D>("background");
+            }
+        }
+
+
+        /// <summary>
+        /// Unloads graphics content for this screen.
+        /// </summary>
+        public override void Unload()
+        {
+            content.Unload();
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the background screen. Unlike most screens, this should not
+        /// transition off even if it has been covered by another screen: it is
+        /// supposed to be covered, after all! This overload forces the
+        /// coveredByOtherScreen parameter to false in order to stop the base
+        /// Update method wanting to transition off.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, false);
+        }
+
+
+        /// <summary>
+        /// Draws the background screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Rectangle fullscreen = new Rectangle(0, 0, viewport.Width, viewport.Height);
+
+            spriteBatch.Begin();
+
+            spriteBatch.Draw(backgroundTexture, fullscreen,
+                             new Color(TransitionAlpha, TransitionAlpha, TransitionAlpha));
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 191 - 0
SampleCode/Screens/Button.cs

@@ -0,0 +1,191 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Button.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// A special button that handles toggling between "On" and "Off"
+    /// </summary>
+    class BooleanButton : Button
+    {
+        private string option;
+        private bool value;
+
+        /// <summary>
+        /// Creates a new BooleanButton.
+        /// </summary>
+        /// <param name="option">The string text to display for the option.</param>
+        /// <param name="value">The initial value of the button.</param>
+        public BooleanButton(string option, bool value)
+            : base(option)
+        {
+            this.option = option;
+            this.value = value;
+
+            GenerateText();
+        }
+
+        protected override void OnTapped()
+        {
+            // When tapped we need to toggle the value and regenerate the text
+            value = !value;
+            GenerateText();
+
+            base.OnTapped();
+        }
+
+        /// <summary>
+        /// Helper that generates the actual Text value the base class uses for drawing.
+        /// </summary>
+        private void GenerateText()
+        {
+            Text = string.Format("{0}: {1}", option, value ? "On" : "Off");
+        }
+    }
+
+    /// <summary>
+    /// Represents a touchable button.
+    /// </summary>
+    class Button
+    {
+        /// <summary>
+        /// The text displayed in the button.
+        /// </summary>
+        public string Text = "Button";
+
+        /// <summary>
+        /// The position of the top-left corner of the button.
+        /// </summary>
+        public Vector2 Position = Vector2.Zero;
+
+        /// <summary>
+        /// The size of the button.
+        /// </summary>
+        public Vector2 Size = new Vector2(250, 75);
+
+        /// <summary>
+        /// The thickness of the border drawn for the button.
+        /// </summary>
+        public int BorderThickness = 4;
+
+        /// <summary>
+        /// The color of the button border.
+        /// </summary>
+        public Color BorderColor = new Color(200, 200, 200);
+
+        /// <summary>
+        /// The color of the button background.
+        /// </summary>
+        public Color FillColor = new Color(100, 100, 100) * .75f;
+
+        /// <summary>
+        /// The color of the text.
+        /// </summary>
+        public Color TextColor = Color.White;
+
+        /// <summary>
+        /// The opacity of the button.
+        /// </summary>
+        public float Alpha = 0f;
+
+        /// <summary>
+        /// Invoked when the button is tapped.
+        /// </summary>
+        public event EventHandler<EventArgs> Tapped;
+        
+        /// <summary>
+        /// Creates a new Button.
+        /// </summary>
+        /// <param name="text">The text to display in the button.</param>
+        public Button(string text)
+        {
+            Text = text;
+        }
+
+        /// <summary>
+        /// Invokes the Tapped event and allows subclasses to perform actions when tapped.
+        /// </summary>
+        protected virtual void OnTapped()
+        {
+            if (Tapped != null)
+                Tapped(this, EventArgs.Empty);
+        }
+
+        /// <summary>
+        /// Passes a tap location to the button for handling.
+        /// </summary>
+        /// <param name="tap">The location of the tap.</param>
+        /// <returns>True if the button was tapped, false otherwise.</returns>
+        public bool HandleTap(Vector2 tap)
+        {
+            if (tap.X >= Position.X &&
+                tap.Y >= Position.Y &&
+                tap.X <= Position.X + Size.X &&
+                tap.Y <= Position.Y + Size.Y)
+            {
+                OnTapped();
+                return true;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Draws the button
+        /// </summary>
+        /// <param name="screen">The screen drawing the button</param>
+        public void Draw(GameScreen screen)
+        {
+            // Grab some common items from the ScreenManager
+            SpriteBatch spriteBatch = screen.ScreenManager.SpriteBatch;
+            SpriteFont font = screen.ScreenManager.Font;
+            Texture2D blank = screen.ScreenManager.BlankTexture;
+
+            // Compute the button's rectangle
+            Rectangle r = new Rectangle(
+                (int)Position.X,
+                (int)Position.Y,
+                (int)Size.X,
+                (int)Size.Y);
+
+            // Fill the button
+            spriteBatch.Draw(blank, r, FillColor * Alpha);
+
+            // Draw the border
+            spriteBatch.Draw(
+                blank, 
+                new Rectangle(r.Left, r.Top, r.Width, BorderThickness),
+                BorderColor * Alpha);
+            spriteBatch.Draw(
+                blank, 
+                new Rectangle(r.Left, r.Top, BorderThickness, r.Height),
+                BorderColor * Alpha);
+            spriteBatch.Draw(
+                blank, 
+                new Rectangle(r.Right - BorderThickness, r.Top, BorderThickness, r.Height),
+                BorderColor * Alpha);
+            spriteBatch.Draw(
+                blank, 
+                new Rectangle(r.Left, r.Bottom - BorderThickness, r.Width, BorderThickness),
+                BorderColor * Alpha);
+
+            // Draw the text centered in the button
+            Vector2 textSize = font.MeasureString(Text);
+            Vector2 textPosition = new Vector2(r.Center.X, r.Center.Y) - textSize / 2f;
+            textPosition.X = (int)textPosition.X;
+            textPosition.Y = (int)textPosition.Y;
+            spriteBatch.DrawString(font, Text, textPosition, TextColor * Alpha);
+        }
+    }
+}

+ 272 - 0
SampleCode/Screens/GameplayScreen.cs

@@ -0,0 +1,272 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameplayScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Threading;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// This screen implements the actual game logic. It is just a
+    /// placeholder to get the idea across: you'll probably want to
+    /// put some more interesting gameplay in here!
+    /// </summary>
+    class GameplayScreen : GameScreen
+    {
+        #region Fields
+
+        ContentManager content;
+        SpriteFont gameFont;
+
+        Texture2D ship;
+
+        Vector2 playerPosition = new Vector2(100, 100);
+        Vector2 enemyPosition = new Vector2(100, 100);
+
+        Random random = new Random();
+
+        float pauseAlpha;
+
+        InputAction pauseAction;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public GameplayScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(1.5);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+
+            pauseAction = new InputAction(
+                new Buttons[] { Buttons.Start, Buttons.Back },
+                new Keys[] { Keys.Escape },
+                true);
+        }
+
+
+        /// <summary>
+        /// Load graphics content for the game.
+        /// </summary>
+        public override void Activate(bool instancePreserved)
+        {
+            if (!instancePreserved)
+            {
+                if (content == null)
+                    content = new ContentManager(ScreenManager.Game.Services, "Content");
+
+                gameFont = content.Load<SpriteFont>("gamefont");
+                ship = content.Load<Texture2D>("blueships1");
+
+                // A real game would probably have more content than this sample, so
+                // it would take longer to load. We simulate that by delaying for a
+                // while, giving you a chance to admire the beautiful loading screen.
+#if !WINDOWS_UAP
+                Thread.Sleep(1000);
+#endif
+                // once the load has finished, we use ResetElapsedTime to tell the game's
+                // timing mechanism that we have just finished a very long frame, and that
+                // it should not try to catch up.
+                ScreenManager.Game.ResetElapsedTime();
+            }
+
+#if WINDOWS_PHONE
+            if (Microsoft.Phone.Shell.PhoneApplicationService.Current.State.ContainsKey("PlayerPosition"))
+            {
+                playerPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"];
+                enemyPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"];
+            }
+#endif
+        }
+
+
+        public override void Deactivate()
+        {
+#if WINDOWS_PHONE
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"] = playerPosition;
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"] = enemyPosition;
+#endif
+
+            base.Deactivate();
+        }
+
+
+        /// <summary>
+        /// Unload graphics content used by the game.
+        /// </summary>
+        public override void Unload()
+        {
+            content.Unload();
+
+#if WINDOWS_PHONE
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("PlayerPosition");
+            Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("EnemyPosition");
+#endif
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the state of the game. This method checks the GameScreen.IsActive
+        /// property, so the game will stop updating when the pause menu is active,
+        /// or if you tab away to a different application.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, false);
+
+            // Gradually fade in or out depending on whether we are covered by the pause screen.
+            if (coveredByOtherScreen)
+                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
+            else
+                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
+
+            if (IsActive)
+            {
+                // Apply some random jitter to make the enemy move around.
+                const float randomization = 10;
+
+                enemyPosition.X += (float)(random.NextDouble() - 0.5) * randomization;
+                enemyPosition.Y += (float)(random.NextDouble() - 0.5) * randomization;
+
+                // Apply a stabilizing force to stop the enemy moving off the screen.
+                Vector2 targetPosition = new Vector2(
+                    ScreenManager.GraphicsDevice.Viewport.Width / 2 - gameFont.MeasureString("Insert Gameplay Here").X / 2, 
+                    200);
+
+                enemyPosition = Vector2.Lerp(enemyPosition, targetPosition, 0.05f);
+
+                // TODO: this game isn't very fun! You could probably improve
+                // it by inserting something more interesting in this space :-)
+            
+            }
+        }
+
+
+        /// <summary>
+        /// Lets the game respond to player input. Unlike the Update method,
+        /// this will only be called when the gameplay screen is active.
+        /// </summary>
+        public override void HandleInput(GameTime gameTime, InputState input)
+        {
+            if (input == null)
+                throw new ArgumentNullException("input");
+
+            // Look up inputs for the active player profile.
+            int playerIndex = (int)ControllingPlayer.Value;
+
+            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
+            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];
+
+            // The game pauses either if the user presses the pause button, or if
+            // they unplug the active gamepad. This requires us to keep track of
+            // whether a gamepad was ever plugged in, because we don't want to pause
+            // on PC if they are playing with a keyboard and have no gamepad at all!
+            bool gamePadDisconnected = !gamePadState.IsConnected &&
+                                       input.GamePadWasConnected[playerIndex];
+
+            PlayerIndex player;
+            if (pauseAction.Evaluate(input, ControllingPlayer, out player) || gamePadDisconnected)
+            {
+#if WINDOWS_PHONE
+                ScreenManager.AddScreen(new PhonePauseScreen(), ControllingPlayer);
+#else
+                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
+#endif
+            }
+            else
+            {
+                // Otherwise move the player position.
+                Vector2 movement = Vector2.Zero;
+
+                if (keyboardState.IsKeyDown(Keys.Left))
+                    movement.X--;
+
+                if (keyboardState.IsKeyDown(Keys.Right))
+                    movement.X++;
+
+                if (keyboardState.IsKeyDown(Keys.Up))
+                    movement.Y--;
+
+                if (keyboardState.IsKeyDown(Keys.Down))
+                    movement.Y++;
+
+                Vector2 thumbstick = gamePadState.ThumbSticks.Left;
+
+                movement.X += thumbstick.X;
+                movement.Y -= thumbstick.Y;
+
+                if (input.TouchState.Count > 0)
+                {
+                    Vector2 touchPosition = input.TouchState[0].Position;
+                    Vector2 direction = touchPosition - playerPosition;
+                    direction.Normalize();
+                    movement += direction;
+                }
+
+                if (movement.Length() > 1)
+                    movement.Normalize();
+
+                playerPosition += movement * 8f;
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the gameplay screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // This game has a blue background. Why? Because!
+            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
+                                               Color.CornflowerBlue, 0, 0);
+
+            // Our player and enemy are both actually just text strings.
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+
+            spriteBatch.Begin();
+
+            spriteBatch.DrawString(gameFont, "// TODO", playerPosition, Color.Green);
+
+            spriteBatch.DrawString(gameFont, "Insert Gameplay Here",
+                                   enemyPosition, Color.DarkRed);
+
+            spriteBatch.Draw(ship, playerPosition, Color.White);
+
+            spriteBatch.End();
+
+            // If the game is transitioning on or off, fade it out to black.
+            if (TransitionPosition > 0 || pauseAlpha > 0)
+            {
+                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);
+
+                ScreenManager.FadeBackBufferToBlack(alpha);
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 163 - 0
SampleCode/Screens/LoadingScreen.cs

@@ -0,0 +1,163 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LoadingScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// The loading screen coordinates transitions between the menu system and the
+    /// game itself. Normally one screen will transition off at the same time as
+    /// the next screen is transitioning on, but for larger transitions that can
+    /// take a longer time to load their data, we want the menu system to be entirely
+    /// gone before we start loading the game. This is done as follows:
+    /// 
+    /// - Tell all the existing screens to transition off.
+    /// - Activate a loading screen, which will transition on at the same time.
+    /// - The loading screen watches the state of the previous screens.
+    /// - When it sees they have finished transitioning off, it activates the real
+    ///   next screen, which may take a long time to load its data. The loading
+    ///   screen will be the only thing displayed while this load is taking place.
+    /// </summary>
+    class LoadingScreen : GameScreen
+    {
+        #region Fields
+
+        bool loadingIsSlow;
+        bool otherScreensAreGone;
+
+        GameScreen[] screensToLoad;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// The constructor is private: loading screens should
+        /// be activated via the static Load method instead.
+        /// </summary>
+        private LoadingScreen(ScreenManager screenManager, bool loadingIsSlow,
+                              GameScreen[] screensToLoad)
+        {
+            this.loadingIsSlow = loadingIsSlow;
+            this.screensToLoad = screensToLoad;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+        }
+
+
+        /// <summary>
+        /// Activates the loading screen.
+        /// </summary>
+        public static void Load(ScreenManager screenManager, bool loadingIsSlow,
+                                PlayerIndex? controllingPlayer,
+                                params GameScreen[] screensToLoad)
+        {
+            // Tell all the current screens to transition off.
+            foreach (GameScreen screen in screenManager.GetScreens())
+                screen.ExitScreen();
+
+            // Create and activate the loading screen.
+            LoadingScreen loadingScreen = new LoadingScreen(screenManager,
+                                                            loadingIsSlow,
+                                                            screensToLoad);
+
+            screenManager.AddScreen(loadingScreen, controllingPlayer);
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the loading screen.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // If all the previous screens have finished transitioning
+            // off, it is time to actually perform the load.
+            if (otherScreensAreGone)
+            {
+                ScreenManager.RemoveScreen(this);
+
+                foreach (GameScreen screen in screensToLoad)
+                {
+                    if (screen != null)
+                    {
+                        ScreenManager.AddScreen(screen, ControllingPlayer);
+                    }
+                }
+
+                // Once the load has finished, we use ResetElapsedTime to tell
+                // the  game timing mechanism that we have just finished a very
+                // long frame, and that it should not try to catch up.
+                ScreenManager.Game.ResetElapsedTime();
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the loading screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // If we are the only active screen, that means all the previous screens
+            // must have finished transitioning off. We check for this in the Draw
+            // method, rather than in Update, because it isn't enough just for the
+            // screens to be gone: in order for the transition to look good we must
+            // have actually drawn a frame without them before we perform the load.
+            if ((ScreenState == ScreenState.Active) &&
+                (ScreenManager.GetScreens().Length == 1))
+            {
+                otherScreensAreGone = true;
+            }
+
+            // The gameplay screen takes a while to load, so we display a loading
+            // message while that is going on, but the menus load very quickly, and
+            // it would look silly if we flashed this up for just a fraction of a
+            // second while returning from the game to the menus. This parameter
+            // tells us how long the loading is going to take, so we know whether
+            // to bother drawing the message.
+            if (loadingIsSlow)
+            {
+                SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+                SpriteFont font = ScreenManager.Font;
+
+                const string message = "Loading...";
+
+                // Center the text in the viewport.
+                Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+                Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+                Vector2 textSize = font.MeasureString(message);
+                Vector2 textPosition = (viewportSize - textSize) / 2;
+
+                Color color = Color.White * TransitionAlpha;
+
+                // Draw the text.
+                spriteBatch.Begin();
+                spriteBatch.DrawString(font, message, textPosition, color);
+                spriteBatch.End();
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 98 - 0
SampleCode/Screens/MainMenuScreen.cs

@@ -0,0 +1,98 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MainMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// The main menu screen is the first thing displayed when the game starts up.
+    /// </summary>
+    class MainMenuScreen : MenuScreen
+    {
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor fills in the menu contents.
+        /// </summary>
+        public MainMenuScreen()
+            : base("Main Menu")
+        {
+            // Create our menu entries.
+            MenuEntry playGameMenuEntry = new MenuEntry("Play Game");
+            MenuEntry optionsMenuEntry = new MenuEntry("Options");
+            MenuEntry exitMenuEntry = new MenuEntry("Exit");
+
+            // Hook up menu event handlers.
+            playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
+            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
+            exitMenuEntry.Selected += OnCancel;
+
+            // Add entries to the menu.
+            MenuEntries.Add(playGameMenuEntry);
+            MenuEntries.Add(optionsMenuEntry);
+            MenuEntries.Add(exitMenuEntry);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Event handler for when the Play Game menu entry is selected.
+        /// </summary>
+        void PlayGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, true, e.PlayerIndex,
+                               new GameplayScreen());
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Options menu entry is selected.
+        /// </summary>
+        void OptionsMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            ScreenManager.AddScreen(new OptionsMenuScreen(), e.PlayerIndex);
+        }
+
+
+#if !__IOS__
+        /// <summary>
+        /// When the user cancels the main menu, ask if they want to exit the sample.
+        /// </summary>
+        protected override void OnCancel(PlayerIndex playerIndex)
+        {
+            const string message = "Are you sure you want to exit this sample?";
+
+            MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(message);
+
+            confirmExitMessageBox.Accepted += ConfirmExitMessageBoxAccepted;
+
+            ScreenManager.AddScreen(confirmExitMessageBox, playerIndex);
+        }
+
+        /// <summary>
+        /// Event handler for when the user selects ok on the "are you sure
+        /// you want to exit" message box.
+        /// </summary>
+        void ConfirmExitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
+        {
+            ScreenManager.Game.Exit();
+        }
+#endif
+
+#endregion
+    }
+}

+ 192 - 0
SampleCode/Screens/MenuEntry.cs

@@ -0,0 +1,192 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuEntry.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Helper class represents a single entry in a MenuScreen. By default this
+    /// just draws the entry text string, but it can be customized to display menu
+    /// entries in different ways. This also provides an event that will be raised
+    /// when the menu entry is selected.
+    /// </summary>
+    class MenuEntry
+    {
+        #region Fields
+
+        /// <summary>
+        /// The text rendered for this entry.
+        /// </summary>
+        string text;
+
+        /// <summary>
+        /// Tracks a fading selection effect on the entry.
+        /// </summary>
+        /// <remarks>
+        /// The entries transition out of the selection effect when they are deselected.
+        /// </remarks>
+        float selectionFade;
+
+        /// <summary>
+        /// The position at which the entry is drawn. This is set by the MenuScreen
+        /// each frame in Update.
+        /// </summary>
+        Vector2 position;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Gets or sets the text of this menu entry.
+        /// </summary>
+        public string Text
+        {
+            get { return text; }
+            set { text = value; }
+        }
+
+
+        /// <summary>
+        /// Gets or sets the position at which to draw this menu entry.
+        /// </summary>
+        public Vector2 Position
+        {
+            get { return position; }
+            set { position = value; }
+        }
+
+
+        #endregion
+
+        #region Events
+
+
+        /// <summary>
+        /// Event raised when the menu entry is selected.
+        /// </summary>
+        public event EventHandler<PlayerIndexEventArgs> Selected;
+
+
+        /// <summary>
+        /// Method for raising the Selected event.
+        /// </summary>
+        protected internal virtual void OnSelectEntry(PlayerIndex playerIndex)
+        {
+            if (Selected != null)
+                Selected(this, new PlayerIndexEventArgs(playerIndex));
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new menu entry with the specified text.
+        /// </summary>
+        public MenuEntry(string text)
+        {
+            this.text = text;
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the menu entry.
+        /// </summary>
+        public virtual void Update(MenuScreen screen, bool isSelected, GameTime gameTime)
+        {
+            // there is no such thing as a selected item on Windows Phone, so we always
+            // force isSelected to be false
+#if WINDOWS_PHONE
+            isSelected = false;
+#endif
+
+            // When the menu selection changes, entries gradually fade between
+            // their selected and deselected appearance, rather than instantly
+            // popping to the new state.
+            float fadeSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 4;
+
+            if (isSelected)
+                selectionFade = Math.Min(selectionFade + fadeSpeed, 1);
+            else
+                selectionFade = Math.Max(selectionFade - fadeSpeed, 0);
+        }
+
+
+        /// <summary>
+        /// Draws the menu entry. This can be overridden to customize the appearance.
+        /// </summary>
+        public virtual void Draw(MenuScreen screen, bool isSelected, GameTime gameTime)
+        {
+            // there is no such thing as a selected item on Windows Phone, so we always
+            // force isSelected to be false
+#if WINDOWS_PHONE
+            isSelected = false;
+#endif
+
+            // Draw the selected entry in yellow, otherwise white.
+            Color color = isSelected ? Color.Yellow : Color.White;
+
+            // Pulsate the size of the selected menu entry.
+            double time = gameTime.TotalGameTime.TotalSeconds;
+            
+            float pulsate = (float)Math.Sin(time * 6) + 1;
+
+            float scale = 1 + pulsate * 0.05f * selectionFade;
+
+            // Modify the alpha to fade text out during transitions.
+            color *= screen.TransitionAlpha;
+
+            // Draw text, centered on the middle of each line.
+            ScreenManager screenManager = screen.ScreenManager;
+            SpriteBatch spriteBatch = screenManager.SpriteBatch;
+            SpriteFont font = screenManager.Font;
+
+            Vector2 origin = new Vector2(0, font.LineSpacing / 2);
+
+            spriteBatch.DrawString(font, text, position, color, 0,
+                                   origin, scale, SpriteEffects.None, 0);
+        }
+
+
+        /// <summary>
+        /// Queries how much space this menu entry requires.
+        /// </summary>
+        public virtual int GetHeight(MenuScreen screen)
+        {
+            return screen.ScreenManager.Font.LineSpacing;
+        }
+
+
+        /// <summary>
+        /// Queries how wide the entry is, used for centering on the screen.
+        /// </summary>
+        public virtual int GetWidth(MenuScreen screen)
+        {
+            return (int)screen.ScreenManager.Font.MeasureString(Text).X;
+        }
+
+
+        #endregion
+    }
+}

+ 267 - 0
SampleCode/Screens/MenuScreen.cs

@@ -0,0 +1,267 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuScreen.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Input;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Base class for screens that contain a menu of options. The user can
+    /// move up and down to select an entry, or cancel to back out of the screen.
+    /// </summary>
+    abstract class MenuScreen : GameScreen
+    {
+        #region Fields
+
+        List<MenuEntry> menuEntries = new List<MenuEntry>();
+        int selectedEntry = 0;
+        string menuTitle;
+
+        InputAction menuUp;
+        InputAction menuDown;
+        InputAction menuSelect;
+        InputAction menuCancel;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Gets the list of menu entries, so derived classes can add
+        /// or change the menu contents.
+        /// </summary>
+        protected IList<MenuEntry> MenuEntries
+        {
+            get { return menuEntries; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public MenuScreen(string menuTitle)
+        {
+            this.menuTitle = menuTitle;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+
+            menuUp = new InputAction(
+                new Buttons[] { Buttons.DPadUp, Buttons.LeftThumbstickUp }, 
+                new Keys[] { Keys.Up },
+                true);
+            menuDown = new InputAction(
+                new Buttons[] { Buttons.DPadDown, Buttons.LeftThumbstickDown },
+                new Keys[] { Keys.Down },
+                true);
+            menuSelect = new InputAction(
+                new Buttons[] { Buttons.A, Buttons.Start },
+                new Keys[] { Keys.Enter, Keys.Space },
+                true);
+            menuCancel = new InputAction(
+                new Buttons[] { Buttons.B, Buttons.Back },
+                new Keys[] { Keys.Escape },
+                true);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Responds to user input, changing the selected entry and accepting
+        /// or cancelling the menu.
+        /// </summary>
+        public override void HandleInput(GameTime gameTime, InputState input)
+        {
+            // For input tests we pass in our ControllingPlayer, which may
+            // either be null (to accept input from any player) or a specific index.
+            // If we pass a null controlling player, the InputState helper returns to
+            // us which player actually provided the input. We pass that through to
+            // OnSelectEntry and OnCancel, so they can tell which player triggered them.
+            PlayerIndex playerIndex;
+
+            // Move to the previous menu entry?
+            if (menuUp.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                selectedEntry--;
+
+                if (selectedEntry < 0)
+                    selectedEntry = menuEntries.Count - 1;
+            }
+
+            // Move to the next menu entry?
+            if (menuDown.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                selectedEntry++;
+
+                if (selectedEntry >= menuEntries.Count)
+                    selectedEntry = 0;
+            }
+
+            if (menuSelect.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                OnSelectEntry(selectedEntry, playerIndex);
+            }
+            else if (menuCancel.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                OnCancel(playerIndex);
+            }
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has chosen a menu entry.
+        /// </summary>
+        protected virtual void OnSelectEntry(int entryIndex, PlayerIndex playerIndex)
+        {
+            menuEntries[entryIndex].OnSelectEntry(playerIndex);
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has cancelled the menu.
+        /// </summary>
+        protected virtual void OnCancel(PlayerIndex playerIndex)
+        {
+            ExitScreen();
+        }
+
+
+        /// <summary>
+        /// Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
+        /// </summary>
+        protected void OnCancel(object sender, PlayerIndexEventArgs e)
+        {
+            OnCancel(e.PlayerIndex);
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Allows the screen the chance to position the menu entries. By default
+        /// all menu entries are lined up in a vertical list, centered on the screen.
+        /// </summary>
+        protected virtual void UpdateMenuEntryLocations()
+        {
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            // start at Y = 175; each X value is generated per entry
+            Vector2 position = new Vector2(0f, 175f);
+
+            // update each menu entry's location in turn
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                MenuEntry menuEntry = menuEntries[i];
+                
+                // each entry is to be centered horizontally
+                position.X = ScreenManager.GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
+
+                if (ScreenState == ScreenState.TransitionOn)
+                    position.X -= transitionOffset * 256;
+                else
+                    position.X += transitionOffset * 512;
+
+                // set the entry's position
+                menuEntry.Position = position;
+
+                // move down for the next entry the size of this entry
+                position.Y += menuEntry.GetHeight(this);
+            }
+        }
+
+
+        /// <summary>
+        /// Updates the menu.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // Update each nested MenuEntry object.
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                bool isSelected = IsActive && (i == selectedEntry);
+
+                menuEntries[i].Update(this, isSelected, gameTime);
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the menu.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // make sure our entries are in the right place before we draw them
+            UpdateMenuEntryLocations();
+
+            GraphicsDevice graphics = ScreenManager.GraphicsDevice;
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            SpriteFont font = ScreenManager.Font;
+
+            spriteBatch.Begin();
+
+            // Draw each menu entry in turn.
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                MenuEntry menuEntry = menuEntries[i];
+
+                bool isSelected = IsActive && (i == selectedEntry);
+
+                menuEntry.Draw(this, isSelected, gameTime);
+            }
+
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            // Draw the menu title centered on the screen
+            Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
+            Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
+            Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
+            float titleScale = 1.25f;
+
+            titlePosition.Y -= transitionOffset * 100;
+
+            spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
+                                   titleOrigin, titleScale, SpriteEffects.None, 0);
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 186 - 0
SampleCode/Screens/MessageBoxScreen.cs

@@ -0,0 +1,186 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MessageBoxScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using GameStateManagement;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// A popup message box screen, used to display "are you sure?"
+    /// confirmation messages.
+    /// </summary>
+    class MessageBoxScreen : GameScreen
+    {
+        #region Fields
+
+        string message;
+        Texture2D gradientTexture;
+
+        InputAction menuSelect;
+        InputAction menuCancel;
+
+        #endregion
+
+        #region Events
+
+        public event EventHandler<PlayerIndexEventArgs> Accepted;
+        public event EventHandler<PlayerIndexEventArgs> Cancelled;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor automatically includes the standard "A=ok, B=cancel"
+        /// usage text prompt.
+        /// </summary>
+        public MessageBoxScreen(string message)
+            : this(message, true)
+        { }
+
+
+        /// <summary>
+        /// Constructor lets the caller specify whether to include the standard
+        /// "A=ok, B=cancel" usage text prompt.
+        /// </summary>
+        public MessageBoxScreen(string message, bool includeUsageText)
+        {
+            const string usageText = "\nA button, Space, Enter = ok" +
+                                     "\nB button, Esc = cancel"; 
+            
+            if (includeUsageText)
+                this.message = message + usageText;
+            else
+                this.message = message;
+
+            IsPopup = true;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.2);
+            TransitionOffTime = TimeSpan.FromSeconds(0.2);
+
+            menuSelect = new InputAction(
+                new Buttons[] { Buttons.A, Buttons.Start },
+                new Keys[] { Keys.Space, Keys.Enter },
+                true);
+            menuCancel = new InputAction(
+                new Buttons[] { Buttons.B, Buttons.Back },
+                new Keys[] { Keys.Escape, Keys.Back },
+                true);
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen. This uses the shared ContentManager
+        /// provided by the Game class, so the content will remain loaded forever.
+        /// Whenever a subsequent MessageBoxScreen tries to load this same content,
+        /// it will just get back another reference to the already loaded data.
+        /// </summary>
+        public override void Activate(bool instancePreserved)
+        {
+            if (!instancePreserved)
+            {
+                ContentManager content = ScreenManager.Game.Content;
+                gradientTexture = content.Load<Texture2D>("gradient");
+            }
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Responds to user input, accepting or cancelling the message box.
+        /// </summary>
+        public override void HandleInput(GameTime gameTime, InputState input)
+        {
+            PlayerIndex playerIndex;
+
+            // We pass in our ControllingPlayer, which may either be null (to
+            // accept input from any player) or a specific index. If we pass a null
+            // controlling player, the InputState helper returns to us which player
+            // actually provided the input. We pass that through to our Accepted and
+            // Cancelled events, so they can tell which player triggered them.
+            if (menuSelect.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                // Raise the accepted event, then exit the message box.
+                if (Accepted != null)
+                    Accepted(this, new PlayerIndexEventArgs(playerIndex));
+
+                ExitScreen();
+            }
+            else if (menuCancel.Evaluate(input, ControllingPlayer, out playerIndex))
+            {
+                // Raise the cancelled event, then exit the message box.
+                if (Cancelled != null)
+                    Cancelled(this, new PlayerIndexEventArgs(playerIndex));
+
+                ExitScreen();
+            }
+        }
+
+
+        #endregion
+
+        #region Draw
+
+
+        /// <summary>
+        /// Draws the message box.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            SpriteFont font = ScreenManager.Font;
+
+            // Darken down any other screens that were drawn beneath the popup.
+            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
+
+            // Center the message text in the viewport.
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+            Vector2 textSize = font.MeasureString(message);
+            Vector2 textPosition = (viewportSize - textSize) / 2;
+
+            // The background includes a border somewhat larger than the text itself.
+            const int hPad = 32;
+            const int vPad = 16;
+
+            Rectangle backgroundRectangle = new Rectangle((int)textPosition.X - hPad,
+                                                          (int)textPosition.Y - vPad,
+                                                          (int)textSize.X + hPad * 2,
+                                                          (int)textSize.Y + vPad * 2);
+
+            // Fade the popup alpha during transitions.
+            Color color = Color.White * TransitionAlpha;
+
+            spriteBatch.Begin();
+
+            // Draw the background rectangle.
+            spriteBatch.Draw(gradientTexture, backgroundRectangle, color);
+
+            // Draw the message box text.
+            spriteBatch.DrawString(font, message, textPosition, color);
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 149 - 0
SampleCode/Screens/OptionsMenuScreen.cs

@@ -0,0 +1,149 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// OptionsMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// The options screen is brought up over the top of the main menu
+    /// screen, and gives the user a chance to configure the game
+    /// in various hopefully useful ways.
+    /// </summary>
+    class OptionsMenuScreen : MenuScreen
+    {
+        #region Fields
+
+        MenuEntry ungulateMenuEntry;
+        MenuEntry languageMenuEntry;
+        MenuEntry frobnicateMenuEntry;
+        MenuEntry elfMenuEntry;
+
+        enum Ungulate
+        {
+            BactrianCamel,
+            Dromedary,
+            Llama,
+        }
+
+        static Ungulate currentUngulate = Ungulate.Dromedary;
+
+        static string[] languages = { "C#", "French", "Deoxyribonucleic acid" };
+        static int currentLanguage = 0;
+
+        static bool frobnicate = true;
+
+        static int elf = 23;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public OptionsMenuScreen()
+            : base("Options")
+        {
+            // Create our menu entries.
+            ungulateMenuEntry = new MenuEntry(string.Empty);
+            languageMenuEntry = new MenuEntry(string.Empty);
+            frobnicateMenuEntry = new MenuEntry(string.Empty);
+            elfMenuEntry = new MenuEntry(string.Empty);
+
+            SetMenuEntryText();
+
+            MenuEntry back = new MenuEntry("Back");
+
+            // Hook up menu event handlers.
+            ungulateMenuEntry.Selected += UngulateMenuEntrySelected;
+            languageMenuEntry.Selected += LanguageMenuEntrySelected;
+            frobnicateMenuEntry.Selected += FrobnicateMenuEntrySelected;
+            elfMenuEntry.Selected += ElfMenuEntrySelected;
+            back.Selected += OnCancel;
+            
+            // Add entries to the menu.
+            MenuEntries.Add(ungulateMenuEntry);
+            MenuEntries.Add(languageMenuEntry);
+            MenuEntries.Add(frobnicateMenuEntry);
+            MenuEntries.Add(elfMenuEntry);
+            MenuEntries.Add(back);
+        }
+
+
+        /// <summary>
+        /// Fills in the latest values for the options screen menu text.
+        /// </summary>
+        void SetMenuEntryText()
+        {
+            ungulateMenuEntry.Text = "Preferred ungulate: " + currentUngulate;
+            languageMenuEntry.Text = "Language: " + languages[currentLanguage];
+            frobnicateMenuEntry.Text = "Frobnicate: " + (frobnicate ? "on" : "off");
+            elfMenuEntry.Text = "elf: " + elf;
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Event handler for when the Ungulate menu entry is selected.
+        /// </summary>
+        void UngulateMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            currentUngulate++;
+
+            if (currentUngulate > Ungulate.Llama)
+                currentUngulate = 0;
+
+            SetMenuEntryText();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Language menu entry is selected.
+        /// </summary>
+        void LanguageMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            currentLanguage = (currentLanguage + 1) % languages.Length;
+
+            SetMenuEntryText();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Frobnicate menu entry is selected.
+        /// </summary>
+        void FrobnicateMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            frobnicate = !frobnicate;
+
+            SetMenuEntryText();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Elf menu entry is selected.
+        /// </summary>
+        void ElfMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            elf++;
+
+            SetMenuEntryText();
+        }
+
+
+        #endregion
+    }
+}

+ 79 - 0
SampleCode/Screens/PauseMenuScreen.cs

@@ -0,0 +1,79 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PauseMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// The pause menu comes up over the top of the game,
+    /// giving the player options to resume or quit.
+    /// </summary>
+    class PauseMenuScreen : MenuScreen
+    {
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public PauseMenuScreen()
+            : base("Paused")
+        {
+            // Create our menu entries.
+            MenuEntry resumeGameMenuEntry = new MenuEntry("Resume Game");
+            MenuEntry quitGameMenuEntry = new MenuEntry("Quit Game");
+            
+            // Hook up menu event handlers.
+            resumeGameMenuEntry.Selected += OnCancel;
+            quitGameMenuEntry.Selected += QuitGameMenuEntrySelected;
+
+            // Add entries to the menu.
+            MenuEntries.Add(resumeGameMenuEntry);
+            MenuEntries.Add(quitGameMenuEntry);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Event handler for when the Quit Game menu entry is selected.
+        /// </summary>
+        void QuitGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            const string message = "Are you sure you want to quit this game?";
+
+            MessageBoxScreen confirmQuitMessageBox = new MessageBoxScreen(message);
+
+            confirmQuitMessageBox.Accepted += ConfirmQuitMessageBoxAccepted;
+
+            ScreenManager.AddScreen(confirmQuitMessageBox, ControllingPlayer);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the user selects ok on the "are you sure
+        /// you want to quit" message box. This uses the loading screen to
+        /// transition from the game back to the main menu screen.
+        /// </summary>
+        void ConfirmQuitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
+                                                           new MainMenuScreen());
+        }
+
+
+        #endregion
+    }
+}

+ 66 - 0
SampleCode/Screens/PhoneMainMenuScreen.cs

@@ -0,0 +1,66 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PhoneMainMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using Microsoft.Xna.Framework;
+using System;
+
+namespace GameStateManagementSample
+{
+    class PhoneMainMenuScreen : PhoneMenuScreen
+    {
+        public PhoneMainMenuScreen()
+            : base("Main Menu")
+        {
+            // Create a button to start the game
+            Button playButton = new Button("Play");
+            playButton.Tapped += playButton_Tapped;
+            MenuButtons.Add(playButton);
+
+            // Create two buttons to toggle sound effects and music. This sample just shows one way
+            // of making and using these buttons; it doesn't actually have sound effects or music
+            BooleanButton sfxButton = new BooleanButton("Sound Effects", true);
+            sfxButton.Tapped += sfxButton_Tapped;
+            MenuButtons.Add(sfxButton);
+
+            BooleanButton musicButton = new BooleanButton("Music", true);
+            musicButton.Tapped += musicButton_Tapped;
+            MenuButtons.Add(musicButton);
+        }
+
+        void playButton_Tapped(object sender, EventArgs e)
+        {
+            // When the "Play" button is tapped, we load the GameplayScreen
+            LoadingScreen.Load(ScreenManager, true, PlayerIndex.One, new GameplayScreen());
+        }
+
+        void sfxButton_Tapped(object sender, EventArgs e)
+        {
+            BooleanButton button = sender as BooleanButton;
+
+            // In a real game, you'd want to store away the value of 
+            // the button to turn off sounds here. :)
+        }
+
+        void musicButton_Tapped(object sender, EventArgs e)
+        {
+            BooleanButton button = sender as BooleanButton;
+
+            // In a real game, you'd want to store away the value of 
+            // the button to turn off music here. :)
+        }
+
+#if !__IOS__
+        protected override void OnCancel()
+        {
+            //ScreenManager.Game.Exit();
+            base.OnCancel();
+        }
+#endif
+    }
+}

+ 149 - 0
SampleCode/Screens/PhoneMenuScreen.cs

@@ -0,0 +1,149 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PhoneMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using System.Collections.Generic;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Provides a basic base screen for menus on Windows Phone leveraging the Button class.
+    /// </summary>
+    class PhoneMenuScreen : GameScreen
+    {
+        List<Button> menuButtons = new List<Button>();
+        string menuTitle;
+
+        InputAction menuCancel;
+
+        /// <summary>
+        /// Gets the list of buttons, so derived classes can add or change the menu contents.
+        /// </summary>
+        protected IList<Button> MenuButtons
+        {
+            get { return menuButtons; }
+        }
+
+        /// <summary>
+        /// Creates the PhoneMenuScreen with a particular title.
+        /// </summary>
+        /// <param name="title">The title of the screen</param>
+        public PhoneMenuScreen(string title)
+        {
+            menuTitle = title;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+
+            // Create the menuCancel action
+            menuCancel = new InputAction(new Buttons[] { Buttons.Back }, null, true);
+
+            // We need tap gestures to hit the buttons
+            EnabledGestures = GestureType.Tap;
+        }
+
+        public override void Activate(bool instancePreserved)
+        {
+            // When the screen is activated, we have a valid ScreenManager so we can arrange
+            // our buttons on the screen
+            float y = 140f;
+            float center = ScreenManager.GraphicsDevice.Viewport.Bounds.Center.X;
+            for (int i = 0; i < MenuButtons.Count; i++)
+            {
+                Button b = MenuButtons[i];
+
+                b.Position = new Vector2(center - b.Size.X / 2, y);
+                y += b.Size.Y * 1.5f;
+            }
+
+            base.Activate(instancePreserved);
+        }
+
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
+        {
+            // Update opacity of the buttons
+            foreach (Button b in menuButtons)
+            {
+                b.Alpha = TransitionAlpha;
+            }
+
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+        }
+
+        /// <summary>
+        /// An overrideable method called whenever the menuCancel action is triggered
+        /// </summary>
+        protected virtual void OnCancel() { }
+
+        public override void HandleInput(GameTime gameTime, InputState input)
+        {
+            // Test for the menuCancel action
+            PlayerIndex player;
+            if (menuCancel.Evaluate(input, ControllingPlayer, out player))
+            {
+                OnCancel();
+            }
+
+            // Read in our gestures
+            foreach (GestureSample gesture in input.Gestures)
+            {
+                // If we have a tap
+                if (gesture.GestureType == GestureType.Tap)
+                {
+                    // Test the tap against the buttons until one of the buttons handles the tap
+                    foreach (Button b in menuButtons)
+                    {
+                        if (b.HandleTap(gesture.Position))
+                            break;
+                    }
+                }
+            }
+
+            base.HandleInput(gameTime, input);
+        }
+
+        public override void Draw(GameTime gameTime)
+        {
+            GraphicsDevice graphics = ScreenManager.GraphicsDevice;
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            SpriteFont font = ScreenManager.Font;
+
+            spriteBatch.Begin();
+
+            // Draw all of the buttons
+            foreach (Button b in menuButtons)
+                b.Draw(this);
+
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            // Draw the menu title centered on the screen
+            Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
+            Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
+            Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
+            float titleScale = 1.25f;
+
+            titlePosition.Y -= transitionOffset * 100;
+
+            spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0, 
+                                   titleOrigin, titleScale, SpriteEffects.None, 0);
+
+            spriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+    }
+}

+ 57 - 0
SampleCode/Screens/PhonePauseScreen.cs

@@ -0,0 +1,57 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PhonePauseScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// A basic pause screen for Windows Phone
+    /// </summary>
+    class PhonePauseScreen : PhoneMenuScreen
+    {
+        public PhonePauseScreen()
+            : base("Paused")
+        {
+            // Create the "Resume" and "Exit" buttons for the screen
+
+            Button resumeButton = new Button("Resume");
+            resumeButton.Tapped += resumeButton_Tapped;
+            MenuButtons.Add(resumeButton);
+
+            Button exitButton = new Button("Exit");
+            exitButton.Tapped += exitButton_Tapped;
+            MenuButtons.Add(exitButton);
+        }
+
+        /// <summary>
+        /// The "Resume" button handler just calls the OnCancel method so that 
+        /// pressing the "Resume" button is the same as pressing the hardware back button.
+        /// </summary>
+        void resumeButton_Tapped(object sender, EventArgs e)
+        {
+            OnCancel();
+        }
+
+        /// <summary>
+        /// The "Exit" button handler uses the LoadingScreen to take the user out to the main menu.
+        /// </summary>
+        void exitButton_Tapped(object sender, EventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
+                                                           new PhoneMainMenuScreen());
+        }
+
+        protected override void OnCancel()
+        {
+            ExitScreen();
+            base.OnCancel();
+        }
+    }
+}

+ 42 - 0
SampleCode/Screens/PlayerIndexEventArgs.cs

@@ -0,0 +1,42 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PlayerIndexEventArgs.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace GameStateManagementSample
+{
+    /// <summary>
+    /// Custom event argument which includes the index of the player who
+    /// triggered the event. This is used by the MenuEntry.Selected event.
+    /// </summary>
+    class PlayerIndexEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public PlayerIndexEventArgs(PlayerIndex playerIndex)
+        {
+            this.playerIndex = playerIndex;
+        }
+
+
+        /// <summary>
+        /// Gets the index of the player who triggered this event.
+        /// </summary>
+        public PlayerIndex PlayerIndex
+        {
+            get { return playerIndex; }
+        }
+
+        PlayerIndex playerIndex;
+    }
+}