FGF: Modules, Not Components

As you no doubt saw in the writing of the Application class, FGF implements its own version of the XNA Framework’s component classes. The first major reason for doing so is that the builtin component classes require you to pass in a (Framework) Game object through the constructor. While this dependency can be circumvented by implementing the interfaces directly and supplying a manual workaround, the attempt is exactly that: a workaround. The second major reason for my implementation is that by controlling the interfaces and base classes, I can easily support more advanced situations such as the separation between initialization and content loading/unloading. You will see more of this later on when I modify the classes to support asynchronous content loading.

For now, the focus is being put on building a robust base for developers to start working with my framework. Because the goal is to fix the XNA Framework’s implementation, we begin with a simplification of the IGameComponent, IUpdateable and IDrawable interfaces. We combine these three into a single, multi-purpose interface, IModule.

public interface IModule
{
    event EventHandler DrawOrderChanged;
    event EventHandler UpdateOrderChanged;

    void Initialize(IGame game);

    void Draw(GameTime gameTime);
    void Update(GameTime gameTime);

    int DrawOrder { get; }
    int UpdateOrder { get; }

    bool IsVisible { get; set; }
    bool IsEnabled { get; set; }
    bool IsInitialized { get; set; }

    IModule Parent { get; set; }
    ModuleCollection Modules { get; }
}

Although we have carried over many of the properties, methods and events, you should spot some key differences. Specifically the Initialize method now takes a single parameter, IGame, to remove the dependency on the Game class in the framework. Also added are the Parent and Modules properties that allow us to build a tree structure with the modules. This will become crucial to building advanced systems later on.

Before we can build the base class for the IModule interface, we need to build the ModuleCollection class. This class acts as a list of modules but adds added support for calling the specific methods of the IModule interface on each item in the list. This makes management of a large number of modules incredibly simple.

public class ModuleCollection : Collection
{
    private List drawList = new List();

The class inherits the custom Collection class we wrote earlier to gain access to the events which we will see shortly. Note that an internal list is kept so that we can sort modules for drawing differently. The main collection will keep the modules sorted based on their update order and this way we don’t have to sort each time the modules need to be updated and drawn.

    public T GetModule() where T : class, IModule
    {
        for (int i = 0; i < Count; i++)
            if (this[i] is T)
                return this[i] as T;

        return default(T);
    }

A generic method is supplied here to make the searching and casting of modules easier on the end developer. Next we provide Initialize, Draw and Update methods that do exactly as their names imply.

    public void Initialize(IGame game)
    {
        if (IsInitialized) return;

        Game = game;

        for (int i = 0; i < Count; i++)
            if(!this[i].IsInitialized)
                this[i].Initialize(game);

        IsInitialized = true;
    }

    public void Draw(GameTime gameTime)
    {
        for (int i = 0; i < drawList.Count; i++)
            if(drawList[i].IsVisible)
                drawList[i].Draw(gameTime);
    }

    public void Update(GameTime gameTime)
    {
        for (int i = 0; i < Count; i++)
            if(this[i].IsEnabled)
                this[i].Update(gameTime);
    }

When an item is added to the list, we have to do a few things. First we have to make sure it is a module. We have to add some event handlers to its events and then we have to add it to the lists and sort each one. If the main list has been initialized, we have to initialize the module itself.

    protected override void OnItemAdded(object sender, CollectionChangedEventArgs args)
    {
        IModule module = args.Item as IModule;

        if (module == null) return;

        module.DrawOrderChanged += OnModuleDrawOrderChanged;
        module.UpdateOrderChanged += OnModuleUpdateOrderChanged;

        if (IsInitialized && !module.IsInitialized)
            module.Initialize(Game);

        drawList.Add(module);
        drawList.Sort(DrawOrderComparer.Default);

        Sort(UpdateOrderComparer.Default);

        base.OnItemAdded(sender, args);
    }

    protected virtual void OnModuleUpdateOrderChanged(object sender, EventArgs e)
    {
        Sort(UpdateOrderComparer.Default);
    }

    protected virtual void OnModuleDrawOrderChanged(object sender, EventArgs e)
    {
        drawList.Sort(DrawOrderComparer.Default);
    }

Later, when a module is removed, we have to remove those event handlers and remove it from any internal list. Essentially this is the reverse process of adding, except that we do not have to resort the lists.

    protected override void OnItemRemoved(object sender, CollectionChangedEventArgs args)
    {
        IModule module = args.Item as IModule;

        if (module == null) return;

        module.DrawOrderChanged -= OnModuleDrawOrderChanged;
        module.UpdateOrderChanged -= OnModuleUpdateOrderChanged;

        drawList.Remove(module);

        base.OnItemRemoved(sender, args);
    }

And finally some properties to close off the class.

    protected IGame Game { get; private set; }
    protected bool IsInitialized { get; private set; }
}

Now that we have the ModuleCollection class, we can write the Module (base) class itself. The class is a basic implementation of the IModule interface and inherits the NotifyPropertyChangedBase class to support some advanced data & binding situations.

public class Module : NotifyPropertyChangedBase, IModule
{
    private int drawOrder = 0;
    private int updateOrder = 0;

    private bool isVisible = true;
    private bool isEnabled = true;

    private IModule parent = null;

    public event EventHandler DrawOrderChanged;
    public event EventHandler UpdateOrderChanged;

    public Module()
    {
        Modules = new ModuleCollection();

        Modules.ItemAdded += OnModuleAdded;
        Modules.ItemRemoved += OnModuleRemoved;
    }

When the DrawOrder or UpdateOrder properties are changed, we need to invoke their respective events.

    protected virtual void OnDrawOrderChanged(object sender, EventArgs args)
    {
        if (DrawOrderChanged != null) DrawOrderChanged.Invoke(sender, args);
    }

    protected virtual void OnUpdateOrderChanged(object sender, EventArgs args)
    {
        if (UpdateOrderChanged != null) UpdateOrderChanged.Invoke(sender, args);
    }

When a child module is added, we have to set its parent. Similarly when a module is removed, we have to unset the parent (if it hasn’t been manually changed).

    protected virtual void OnModuleAdded(object sender, CollectionChangedEventArgs args)
    {
        IModule module = args.Item as IModule;

        if (module == null) return;

        module.Parent = this;
    }

    protected virtual void OnModuleRemoved(object sender, CollectionChangedEventArgs args)
    {
        IModule module = args.Item as IModule;

        if (module == null) return;

        // First we check to ensure that the developer
        // hasn't set the parent manually to some other
        // object before we remove ourselves as parent.
        if(module.Parent == this)
            module.Parent = null;
    }

The Initialize, Draw and Update methods are incredibly straight forward. For the most part the respective method is called on the list of child modules.

    public virtual void Initialize(IGame game)
    {
        Game = game;

        Modules.Initialize(game);

        IsInitialized = true;
    }

    public virtual void Draw(GameTime gameTime)
    {
        Modules.Draw(gameTime);
    }

    public virtual void Update(GameTime gameTime)
    {
        Modules.Update(gameTime);
    }

And last but not least the properties.

    public int DrawOrder
    {
        get { return drawOrder; }
        set
        {
            drawOrder = value;

            OnDrawOrderChanged(this, EventArgs.Empty);
        }
    }

    public int UpdateOrder
    {
        get { return updateOrder; }
        set
        {
            updateOrder = value;

            OnUpdateOrderChanged(this, EventArgs.Empty);
        }
    }

    public bool IsVisible
    {
        get { return isVisible; }
        set { isVisible = value; }
    }

    public bool IsEnabled
    {
        get { return isEnabled; }
        set { isEnabled = value; }
    }

    public IModule Parent
    {
        get { return parent; }
        set { parent = value; }
    }

    public bool IsInitialized { get; protected set; }

    public ModuleCollection Modules { get; private set; }

    protected IGame Game { get; private set; }
}

Now that we have our base module class, we can start creating games without using the builtin component classes!

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>