| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- #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
- }
- }
|