Running on Empty

The few things I know, I like to share.

XNA Framework GameEngine Development. (Part 20, Plug-in Manager)

Introduction

Welcome to Part 20 of the XNA Framework GameEngine Development series.  This is the first article of my new game engine series Roe3. 

This article will focus on creating a plug-in architecture for game components and managers.

Creating the plug-in socket

While writing this series I found a few instances when I wanted to be able to turn off a component or manager in order to test or even to create a game that didn’t use all of bells and whistles that the engine had to offer.  I normally would go into the base engine code and remove the components rebuild and move on.  Of course this works, but it is not exactly a very elegant solution.

So I created a configuration management system that would allow me to instantiate managers and components using an xml file that looks something like the following.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <components>
    <component value="RoeEngine3.GameComponents.Cursor"/>
    <component value="RoeEngine3.GameComponents.FpsCounter"/>
    <component value="RoeEngine3.GameComponents.DebugOverlay"/>
  </components>
  <managers>
    <manager value="RoeEngine3.Managers.AudioManager"/>
    <manager value="RoeEngine3.Managers.ScreenManager"/>
  </managers>
</configuration>

Much nicer!  In this example there are 3 components and 2 managers that will be created as the engine starts up… without the need to recompile the engine.  Don’t want to show a cursor or fpscounter?  Simply remove the component from the config.

So all we need to do is create an xml reader for this file, something like the following.

namespace RoeEngine3.Managers
{
    [XmlType("configuration")]
    public sealed class ConfigurationManager
    {
        public static ConfigurationManager Instance { get; private set; }

        static ConfigurationManager()
        {
            if(Instance == null)
            {
                string file = @"Managers.config";

                if(!File.Exists(file))
                {
                    throw new FileNotFoundException();
                }

                using(StreamReader reader = new StreamReader(file))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(ConfigurationManager));
                    Instance = (ConfigurationManager)serializer.Deserialize(reader);
                }
            }
        }

        [XmlArray("managers"), XmlArrayItem("manager")]
        public Managers Managers { get; set; }

        [XmlArray("components"), XmlArrayItem("component")]
        public Components Components { get; set; }
    }

    public sealed class Components : KeyedCollection<string, Component>
    {
        protected override string GetKeyForItem(Component item)
        {
            return item.Value;
        }
    }

    public sealed class Managers : KeyedCollection<string, Manager>
    {
        protected override string GetKeyForItem(Manager item)
        {
            return item.Value;
        }
    }

    [XmlType("component")]
    public sealed class Component
    {
        [XmlAttribute("value")]
        public string Value { get; set; }
    }

    [XmlType("manager")]
    public sealed class Manager
    {
        [XmlAttribute("value")]
        public string Value { get; set; }
    }
}

Now we have a list of Managers and Components that we want to activate in the engine.  So in our base engine class we need to write some prep code for our managers and components.

        private void PrepareComponents()
        {
            foreach (Component component in ConfigurationManager.Instance.Components)
            {
                GameComponent baseComponent = ComponentActivator.CreateInstance(Type.GetType(component.Value)) as GameComponent;
                EngineManager.Game.Components.Add(baseComponent);
            }
        }
       
        private void PrepareManagers()
        {    
            foreach (Manager manager in ConfigurationManager.Instance.Managers)
            {
                GameComponent baseManager = ManagerActivator.CreateInstance(Type.GetType(manager.Value)) as GameComponent;              
                Components.Add(baseManager);
            }
        }
        protected override void Initialize()
        {
            PrepareManagers();
            PrepareComponents();

     ... // OTHER INITIALIZE STUFF GOES HERE
 }

Turn on the Power

All that is left for us to do is activate the managers and components.  Just need to create some reflection magic to invoke them.

    public sealed class ManagerActivator
    {
        public static object CreateInstance(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("Manager Type");
            }

            ConstructorInfo info = type.GetConstructor(new Type[] { typeof(Game) });

            if (info == null)
            {
                return null;
            }

            object instance;

            try
            {
                instance = info.Invoke(new object[] { EngineManager.Game });
            }
            catch (Exception ex)
            {
                if (ex is SystemException)
                {
                    throw;
                }
                instance = null;
            }
            return instance;
        }
    }

    public sealed class ComponentActivator
    {
        public static object CreateInstance(Type type)
        {
            return ManagerActivator.CreateInstance(type);
        }
    }

A few side notes here, if you notice the GetConstructor object passes in a Type array of type Game.  This is because all GameComponents have a constructor parameter of the Game.  Your components will obviously need to subscribe to the correct GetConstructor types.

Conclusion

In this article I outlined a simple method of creating a pluggable interface for components and managers.  This interface will allow us to turn on and off components and managers as we need them without writing additional code.

December 17, 2008 - Posted by | C#, GameComponent, XBOX360, XNA

22 Comments »

  1. Glad to see you are still cranking on this series. I do have some feedback on this particular point. You may want to consider using an existing dependency injection container to handle you componentization. You get a lot more features and flexibility out of the box if you use something like Windsor/SturctureMap/Ninject/Spring.NET. These are all open source, so you could recompile for XBox if you like.

    Comment by Rob | December 17, 2008 | Reply

  2. Thank you for the suggestion. I will have to take a look at some of these.

    Comment by roecode | December 17, 2008 | Reply

  3. Hi,

    You make a good jod, congratulation.
    Why do you say it’s you first article of the third version of your engine ? Do you repeat all ? Or you continued ?

    Thanks and good luck

    Comment by new.proger | December 19, 2008 | Reply

    • No, I wont be repeating any articles in the series. Even though this is a new engine… all of the previous articles stand. Of course, I plan to go back to all of the previous articles and do some major rewrites to improve performance. However, when I do those rewrites, they will be new articles.

      I do plan to get back into some more advanced topics, but for now I am focused on speed improvements and performance.

      Comment by roecode | December 19, 2008 | Reply

  4. Ok thanks for your quick reply.

    Comment by new.proger | December 19, 2008 | Reply

  5. Hi roecode

    Just a quick tip. It’s been some time since I used XNA so my tip might be useless, but as I recently developed a component for a project that used a lot of reflection and type checks for a custom serialization, I’ve noticed that you might add to your CreateInstance() or PrepareComponents() & PrepareManagers() code some type checks to avoid configuration errors.

    Example:
    public static object CreateInstance(Type type)
    {

    if (type.BaseType != typeof(GameComponent))
    {
    throw new ArgumentException(“Not a valid gamecomponent”);
    }

    }

    Just checks if the supplied type is derived from GameComponent.

    As I said this might be unnecessary or code might not be exactly that but something similar (maybe a “if !(type is GameComponent)” is enough), I don’t remember exactly how GameComponents worked when instancing them…

    Best regards and keep up the fantastic work!

    Comment by Kartones | December 29, 2008 | Reply

  6. I am very happy to see that you will go on with your excellent work.

    I am really interested to see you improvement!

    Thank’s and bye!

    Comment by Kenshin | January 4, 2009 | Reply

  7. WOW! I had no idea that doing that was even possible. There’s more to .net than I thought! I remember talking about a new engine that made some of the sections out dated, I really want to see what’s new/changed in version 3! I’m surprised at how much I understand, usually I get lost halfway through a tutorial, but it’s abstracted enough that I can follow the general function fairly well. Not much of the specific mathematical stuff, but it’s enough that I think I can safely add to the parts that I know to suit the project I am working on. Will there be new articles for the things that have changed, or will they just be improvements that are added to the next source release?

    Comment by META_Cog | February 9, 2009 | Reply

  8. Hey Roe,

    I am having some trouble with your code. Just mucking around with it, when I disable your atmosphere/skydome the text becomes partially culled or something. Im not really sure whats happening.

    Cant find anything in the code thats causing it

    Comment by sTeeL | February 25, 2009 | Reply

    • More than likely it is a RenderState staying in line only mode.

      Comment by roecode | February 25, 2009 | Reply

  9. Thanks Roe. Found out that removing the Spheres fixes that but I cant find where a Sphere sets the RenderState. Nor why using the SkyDome would stop that.

    Comment by sTeeL | February 25, 2009 | Reply

  10. Found the offending code and how to fix it. Thanks anyway

    Comment by sTeeL | February 26, 2009 | Reply

  11. HI~ RoeCode

    What is your roe3 Lane ContentPipeline how the texture should support?

    namespace Roe3EngineContentPipeline.Material
    {
    public class CompiledMaterial
    {
    public enum VariableType
    {
    Texture=0,—->>>>>????
    Matrix,
    Float4,
    Float3,
    Float
    }

    Comment by TianYu | March 9, 2009 | Reply

    • If I understand your question, which is probably unlikely. Texture support is offered by the TextureManager class.

      Comment by roecode | March 9, 2009 | Reply

  12. Great tutorial so far, just wondering if the game engine is still being worked on or if its been abandoned?

    Comment by J | May 19, 2009 | Reply

    • Still being worked on. I have been working on some games, so I have been a bit too busy to update recently.

      Comment by roecode | May 19, 2009 | Reply

  13. Would love to see a download at some point roecode.

    its kind of tricky to visualise the full thing.

    chances of?

    good work!

    Comment by simonjohnroberts | June 9, 2009 | Reply

    • I have released some code in later additions of the series. I do not have a build for this code since, I essentially am working on completing a game with the engine. I have gone back and redesigned quite a bit of the work and will continue to refactor.

      Comment by roecode | June 10, 2009 | Reply

  14. Just like to add my voice to many … THIS IS GREAT WORK.

    I am waiting for your next release of the game engine (XNA 3.1) and examples would be even more welcome.

    I’ve tried other engines, but the learning from your series has been a blast. Thank you for your time and effort. I certainly appriciate it.

    Comment by Kamran | July 7, 2009 | Reply

  15. I appreciate the feedback Kamran. Glad the series is helpful.

    Comment by roecode | July 7, 2009 | Reply

  16. please some examples and source code 😉
    thanks in advance

    Comment by david | July 10, 2009 | Reply

  17. Are you going to write more posts on this series?

    Comment by Christoph | October 22, 2010 | Reply


Leave a comment