Add a new folder and name it Framework, then add a new interface to the folder called INode and replace the code inside with this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XNAGameEngineData;

namespace KrakenEngine.Framework
{
    public interface INode : IGameObject
    {
        Component Parent
        {
            get;
        }
        RootNode Root
        {
            get;
        }
        double Version
        {
            get;
        }
        ComponentBuilder ComponentBuilder
        {
            get;
        }
        void AddComponent(Component component);
        void RemoveComponent(Component component);
        void RemoveComponent(string name);
        void AddModule(Module module);
        void RemoveModule(Module module);
        void RemoveModule(string name);
        Component GetComponent(string name);
        List<Component> GetComponents();
        List<Component> SearchComponents(string searchPattern);
        Component SearchForComponent(string searchPattern);
        T GetModule<T>() where T : Module;
        List<T> GetModules<T>() where T : Module;
        List<T> SearchModules<T>(string searchPattern) where T : Module;
        T SearchForModule<T>(string searchPattern) where T : Module;
        void SetModule<T>(T module) where T : Module;
        void SetDependencies();
    }
}

These are the property and method stubs that the Node class will have and the Module class will decorate.
Add any classes that this interface references to the Framework folder and leave them for now (make sure they're all public classes).

Next, the Node class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XNAGameEngineData;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace KrakenEngine.Framework
{
    public abstract class Node : INode
    {
        string _name;
        double _version;
        GameCollection<Module> _modules = new GameCollection<Module>();
        GameCollection<Component> _components = new GameCollection<Component>();
        List<Component> _tempComponents = new List<Component>();
        Component _parent;
        RootNode _root;
        ComponentBuilder _componentBuilder = new ComponentBuilder();

        public string Name
        {
            get { return _name; }
        }
        public virtual Component Parent
        {
            get { return _parent; }
            protected set { _parent = value; }
        }
        public virtual RootNode Root
        {
            get { return _root; }
            set { _root = value; }
       }
        public virtual double Version
        {
            get { return _version; }
        }
        public virtual ComponentBuilder ComponentBuilder
        {
            get { return _componentBuilder; }
        }

        public Node(string name, double version)
        {
            _name = name;
            _version = version;
        }

        public virtual void AddComponent(Component component)
        {
            _components.Add(component.Name, component);
            if (Root != null)
            {
                Root.AddToTree(component);
            }
            SetDependencies();
        }
        public virtual void RemoveComponent(Component component)
        {
            _components.Remove(component);
            if (Root != null)
            {
                Root.AddToTree(component);
            }
            SetDependencies();
        }
        public virtual void RemoveComponent(string name)
        {
            RemoveComponent(_components[name]);
        }
        public virtual void AddModule(Module module)
        {
            module.WrapNode(GetTopWrapper());
            _modules.Add(module.Name, module);
            SetDependencies();
        }
        public virtual void RemoveModule(Module module)
        {
            _modules.Remove(module);
            Rewrap();
            SetDependencies();
        }
        void Rewrap()
        {
            for (int i = 0; i < _modules.Count; i++)
            {
                _modules[i].WrapNode(i == 0 ? this as INode : _modules[i - 1]);
            }
        }
        public virtual void RemoveModule(string name)
        {
            RemoveModule(_modules[name]);
        }
        public virtual Component GetComponent(string name)
        {
            return _components.GetItem<Component>(name);
        }
        public virtual List<Component> GetComponents()
        {
            return _components.GetItems<Component>();
        }
        public virtual List<Component> SearchComponents(string searchPattern)
        {
            return _components.GetItems<Component>(searchPattern);
        }
        public virtual Component SearchForComponent(string searchPattern)
        {
            List<Component> matches = SearchComponents(searchPattern);
            return matches.Count > 0 ? matches[0] : null;
        }
        public virtual T GetModule<T>() where T : Module
        {
            return _modules.GetItem<T>();
        }
        public virtual List<T> GetModules<T>() where T : Module
        {
            return _modules.GetItems<T>();
        }
        public virtual List<T> SearchModules<T>(string searchPattern) where T : Module
        {
            return _modules.GetItems<T>(searchPattern);
        }
        public virtual T SearchForModule<T>(string searchPattern) where T : Module
        {
            List<T> matches = SearchModules<T>(searchPattern);
            return matches.Count > 0 ? matches[0] : default(T);
        }
        public INode GetTopWrapper()
        {
            return _modules.Count > 0 ? _modules[_modules.Count - 1] : this as INode;
        }       
        public virtual void LoadContent(ContentManager content)
        {
            foreach (Component component in _components)
            {
                component.LoadContent(content);
            }
        }
        public virtual void Update(GameTime gameTime)
        {
            _tempComponents.Clear();
            foreach (Component component in _components)
            {
                _tempComponents.Add(component);
            }
            foreach (Component component in _tempComponents)
            {
                component.Update(gameTime);
            }
        }
        public virtual void Draw(SpriteBatch spriteBatch)
        {
            foreach (Component component in _components)
            {
                component.Draw(spriteBatch);
            }
        }
        public string GetTreeAddress()
        {
            string fullName = _name;
            Component nextComponent = Parent;
            while (nextComponent != null)
            {
                fullName = nextComponent.Name + ":" + fullName;
                nextComponent = nextComponent.Parent;
            }
            return fullName;
        }
        public virtual void SetModule<T>(T module) where T : Module
        {
            T oldModule = GetModule<T>();
            if (oldModule != null)
            {
                int oldIndex = _modules.IndexOf(oldModule);
                _modules.Remove(oldModule);
                _modules.Insert(module.Name, module, oldIndex);
                Rewrap();
                SetDependencies();
            }
            else
            {
                AddModule(module);
            }
        }
        public void SetDependencies()
        {
            foreach (Module module in _modules)
            {
                module.SetDependencies();
            }
            foreach (Component child in _components)
            {
                child.SetDependencies();
            }
        }
    }
}

Here we have a collection of Modules and Components and methods for managing them. The GetTreeAddress method returns a string in the form of:
[ParentName]:[ChildName]:[GrandChildName]:...
so we can later search the tree for any component we'd like.
The Component class will differ in that all these methods will be marked new. We're intentionally hiding the methods and properties. Let's see the code and then I'll explain why.

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

namespace KrakenEngine.Framework
{
    public class Component : Node
    {
        public new ComponentBuilder ComponentBuilder
        {
            get { return GetTopWrapper().ComponentBuilder; }
        }       
        public new Component Parent
        {
            get { return GetTopWrapper().Parent; }
            private set { base.Parent = value; }
        }
        public new RootNode Root
        {
            get { return GetTopWrapper().Root; }
            set { base.Root = value; }
        }
        public new double Version
        {
            get { return GetTopWrapper().Version; }
        }

        public Component(string name, double version) : base(name, version) { }

        public new void AddComponent(Component component)
        {
            component.Parent = this;
            GetTopWrapper().AddComponent(component);
        }
        public new void AddModule(Module module)
        {
            GetTopWrapper().AddModule(module);
        }
        public new void RemoveComponent(Component component)
        {
            GetTopWrapper().RemoveComponent(component);
        }
        public new void RemoveComponent(string name)
        {
            GetTopWrapper().RemoveComponent(name);
        }
        public new void RemoveModule(Module module)
        {
            GetTopWrapper().RemoveModule(module);
        }
        public new void RemoveModule(string name)
        {
            GetTopWrapper().RemoveModule(name);
        }
        public new Component GetComponent(string name)
        {
            return GetTopWrapper().GetComponent(name);
        }
        public new List<Component> GetComponents()
        {
            return GetTopWrapper().GetComponents();
        }
        public new T GetModule<T>() where T : Module
        {
            return GetTopWrapper().GetModule<T>();
        }
        public new List<T> GetModules<T>() where T : Module
        {
            return GetTopWrapper().GetModules<T>();
        }
        public new void SetModule<T>(T module) where T : Module
        {
            GetTopWrapper().SetModule<T>(module);
        }
        public new List<Component> SearchComponents(string searchPattern)
        {
            return GetTopWrapper().SearchComponents(searchPattern);
        }
        public new Component SearchForComponent(string searchPattern)
        {
            return GetTopWrapper().SearchForComponent(searchPattern);
        }
        public new T SearchForModule<T>(string searchPattern) where T : Module
        {
            return GetTopWrapper().SearchForModule<T>(searchPattern);
        }
        public new List<T> SearchModules<T>(string searchPattern) where T : Module
        {
            return GetTopWrapper().SearchModules<T>(searchPattern);
        }       
        public new void LoadContent(ContentManager content)
        {
            GetTopWrapper().LoadContent(content);
        }
        public new void Update(GameTime gameTime)
        {
            GetTopWrapper().Update(gameTime);
        }
        public new void Draw(SpriteBatch spriteBatch)
        {
            GetTopWrapper().Draw(spriteBatch);
        }
    }
}

Each of the hidden methods call GetTopWrapper() and remember that the Node class holds a list of Components. When we iterate through the tree, it is these methods that will be called which in turn calls the top decorator. This ensures that every module will be called without having to always type GetTopWrapper() before whatever it is we'd like to do.

Let's take care of the Module class now:

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

namespace KrakenEngine.Framework
{
    public class Module : INode
    {
        INode _node = NullNode.GetNullNode();
        string _name;
        string _assemblyQualifiedName;

        public string Name
        {
            get { return _name; }
        }
        public string AssemblyQualifiedName
        {
            get { return _assemblyQualifiedName; }
        }
        public virtual Component Parent
        {
            get { return _node.Parent; }
        }
        [ContentSerializerIgnore]
        public virtual RootNode Root
        {
            get { return _node.Root; }
        }
        public virtual double Version
        {
            get { return _node.Version; }
        }
        [ContentSerializerIgnore]
        public virtual ComponentBuilder ComponentBuilder
        {
            get { return _node.ComponentBuilder; }
        }

        public Module()
        {
            _name = GetType().Name;
            _assemblyQualifiedName = GetType().AssemblyQualifiedName;
        }

        public virtual void AddComponent(Component component)
        {
            _node.AddComponent(component);
        }
        public virtual void RemoveComponent(Component component)
        {
            _node.RemoveComponent(component);
        }
        public virtual void RemoveComponent(string name)
        {
            _node.RemoveComponent(name);
        }
        public virtual void AddModule(Module module)
        {
            _node.AddModule(module);
        }
        public virtual void RemoveModule(Module module)
        {
            _node.RemoveModule(module);
        }
        public virtual void RemoveModule(string name)
        {
            _node.RemoveModule(name);
        }
        public virtual Component GetComponent(string name)
        {
            return _node.GetComponent(name);
        }
        public virtual List<Component> GetComponents()
        {
            return _node.GetComponents();
        }
        public virtual List<Component> SearchComponents(string searchPattern)
        {
            return _node.SearchComponents(searchPattern);
        }
        public virtual Component SearchForComponent(string searchPattern)
        {
            return _node.SearchForComponent(searchPattern);
        }
        public virtual T GetModule<T>() where T : Module
        {
            return _node.GetModule<T>();
        }
        public virtual List<T> GetModules<T>() where T : Module
        {
            return _node.GetModules<T>();
        }
        public virtual List<T> SearchModules<T>(string searchPattern) where T : Module
        {
            return _node.SearchModules<T>(searchPattern);
        }
        public virtual T SearchForModule<T>(string searchPattern) where T : Module
        {
            return _node.SearchForModule<T>(searchPattern);
        }
        public virtual void SetModule<T>(T module) where T : Module
        {
            _node.SetModule<T>(module);
        }
        public virtual void SetDependencies() { }
        public virtual void LoadContent(ContentManager content)
        {
            _node.LoadContent(content);
        }
        public virtual void Update(GameTime gameTime)
        {
            _node.Update(gameTime);
        }
        public virtual void Draw(SpriteBatch spriteBatch)
        {
            _node.Draw(spriteBatch);
        }
        public void WrapNode(INode node)
        {
            _node = node;
        }
    }
}

The assembly qualified name field is in case we'd like to do some reflection later and those [ContentSerializerIgnore] attributes prevent errors in the content type reader we'll build later.
The NullNode class uses the null object pattern to avoid null exceptions. And here it is:

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

namespace KrakenEngine.Framework
{
    public class NullNode:INode
    {
        static INode _instance;

        public Component Parent
        {
            get { return null; }
        }
        public RootNode Root
        {
            get { return null; }
        }
        public double Version
        {
            get { return 0d; }
        }
        public ComponentBuilder ComponentBuilder
        {
            get { return null; }
        }

        NullNode() { }

        public void AddComponent(Component component) { }
        public void RemoveComponent(Component component) { }
        public void RemoveComponent(string name) { }
        public void AddModule(Module module) { }
        public void RemoveModule(Module module) { }
        public void RemoveModule(string name) { }
        public Component GetComponent(string name)
        {
            return null;
        }
        public List<Component> GetComponents()
        {
            return new List<Component>();
        }
        public List<Component> SearchComponents(string searchPattern)
        {
            return new List<Component>();
        }
        public Component SearchForComponent(string searchPattern)
        {
            return null;
        }
        public T GetModule<T>() where T : Module
        {
            return default(T);
        }
        public List<T> GetModules<T>() where T : Module
        {
            return new List<T>();
        }
        public void SetModule<T>(T module) where T : Module { }
        public List<T> SearchModules<T>(string searchPattern) where T : Module
        {
            return new List<T>();
        }
        public T SearchForModule<T>(string searchPattern) where T : Module
        {
            return default(T);
        }
        public void SetDependencies() { }
        public void LoadContent(ContentManager content) { }
        public void Update(GameTime gameTime) { }
        public void Draw(SpriteBatch spriteBatch) { }
        public static INode GetNullNode()
        {
            if (_instance == null)
            {
                _instance = new NullNode();
            }
            return _instance;
        }
    }
}

Next is the RootNode class. It will hold a collection of every component that is a part of the tree and methods to search and retrieve them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KrakenEngine.Framework.XNAData;
using XNAGameEngineData;
using Microsoft.Xna.Framework.Content;

namespace KrakenEngine.Framework
{
    public class RootNode : Component
    {
        XNAEngine _engine;
        GameCollection<Component> _gameTree = new GameCollection<Component>();

        public XNAEngine Engine
        {
            get { return _engine; }
            set { _engine = value; }
        }

        public RootNode() : base("RootNode", 0d)
        {
            Root = this;
        }

        public void AddToTree(Component component)
        {
            component.Root = Root;
            _gameTree.Add(component.GetTreeAddress(), component);
            if (Engine.IsInitialized)
            {
                component.LoadContent(Engine.Game.Content);
            }
            foreach (Component child in component.GetComponents())
            {
                AddToTree(child);
            }
        }
        public void RemoveFromTree(Component component)
        {
            _gameTree.Remove(component);
            foreach (Component child in component.GetComponents())
            {
                RemoveFromTree(child);
            }
        }
        public void RemoveFromTree(string address)
        {
            RemoveFromTree(_gameTree[address]);
        }
    }
}

So, every time a component is added to another component, we check to see if it's connected to a RootNode (by checking for a null reference) and then call AddToTree, which adds the Component and all of its children to the tree recursively, setting its key to the components address. A similar recursion happens when removing a component.

Notice the XNAEngine class. That's not a typo, it's named that because I couldn't think of a better one. It's a class that inherits from the XNAGameEngine class and holds a root node that it loads, updates and draws. Later, we can make a collection of roots with a single active root.
Here it is:

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

namespace KrakenEngine.Framework.XNAData
{
    public class XNAEngine : XNAGameEngine
    {
        static RootNode _root;

        public RootNode Root
        {
            get { return _root; }
            set 
            { 
                _root = value;
                _root.Engine = this;
            }
        }

        public Point CenterScreen
        {
            get { return 
                new Point(
                    GraphicsDevice.Viewport.Width / 2, 
                    GraphicsDevice.Viewport.Height / 2); }
        }

        public XNAEngine(Game game) : base(game)
        {
            
        }
        public override void Initialize()
        {
            base.Initialize();
            Mouse.SetPosition(CenterScreen.X, CenterScreen.Y);
        }
        protected override void LoadContent()
        {
            _root.LoadContent(Game.Content);
            base.LoadContent();
        }
        public override void Update(GameTime gameTime)
        {
            _root.Update(gameTime);
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
            _root.Draw(SpriteBatch);
        }
        public static RootNode GetActiveRoot()
        {
            return _root;
        }
    }
}

That's it for now. Next, we'll make some modules and then a content pipeline extension so we can load components in from a file.

Latest source code

Previous

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"