XNA Game Engine Tutorial Series: #2 – Engine, Content, and Services

In this chapter, we’re going to be building the main class of our engine, rightfully called “Engine”. Start off by adding a new file to your “Components” folder called “Engine.cs”. Add the following code to create a static class called “Engine”. The difference between this and all the other classes we’ve created so far is that it doesn’t have to be instantiated (using “new ObjectType();”). Instead we can get its members using “Engine.MemberName”. Here is the starting code:

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Innovation
{
    public static class Engine
    {
    }
}

We are going to add some member variables to the Engine class. Add the following to our class:

// The GraphicsDevice the engine is using
public static GraphicsDevice GraphicsDevice;

// The Engine's SpriteBatch
public static SpriteBatch SpriteBatch;

// The collection of GameScreens we are managinng
public static GameScreenCollection GameScreens = new GameScreenCollection();

// The current GameTime
public static GameTime GameTime;

// Whether the Engine has been initialized yet
public static bool IsInitialized = false;

Next we are going to create a method to initialize the engine. This method accepts an IGraphicsDeviceService object which helps us set up the Engine’s graphics. If initializing from the Game class, this is the GraphicsDeviceManager graphics that is created in the default Game template. The initialize method can be found below:

// Initializes the engine
public static void SetupEngine(IGraphicsDeviceService GraphicsDeviceService)
{
    // Setup the GraphicsDevice and SpriteBatch
    Engine.GraphicsDevice = GraphicsDeviceService.GraphicsDevice;
    Engine.SpriteBatch = new SpriteBatch(GraphicsDeviceService.GraphicsDevice);

    Engine.IsInitialized = true;
}

The next method we will need is an update method. This will update all the GameScreens who will then update their components. There is some complex logic in updating and drawing because screens are allowed to block drawing and updating of each other. The update accepts a GameTime to update the internal GameTime, as does the draw method.

// Update the engine, screens, and components
public static void Update(GameTime gameTime)
{
    // Update the game time
    Engine.GameTime = gameTime;

    // Create a temporary list
    List<GameScreen> updating = new List<GameScreen>();

    // Populate the temp list
    foreach (GameScreen screen in GameScreens)
        updating.Add(screen);

    // BlocksUpdate and OverrideUpdateBlocked login
    for (int i = GameScreens.Count - 1; i >= 0; i--)
        if (GameScreens[i].BlocksUpdate)
        {
            if (i > 0)
                for (int j = i - 1; j >= 0; j--)
                    if (!GameScreens[j].OverrideUpdateBlocked)
                        updating.Remove(GameScreens[j]);

            break;
        }

    // Update remaining components
    foreach (GameScreen screen in updating)
        if (screen.Initialized)
            screen.Update();

    // Clear list
    updating.Clear();

    // Repopulate list
    foreach (GameScreen screen in GameScreens)
        updating.Add(screen);

    // BlocksInput and OverrideInputBlocked login
    for (int i = GameScreens.Count - 1; i >= 0; i--)
        if (GameScreens[i].BlocksInput)
        {
            if (i > 0)
                for (int j = i - 1; j >= 0; j--)
                    if (!GameScreens[j].OverrideInputBlocked)
                        updating.Remove(GameScreens[j]);

            break;
        }

    // Set IsInputAllowed for all GameScreens
    foreach (GameScreen screen in GameScreens)
        if (!screen.InputDisabled)
            screen.IsInputAllowed = updating.Contains(screen);
        else
            screen.IsInputAllowed = false;
}

The draw method is very similar, we do some logic for BlocksDraw and for the type of components we are supposed to be drawing, and then we draw them.

// Draws the current collection of screens and components. Accepts a
// ComponentType to render
public static void Draw(GameTime gameTime, ComponentType RenderType)
{
    // Update the time, create a temp list
    Engine.GameTime = gameTime;
    List<GameScreen> drawing = new List<GameScreen>();

    // Clear the back buffer
    GraphicsDevice.Clear(Color.CornflowerBlue);

    // Populate the temp list if the screen is visible
    foreach (GameScreen screen in GameScreens)
        if (screen.Visible)
            drawing.Add(screen);

    // BlocksDraw and OverrideDrawBlocked logic
    for (int i = GameScreens.Count - 1; i >= 0; i--)
        if (GameScreens[i].BlocksDraw)
        {
            if (i > 0)
                for (int j = i - 1; j >= 0; j--)
                {
                    if (!GameScreens[j].OverrideDrawBlocked)
                        drawing.Remove(GameScreens[j]);
                }

            break;
        }

    // Draw the remaining screens
    foreach (GameScreen screen in drawing)
        if (screen.Initialized)
            screen.Draw(RenderType);
}

Next we are going to add an IServiceContainer object. This is an object that will keep track of objects called Service Providers. We can retrieve and store Service Providers by their type. For example, to get the GraphicsDeviceService, we would use “Engine.Services.GetService(typeof(IGraphicsDeviceService));”. Or, to store a ContentManager object called “content”, we would use “Engine.Services.AddService(typeof(ContentManager), content);”. We are going to create a class that inherits from IServiceContainer, which contains methods to add, remove, and get services. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Innovation
{
    public class IEServiceContainer : IServiceProvider
    {
        // Contains the service types and services
        Dictionary<Type, object> services = new Dictionary<Type, object>();

        // Add a new service
        public void AddService(Type Service, object Provider)
        {
            // If we already have this type of service provider, throw an
            // exception
            if (services.ContainsKey(Service))
                throw new Exception("The service container already has a "
            + "service provider of type " + Service.Name);

            // Otherwise, add it to the list
            this.services.Add(Service, Provider);
        }

        // Get a service from the service container
        public object GetService(Type Service)
        {
            // If we have this type of service, return it
            foreach (Type type in services.Keys)
                if (type == Service)
                    return services[type];

            // Otherwise, throw an exception
            throw new Exception("The service container does not contain "
            + "a service provider of type " + Service.Name);
        }

        // A shortcut way to get a service. The benefit here is that we
        // can specify the type in the brackets and also return the
        // service of that type. For example, instead of
        // "Camera cam = (Camera)Services.GetService(typeof(Camera));",
        // we can use "Camera cam = Services.GetService()"
        public T GetService<T>()
        {
            object result = GetService(typeof(T));

            if (result != null)
                return (T)result;

            return default(T);
        }

        // Removes a service provider from the container
        public void RemoveService(Type Service)
        {
            if (services.ContainsKey(Service))
                services.Remove(Service);
        }

        // Gets whether or not the container has a provider of this type
        public bool ContainsService(Type Service)
        {
            return services.ContainsKey(Service);
        }
    }
}

Now we need to create an instance of it in our Engine class. Add the following somewhere in the top of the Engine class:

// The engine's service container
public static IEServiceContainer Services;

And the following in the SetupEngine() method:

// Setup the service container and add the IGraphicsDeviceService to it
Engine.Services = new IEServiceContainer();
Engine.Services.AddService(typeof(IGraphicsDeviceService),
    GraphicsDeviceService);

Now we are going to create a custom ContentManager and add it to the Engine class. Our ContentManager will expand on XNA’s base ContentManager by allowing us to choose if we want to use the content cache (store loaded objects loaded so we don’t need to load the same asset twice), and allow us to unload specific pieces of content individually instead of unloading everything. Here is the code:

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;

namespace Innovation
{
    public class IEContentManager : ContentManager
    {
        // Do nothing in the constructor except inherit from ContentManager
        public IEContentManager(IServiceProvider serviceProvider)
            : base(serviceProvider) { }

        // Whether or not we should keep objects that have been loaded. This
        // way we can avoid loading assets multiple times. However, this may
        // lead to problems with multiple objects changing loaded data, such
        // as effects on a model
        public bool PreserveAssets = true;

        // Keep a list of disposable assets and loaded assets
        List<IDisposable> disposable = new List<IDisposable>();
        Dictionary<string, object> loaded = new Dictionary<string, object>();

        // Override loading of assets so we can use our own functionality
        public override T Load<T>(string assetName)
        {
            // Create a new instance of the requested asset
            T r = this.ReadAsset<T>(assetName, RecordIDisposable);

            // If we are holding on to loaded assets, add it to the list of
            // loaded assets
            if (PreserveAssets && !loaded.ContainsKey(assetName))
                loaded.Add(assetName, r);

            // Return the loaded asset
            return r;
        }

        // Internal method to record disposable assets
        void RecordIDisposable(IDisposable asset)
        {
            // If we are monitoring loaded assets, add it to the list of
            // disposable assets
            if (PreserveAssets)
                disposable.Add(asset);
        }

        // Unload all content
        public override void Unload()
        {
            // Dispose all disposable assets
            foreach (IDisposable disp in disposable)
                disp.Dispose();

            // Clear all loaded assets
            loaded.Clear();
            disposable.Clear();
        }

        // Unload a specific piece of content
        public void Unload(string assetName)
        {
            // If the asset has been loaded
            if (loaded.ContainsKey(assetName))
            {
                // If it is disposable, dispose it and take it off the
                // list of disposable content
                if (loaded[assetName] is IDisposable
                        && disposable.Contains((IDisposable)loaded[assetName]))
                {
                    IDisposable obj = disposable[
                        disposable.IndexOf((IDisposable)loaded[assetName])];

                    obj.Dispose();

                    disposable.Remove(obj);
                }

                // Take it off the list of loaded content
                loaded.Remove(assetName);
            }
        }
    }
}

Now we need to add an instance of our content manager to the Engine class. Add the following in the class near the other member declarations:

// The engine's content manager
public static IEContentManager Content;

Now initialize it by adding this at the end of SetupEngine():

// Setup the content manager using the service container
Engine.Content = new IEContentManager(Services);

Now we are going to do is add some GameScreens to the Engine class. The first GameScreen is called BackgroundScreen, and overrides BlocksDraw, BlocksUpdate, and BlocksInput. This way we can have components running in the background that may need to update or accept input, such as the input class. We will create another called DefaultScreen, and set it to the BackgroundScreen. This screen is the one that components will be created in unless they specify otherwise. We can change the DefaultScreen while running, so new components will be created on that screen instead. If the current DefaultScreen is disabled, it will be set back to the BackgroundScreen. So, add this to the class:

// GameScreen provided by the engine.
public static GameScreen BackgroundScreen;

// The GameScreen to set to new GameScreens when a screen is not specified
public static GameScreen DefaultScreen;

And add this in SetupEngine():

// Setup the background screen
BackgroundScreen = new GameScreen("Engine.BackgroundScreen");
BackgroundScreen.OverrideUpdateBlocked = true;
BackgroundScreen.OverrideDrawBlocked = true;
BackgroundScreen.OverrideInputBlocked = true;

// Set the default screen to the background screen so new screens will
// use it automatically unless told otherwise
DefaultScreen = BackgroundScreen;

The final thing we need to do is create the GameScreenCollection class. This is a class that keeps track of GameScreens. It inherits from KeyedCollection, so it can return a GameScreen by name, like so: “Engine.GameScreens["GameScreenName"]“. This is better than a list because with a list we would have to keep track of number IDs for the screens. It also has some logic to handle the resetting of Engine.DefaultScreen if it is removed. Here’s the code:

using System.Collections.ObjectModel;

namespace Innovation
{
    public class GameScreenCollection : KeyedCollection<string, GameScreen>
    {
        // Allow us to get a screen by name like so:
        // Engine.GameScreens["ScreenName"]
        protected override string GetKeyForItem(GameScreen item)
        {
            return item.Name;
        }

        protected override void RemoveItem(int index)
        {
            // Get the screen to be removed
            GameScreen screen = Items[index];

            // If this screen is the current default screen, set the
            // default to the background screen
            if (Engine.DefaultScreen == screen)
                Engine.DefaultScreen = Engine.BackgroundScreen;

            base.RemoveItem(index);
        }
    }
}

Well, thats all for this tutorial! Dont be dissappointed though, we are now done with our engine framework! In the next tutorial we will actually start working on some game components!

Download the Code for this Chapter

Back to Game Engine Tutorial Series

19 Responses to “XNA Game Engine Tutorial Series: #2 – Engine, Content, and Services” »

  1. Comment by Florian Oeser — October 12, 2008 @ 5:29 am

    Hi there,
    nice implementation of your Content Manager!Thats one of the missing thing in my engine :D

  2. Comment by KaBaL — October 13, 2008 @ 8:57 am

    Issues:
    1) The beginning of this post seems to be a little goofed up. (large gap before the code section starts)
    2) The download for the code is an empty archive.
    3) Where does the GameScreenCollection come from? I haven’t seen any code for this class yet.(maybe you haven’t covered this yet)

  3. Comment by Sean James — October 13, 2008 @ 10:48 pm

    1) Fixed
    2) Fixed
    3) You’re right, I updated the post to include this. (Fixed :) )

    Thanks for your feedback!

  4. Comment by KaBaL — October 14, 2008 @ 7:57 am

    Issues: (cont)

    Found another item. In the Update and Draw methods – in the article you are creating a List when it should be List.

    This is correct in your download, just not in the article.

  5. Comment by KaBaL — October 14, 2008 @ 7:59 am

    Sorry the comment stripped the greater/less than > < symbols.

    the List type is incorrect. The article has “Component” where it should be “GameScreen”

  6. Comment by Sean James — October 14, 2008 @ 2:23 pm

    All better

  7. Comment by Liort — November 11, 2008 @ 1:21 am

    // The Engine’s SpriteBatch
    public static SpriteBatch SpriteBatch;

    Is it ok to define your variable name to be the same as the class name?

  8. Comment by Sean James — November 11, 2008 @ 10:55 am

    Yeah, its okay to do that. The only time it becomes a problem is if you have an instance with the same name in a class and you try to access a static function or property from the actual Type. For example, if you had an instance called “Vector3 Vector3″ and you wanted to use “Vector3.Dot()”, the instance would get in the way. In that case you would have to use “Microsoft.Xna.Framework.Vector3.Dot()”. But because we won’t usually be accessing the SpriteBatch from inside Engine, it’s ok to call it “SpriteBatch”.

  9. Pingback by » XNA Game Engine Tutorial Series #6 - Input — November 21, 2008 @ 11:56 pm

    [...] Series #4 – DrawOrder XNA Game Engine Tutorial Series #3 – Utilities, Components, Camera, Actor XNA Game Engine Tutorial Series: #2 – Engine, Content, and Services XNA Game Engine Tutorial Series: #1 – Component and [...]

  10. Comment by Ilya Ostrovskiy — November 23, 2008 @ 11:45 am

    Very, very nice! I’m loving these tutorials so far, however, in the ServicesManager class, I added a genericized version of AddService, similar to how you did in the ContentManager.Load

    All you have to do is Engine.Services.AddService(Provider), saving you a couple keystrokes so you don’t have to surround the type with typeof().

    Here’s the code for the genericized AddService:

    ///
    /// A really neat shortcut for AddService
    ///
    /// The type of service that provider will provide
    /// The actual provider
    public void AddService(object Provider)
    {
    Type Service = typeof(T);

    // if we already have a provider of this type, throw
    // an exception
    if (services.ContainsKey(Service))
    throw new Exception(“The service container already has a provider of type ”
    + Service.Name + “.”);

    // otherwise add it to the list
    services.Add(Service, Provider);
    }

  11. Comment by Sean James — November 23, 2008 @ 12:30 pm

    That would be good for basic service providers, but it doesn’t work so well when you are trying to be less specific about what type is actually providing the service. For example, later on we have components that require a Camera. However, we would more likely have an FPSCamera providing the service since Camera is very basic (no input). So, we would have a problem when the component went to look for Camera and it wasn’t there. So, be careful using this method.

  12. Comment by A dude — December 24, 2008 @ 5:53 pm

    First, i really like these tutorials!

    What i really miss in the tutorial is the the big picture. Something like an UML-Diagram or something. A place where i can see how all these things connect together. That would be cool :)

    Oh, and i have 2 small questions: Why exactly did you made the Engine class static?
    I mean if you have to initialize it, why don’t you use a constructor?

    And the other thing is: Is it normal to start all function and variable names with capitals? In the file “Game1.cs” the variables name first letter is lowercase. I am just wondering because i have never seen a piece of code where function/variable names where “ucfirst” :) . Is that a C# special? Okay, so far ive only seen C, PHP, Java and JS code…
    i just wanna make it right and learn the standard-definition of C#.

  13. Comment by Roonda — January 10, 2009 @ 4:59 am

    Hi Sean, thanks for the really nice tutorial series. It’s a well thought out structure and I definitely plan to borrow some of your ideas :)

    One thing on your custom ContentManager: when you Unload(string assetName), you only dispose the actual asset (if it is IDisposable). The problem is that RecordIDisposable(IDisposable asset) will get called on internal IDisposables like the VertexBuffer and IndexBuffer of a Model. The result is that unloading an asset won’t necessarily free all of it’s memory to be garbage collected – particularly bad if you repeatedly load/unload the same asset.

    I managed to solve this hairy issue in my ContentTracker class by using reference counting and a Stack (http://www.ziggyware.com/readarticle.php?article_id=231 if anyone’s interested).

  14. Comment by Jon McClure — January 21, 2009 @ 3:32 pm

    Thanks you for sharing your experience. There is an obvious boat load of work in these tutorials, and it means a lot to the community that you’re posting. Please, keep it up!

    I’m hoping you can clear something up for me. In the ContentManager’s overridden Load function, it doesn’t appear as though you are actually returning cached assets if they’re already loaded. Shouldn’t the Load function look something like this:

    // Override loading of assets so we can use our own functionality
    public override T Load(string assetName)
    {
    // If we are holding assets, and we have already loaded this
    // one, grab it from the list and return it
    if (PreserveAssets && loaded.ContainsKey(assetName))
    return (T)loaded["assetName"];

    // Create a new instance of the requested asset
    T r = this.ReadAsset(assetName, RecordIDisposable);

    // If we are holding on to loaded assets, add it to the list of
    // loaded assets
    if (PreserveAssets && !loaded.ContainsKey(assetName))
    loaded.Add(assetName, r);

    // Return the loaded asset
    return r;
    }

    Please correct me if I missed something.

  15. Comment by Chris Harshman — February 9, 2009 @ 10:28 am

    // Clear the back buffer
    GraphicsDevice.Clear(Color.CornflowerBlue);

    This line in the Engine.Draw() should not be there.

    Doing so will override any Background color you set with Screens in the Game Class.

  16. Comment by Chris Harshman — February 9, 2009 @ 11:06 am

    Forget that last comment, I was streamlining the code to my tastes, changing alot of variables to Properties and I forgot to add in the BackgroundScreen was Visble.

  17. Comment by haxpor — March 26, 2009 @ 3:33 am

    Thanks for your great tutorials.
    I can adapt it to create my own one. ;)

    Only one idea so far that I admit myself I will stick to it, is ‘StateManagement’. FMHO: it will give less for-loop and give more flexible across game’s components.

    Anyway though it’s up to the app’s complexity.

    Thanks

  18. Comment by Nick — July 21, 2009 @ 4:34 am

    Sorry if this question sounds a bit newbie, but I am one.

    Do all t5he classes; Engine.cs, IEServiceContainer.cs, IEContentManager.cs, and GameScreensCollection.cs all go into the components folder?

    I’m a bit unsure about these folders as to what they do, and what they mean. Can anyone help?

    thanks in advance
    Nick

  19. Comment by Ruben — July 28, 2010 @ 8:05 am

    You can put them in a folder if you want to, you wil have to drag them into one and set the namespace at the top of each file to [yourEngineName].[yourFolderName].

    And if you want to use an object that’s in another folder, you add a using statement: “using [yourEngineName].[yourFolderName]“

RSS feed for comments on this post. TrackBack URI

Leave a comment