Part 4: The Engine

Well I know its only been a couple of hours since my last post but since we haven’t really got anything working yet and there is errors in the code due to missing classes, I thought I would get this one up as soon as possible.

 

So lets get started.

The Engine Class this is the main class of the engine so I have named it Engine as I thought that was the most logical name.

Lets start by adding a new class called engine to the root of our folder structure.

Here is the starting code

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine.Components;
using Engine.Services;
using Engine.GameScreens;

namespace Engine
{
    public static class Engine
    {

        public static GraphicsDevice GraphicsDevice;
        public static SpriteBatch SpriteBatch;
        public static GameScreenCollection GameScreens = new GameScreenCollection();
        public static GameTime GameTime;
        public static bool IsInitialized = false;
        public static IEServiceContainer Services;
        public static IEContentManager Content;
        public static GameScreen BackgroundScreen;
        public static GameScreen DefaultScreen;

        /// <summary>
        /// Setups the engine.
        /// </summary>
        /// <param name="GraphicsDeviceService">The graphics device service.</param>
        public static void SetupEngine(IGraphicsDeviceService GraphicsDeviceService)
        {

            Engine.GraphicsDevice = GraphicsDeviceService.GraphicsDevice;
            Engine.SpriteBatch = new SpriteBatch(GraphicsDeviceService.GraphicsDevice);

            Engine.IsInitialized = true;

            Engine.Services = new IEServiceContainer();
            Engine.Services.AddService(typeof(IGraphicsDeviceService), GraphicsDeviceService);

            Engine.Content = new IEContentManager(Services);

            BackgroundScreen = new GameScreen("Engine.BackgroundScreen");
            BackgroundScreen.OverrideUpdateBlocked = true;
            BackgroundScreen.OverrideDrawBlocked = true;
            BackgroundScreen.OverrideInputBlocked = true;

            DefaultScreen = BackgroundScreen;
        }

        /// <summary>
        /// Updates the specified game time.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        public static void Update(GameTime gameTime)
        {
            Engine.GameTime = gameTime;
            List<GameScreen> updating = new List<GameScreen>();
            foreach (GameScreen screen in GameScreens)
                updating.Add(screen);
            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;
                }

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

            updating.Clear();

            foreach (GameScreen screen in GameScreens)
                updating.Add(screen);

            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;
                }

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

        /// <summary>
        /// Draws the specified game time.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        /// <param name="RenderType">Type of the render.</param>
        public static void Draw(GameTime gameTime, ComponentType RenderType)
        {
            Engine.GameTime = gameTime;
            List<GameScreen> drawing = new List<GameScreen>();

            GraphicsDevice.Clear(Color.CornflowerBlue);

            foreach (GameScreen screen in GameScreens)
                if (screen.Visible)
                    drawing.Add(screen);

            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;
                }

            foreach (GameScreen screen in drawing)
                if (screen.Initialized)
                    screen.Draw(RenderType);
        }
    }
}

So by adding this code to our engine we have got ride of all our error and got 8 new ones but that’s ok we will get rid of then by the end of this tutorial.
The main difference  between this class and the other class we have created so far is that we don’t have to instantiate it, instead we can get its members using Engine.MemberName.
Next we will add a new class that Implements IServiceProvider. This is a object that will keep track Service providers by inheriting from IServiceProvider.

So with in the Services folder in our solution add a new class called IEServiceContainer.

So here is the code

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

namespace Engine.Services
{
    public class IEServiceContainer : IServiceProvider
    {

        Dictionary<Type, object> services = new Dictionary<Type, object>();

        /// <summary>
        /// Adds the service.
        /// </summary>
        /// <param name="Service">The service.</param>
        /// <param name="Provider">The provider.</param>
        public void AddService(Type Service, object Provider)
        {
            if (services.ContainsKey(Service))
                throw new Exception("The service container already has a service provider of type " + Service.Name);
            this.services.Add(Service, Provider);
        }

        /// <summary>
        /// Gets the service.
        /// </summary>
        /// <param name="Service">The service.</param>
        /// <returns></returns>
        public object GetService(Type Service)
        {
            foreach (Type type in services.Keys)
                if (type == Service)
                    return services[type];

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

        /// <summary>
        /// Gets the service.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetService<T>()
        {
            object result = GetService(typeof(T));

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

            return default(T);
        }

        /// <summary>
        /// Removes the service.
        /// </summary>
        /// <param name="Service">The service.</param>
        public void RemoveService(Type Service)
        {
            if (services.ContainsKey(Service))
                services.Remove(Service);
        }

        /// <summary>
        /// Determines whether the specified service contains service.
        /// </summary>
        /// <param name="Service">The service.</param>
        /// <returns>
        ///   <c>true</c> if the specified service contains service; otherwise, <c>false</c>.
        /// </returns>
        public bool ContainsService(Type Service)
        {
            return services.ContainsKey(Service);
        }
    }
}

Next we are going to create a custom content manager expanding on the XNA’s base content manager, the main reason for this is so we can use cached content and also allow us to unload content insted of unloading everything.

So add a new class to services folder and call it IEContentManager

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

namespace Engine.Services
{
    public class IEContentManager : ContentManager
    {

        public IEContentManager(IServiceProvider serviceProvider)
            : base(serviceProvider) { }

        public bool PreserveAssets = true;

        List<IDisposable> disposable = new List<IDisposable>();
        Dictionary<string, object> loaded = new Dictionary<string, object>();

       public override T Load<T>(string assetName)
        {

            T r = this.ReadAsset<T>(assetName, RecordIDisposable);
            if (PreserveAssets && !loaded.ContainsKey(assetName))
                loaded.Add(assetName, r);
            return r;
        }

        void RecordIDisposable(IDisposable asset)
        {
            if (PreserveAssets)
                disposable.Add(asset);
        }

        public override void Unload()
        {
            foreach (IDisposable disp in disposable)
                disp.Dispose();
            loaded.Clear();
            disposable.Clear();
        }

        public void Unload(string assetName)
        {
            if (loaded.ContainsKey(assetName))
            {

                if (loaded[assetName] is IDisposable && disposable.Contains((IDisposable)loaded[assetName]))
                {
                    IDisposable obj = disposable[disposable.IndexOf((IDisposable)loaded[assetName])];
                    obj.Dispose();
                    disposable.Remove(obj);
                }
                loaded.Remove(assetName);
            }
        }
    }
}

Last but not least we are going to add a game screen collection class so we can have multiple game screens in our engine.

So we need to add a new class to our GameScreen folder and call it can you guess that’s right GameScreenCollection

using System.Collections.ObjectModel;

namespace Engine.GameScreens
{
    public class GameScreenCollection : KeyedCollection<string, GameScreen>
    {

        /// <summary>
        /// When implemented in a derived class, extracts the key from the specified element.
        /// </summary>
        /// <param name="item">The element from which to extract the key.</param>
        /// <returns>
        /// The key for the specified element.
        /// </returns>
        protected override string GetKeyForItem(GameScreen item)
        {
            return item.Name;
        }

        /// <summary>
        /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.KeyedCollection`2"/>.
        /// </summary>
        /// <param name="index">The index of the element to remove.</param>
        protected override void RemoveItem(int index)
        {
            GameScreen screen = Items[index];
            if (Engine.DefaultScreen == screen)
                Engine.DefaultScreen = Engine.BackgroundScreen;

            base.RemoveItem(index);
        }
    }
}

1 Comment

  1. Hot jobs says:

    Pretty nice post. I just stumbled upon your blog and wished to say that I have really enjoyed browsing26 your blog posts. In any case I’ll be subscribing to your feed and I hope you write again soon!…
    Welcome to my site Career advice ;-)

Leave a comment

Switch to our mobile site