More on MonoGame/XNA Components

Published April 21, 2013
Advertisement
Have been slowly plugging away at my Combat Prototype for my game. I've kept a clean separation of the "Engine" and "Game", with each being in separate assemblies. The Engine code is the foundations - A Unity-like GameObject + component model that sits on top of MonoGame (or XNA). I'm not going to talk about the game part, but the continuation of the Engine core.


Messaging

Lately, I've added in a Messaging system. This was primarily due to me needing to communicate between GameObjects. For component-component communication on the same object, I'm just referencing the components directly. I decided to go messaging for intra-object communication because I really didn't want to start tightly coupling other objects to each other. My messaging implementation is different to Unity's. I'll talk about that here.

When a component wants to hook a message, it must call "RegisterMessageHandler" on the GameObject it's attached to.

This has the following prototype:void RegisterMessageHandler(string messageType, Action handler);

As you can see, it takes a handler function as a parameter. The GameObject itself then listens to any messages hooked by the components and routes them to each component when received.

Each message must implement the interface IGameObjectMessage, which has a single required property - "MessageName". I'm tempted to remove this requirement entirely and make it more like how Components work - requiring the messages to be bound by the message Type. This would give me something such as:
void RegisterMessageHandler(Action);
This feels more elegant. However, this may create an issue if the components are destroyed and don't unhook the messages first. I'm contemplating using C#'s event system and following a Weak Event Pattern. Thoughts are appreciated.

There's a few ways to send a message to other objects. I'll list them out:

  • SendMessage - Sends a message to a particular GameObject
  • BroadcastMessage - Sends a message to a GameObject and all its children
  • SendMessageUpwards - Sends the message to the GameObject's parent
  • BroadcastMessageUpwards - Sends the message to the GameObject's parent and it's ancestors.

One thing I'm debating with the Broadcast options is whether they should cascade all the way up and/or down the tree to children of children, etc.

I've also got a GameObjectGraph. Basically, a way of storing and accessing my "root" objects. I'm currently figuring out a nice way of exposing this to the world other than using a Singleton. It's likely to appear as a property on the GameObject in some way.

I need this because it acts as a sort of scene graph. It is the root object of the "world" and is called when I want to both Update() and Render() my scene. It also acts a place to fire global messages to notify all GameObjects in the world about something. At this level, I have:

  • BroadcastMessage - sends a message immediately to all objects, starting at the root and cascading downwards
  • QueueBroadcastMessage - drops a message onto a queue to be sent the next tick, before Update() is called

The message handlers themselves have the signature:[code=:0]void SomeHandler(IGameObject sender, IGameObjectMessage message);
The first parameter is the GameObject which sent the message. The astute of you will realise that it's actually a Component which sends messages and not the GameObject. It's debatable whether the first parameter should reference the component instead of the GameObject, as this would open up component-component direct communications.


Tagging

I've added a simple Tag collection to each object. These are text labels which can be searched on. A GameObject can have as many as needed. I currently use them for Linq queries over child objects:
[code=:0]foreach(var child in this.Children.Where( (o) => o.HasTag("sometag") ){ // do something with tagged object}

I'm contemplating extending this system to become a Triple Tag, basically: namespace:predicate=value. This would make things nice and flexible, especially when combined with Linq queries. For example, if I had two sides in the game and I wanted to find all units of type "tank", I could write a Linq query such as:
[code=:0]foreach(var tank in this.Children.Where( (o) => o.HasTagNamespace("SideA") && o.HasTagPredicate("UnitType") && o.HasTagValue("Tank")){}

That's about it for now. There's a few current niggles and areas I need to start looking into. These are:

  • Object creation via cloning (allows me to have Prefabs)
  • Object destruction (queue to kill next frame)
  • Object serialisation to JSON/BSON (allows me to save/load object definitions from files)
  • General object management and tracking.


The current systemis nice enough, I guess - it has quite a few rough edges and things I'm changing as I go on - I already plan on reusing this part of the system in other projects. I'm also contemplating open sourcing it on GitHub, I'm not sure though...
Previous Entry More on Components
Next Entry Articles submitted
1 likes 1 comments

Comments

Xai

Note, combining the "HasTagX, Y and Z" method calls as you have shown doesn't do what you would want. You don't want a "has SOMETHING with namespace X" and "SOMETHING with predicate Y" probably. would want a tag row where all 3 are true for the same row. So you need overloads that take multiple parameters, like "HasTagPredicate(namespace, predicate)" for a 2 parter and "HasTagValue(namespace, predicate, value)" for a 3 parter.

good luck.

April 27, 2013 01:24 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement