Thursday, March 01, 2007

Enabling-Disabling GameComponents when a game is Activated-Deactivated

Updated Again: Reformatted source again. Should be the last time. Check out this post for details.

Updated: Removed line numbers in code snippets after some constructive criticism from ziggy.

For my GameComponents, I wanted a graceful way to Disabled and then Re-Enable them when the Game is Activated and when it is Deactivated. The important thing for me was to Disable all of the GameComponents when the Game gets Deactivated and then when the Game is Activated, I want to Re-Enable the GameComponents that were already Enabled prior to the Game being Deactivated.

The best way I saw to do this, was to extend the GameComponent class. So, that's what I have done for all (not that there is a lot yet, only 2 so far) of my custom GameComponents.

So, here you'll find the code that I used to accomplish this. Maybe in a future version of XNA this will be included into the GameComponent class.

All of the code on this site is subject to this license:

  1. /*
  2. * Copyright (c) 2007, Eric Lebetsamer (http://tehone.com)
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights to
  7. * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8. * the Software, and to permit persons to whom the Software is furnished to do so,
  9. * subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in all copies
  12. * or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  15. * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  16. * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  17. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  18. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  19. * USE OR OTHER DEALINGS IN THE SOFTWARE.
  20. */


Ok, now on to the code. (code license)
I chose to first create an Interface. I created this because I needed to extend both GameComponent and also DrawableGameComponent, but I wanted to extend both of them in the same way and wanted to make sure each new extended class would adhere to a set of rules about its structure. If you are not familiar with Interfaces, see this great post on jsedlak.org for an introduction about them.

So, let's start with the Interface code. It's a pretty simple Interface, not much going on. Remember, we are only extending the GameComponent class to add 3 things: a way to keep track of the previous Enabled state, an EventHandler for Game.Activated, and an EventHandler for Game.Deactivated. Ideally, by using an Interface, I leave the ability to easily add other features later if needed and still make sure that my new extended classes adhere to a structure.
Show Plain Text
Show Line #
Alternating Lines
Hide Comments
  1. using System;
  2.   
  3. namespace TehOne.Xna.GameComponents
  4. {
  5.     /// <summary>
  6.     /// This <c>interface</c> is used to help extend the base <see cref="Microsoft.Xna.Framework.GameComponent"/> object.
  7.     /// <para>We add the <see cref="PreviouslyEnabled"/> field and an <see cref="System.EventHandler"/> method for handling the <see cref="Microsoft.Xna.Framework.GameComponent.EnabledChanged"/> event.</para>
  8.     /// </summary>
  9.     public interface IXnaGameComponent
  10.     {
  11.         /// <summary>
  12.         /// The PreviouslyEnabled property represents the previous value of <see cref="Microsoft.Xna.Framework.GameComponent.Enabled"/> for this <see cref="IXnaGameComponent"/>.
  13.         /// </summary>
  14.         bool PreviouslyEnabled
  15.         {
  16.             get;
  17.             set;
  18.         }
  19.   
  20.         /// <summary>
  21.         /// This method is the <see cref="System.EventHandler"/> for the <see cref="Microsoft.Xna.Framework.Game.Activated"/> event.
  22.         /// </summary>
  23.         /// <param name="sender">The <see cref="System.Object"/> that called this event.</param>
  24.         /// <param name="e">The arguments that were passed into this event.</param>
  25.         void Game_Activated(object sender, EventArgs args);
  26.   
  27.         /// <summary>
  28.         /// This method is the <see cref="System.EventHandler"/> for the <see cref="Microsoft.Xna.Framework.Game.Deactivated"/> event.
  29.         /// </summary>
  30.         /// <param name="sender">The <see cref="System.Object"/> that called this event.</param>
  31.         /// <param name="e">The arguments that were passed into this event.</param>
  32.         void Game_Deactivated(object sender, EventArgs args);
  33.     }
  34. }


Ok, so from here, we need to create 2 classes that implement the Interface we just created and extend a base class from the XNA framework. One that extends GameComponent and one that extends DrawableGameComponent. The 2 new classes are basically exactly the same. The only difference is the base class which they extend.

Show Plain Text
Show Line #
Alternating Lines
Hide Comments
  1. using System;
  2. using Microsoft.Xna.Framework;
  3.   
  4. namespace TehOne.Xna.GameComponents
  5. {
  6.     /// <summary>
  7.     /// This <c>partial class</c> is used to extend the base <see cref="Microsoft.Xna.Framework.DrawableGameComponent"/> object.
  8.     /// <para>Implements the members of <see cref="IXnaGameComponent"/>.</para>
  9.     /// </summary>
  10.     public partial class XnaDrawableGameComponent : Microsoft.Xna.Framework.DrawableGameComponent, IXnaGameComponent
  11.     {
  12.         private bool _previouslyEnabled = true;
  13.   
  14.         /// <summary>
  15.         /// The PreviouslyEnabled property represents the previous value of <see cref="Microsoft.Xna.Framework.GameComponent.Enabled"/> for this <see cref="Microsoft.Xna.Framework.GameComponent"/>.
  16.         /// </summary>
  17.         /// <value>The PreviouslyEnabled property gets/sets the <see cref="_previouslyEnabled"/> data member.</value>
  18.         /// <remarks>The value of the <see cref="_previouslyEnabled"/> data member is set when the <see cref="OnEnabledChange(object, EventArgs)"/> event is fired.</remarks>
  19.         public bool PreviouslyEnabled
  20.         {
  21.             get
  22.             {
  23.                 return _previouslyEnabled;
  24.             }
  25.             set
  26.             {
  27.                 _previouslyEnabled = value;
  28.             }
  29.         }
  30.   
  31.         /// <summary>
  32.         /// The main constructor for <see cref="XnaDrawableGameComponent" />
  33.         /// </summary>
  34.         /// <param name="game">The <see cref="Microsoft.Xna.Framework.Game" /> instance.</param>
  35.         public XnaDrawableGameComponent(Game game)
  36.             : base(game)
  37.         {
  38.             game.Activated += new EventHandler(Game_Activated);
  39.             game.Deactivated += new EventHandler(Game_Deactivated);
  40.         }
  41.   
  42.         /// <summary>
  43.         /// This method is the <see cref="System.EventHandler"/> for the <see cref="Microsoft.Xna.Framework.Game.Activated"/> event.
  44.         /// </summary>
  45.         /// <param name="sender">The <see cref="System.Object"/> that called this event.</param>
  46.         /// <param name="e">The arguments that were passed into this event.</param>
  47.         /// <remarks>When this <see cref="System.EventHandler"/> is fired, we set our <see cref="Microsoft.Xna.Framework.GameComponent.Enabled"/> value to be the value of the <see cref="_previouslyEnabled"/> data member.</remarks>
  48.         public void Game_Activated(object sender, EventArgs e)
  49.         {
  50.             this.Enabled = _previouslyEnabled;
  51.         }
  52.   
  53.         /// <summary>
  54.         /// This method is the <see cref="System.EventHandler"/> for the <see cref="Microsoft.Xna.Framework.Game.Deactivated"/> event.
  55.         /// </summary>
  56.         /// <param name="sender">The <see cref="System.Object"/> that called this event.</param>
  57.         /// <param name="e">The arguments that were passed into this event.</param>
  58.         /// <remarks>When this <see cref="System.EventHandler"/> is fired, we set our <see cref="_previouslyEnabled"/> data member value to be the value of <see cref="Microsoft.Xna.Framework.GameComponent.Enabled"/>. Then, we set our <see cref="Microsoft.Xna.Framework.GameComponent.Enabled"/> state to <b><c>false</c></b>.</remarks>
  59.         public void Game_Deactivated(object sender, EventArgs e)
  60.         {
  61.             _previouslyEnabled = this.Enabled;
  62.             this.Enabled = false;
  63.         }
  64.   
  65.         /// <summary>
  66.         /// Allows the game component to perform any initialization it needs to before starting
  67.         /// to run.  This is where it can query for any required services and load content.
  68.         /// </summary>
  69.         public override void Initialize()
  70.         {
  71.             _previouslyEnabled = this.Enabled;
  72.   
  73.             base.Initialize();
  74.         }
  75.     }
  76. }


Ok, so that is all we need. Now, when we create a custom GameComponent or DrawableGameComponent we will just have it extend from one of these 2 new classes. As an example:
Show Plain Text
Show Line #
Alternating Lines
Hide Comments
  1. public partial class Framerate : XnaDrawableGameComponent


That's all there is too it. Now any GameComponent that use XnaGameComponent as it's base class, or any DrawableGameComponent that use XnaDrawableGameComponent as it's base class, will automatically be disabled when the Game is Deactivated and they will re-enable (if they were enabled before the Deactivate) themselves when the Game is Activated.


I hope this code proves usefull for others. Any comments/suggestions/feedback?