Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm looking at implementing a messaging system in my entity component system. I've deduced that I can use an event / queue for passing messages, but right now, I just use a generic object and cast out the data I want. I also considered using a dictionary. I see a lot of information on this, but they all involve a lot of casting and guessing. Is there any way to do this elegantly and keep strong typing on my messages?

share|improve this question
    
If you are using Java then Google's Guava library has a great event system that may solve your issue. –  mobo Jul 1 '13 at 15:02
    
2  
You almost certainly will need to do casting, but you can hide it via the messaging API. However, the language you're using will have a pretty big impact on the techniques available -- which language are you doing this in? –  Josh Petrie Jul 1 '13 at 15:18
    
C# is the language of choice. I'd take Java answers, too for curiosity. –  Vaughan Hilts Jul 1 '13 at 19:39
    
Sounds like you want to use Generics. I'd post a lengthy explanation of how to design a system but there is already a pretty awesome system designed for use in Unity (but you can actually use it anywhere) wiki.unity3d.com/index.php?title=CSharpMessenger_Extended –  Benjamin Danger Johnson Jul 18 '13 at 22:10
add comment

3 Answers

Create a separate message class for each type of message (to a degree).

In your entities, make a different method for each type of message. eg.

class CollisionMessage;
class DamageMessage;

class Entity {
public:
  virtual void onCollision(CollisionMessage&);
  virtual void onDamage(DamageMessage&);
};

You can now use a form of double-dispatch to select the right version. Have a deliver method on your Message types that delivers them to the right handler:

class Message {
public:
  virtual void deliver(Entity& e) = 0;
};

class CollisionMessage : public Message {
public:
  virtual void deliver(Entity& e) { e.onCollision(*this); }
};

class DamageMessage : public Message {
public:
  virtual void deliver(Entity& e) { e.onDamage(*this); }
};

You can extend this to an observer system by registering message handlers. You can also use this to allow multiple independent messages to share the same type using a little metadata, e.g, ensuring that the "EnvironmentDamage" and "GunDamage" messages both use the DamageMessage type.

The use of components or ECS really doesn't change this much. With components you're just delivering to individual components instead of a main game object class. With ECS you're delivering them to systems and including a "target entity ID" as part of the message. Nice and simple.

If you're going down the ECS route and want it to actually be useful, you might consider a more heavily data-oriented approach to messaging. See the BitSquid blog on managing coupling (actually, read all their articles; they're quite fantastic, even if I don't agree with all of it).

share|improve this answer
add comment

Separate queues per data type to end casting. (I like this one better as it is simple, flexible, and allows for explicit ordering of types of operations)

class SpawnEntityEvent {
public:
    std::string resource;
    double x,y,z;
};
class MoveEntityEvent {
public:
    Entity* ent;
    double x,y,z;
};
class ChangeEntityPropEvent {
public:
    Entity* ent;
    std::string key;
    std::string value;
};
std::queue<SpawnEntityEvent> spawnQueue;
std::queue<MoveEntityEvent> moveQueue;
std::queue<ChangeEntityPropEvent> propQueue;

    /*** And if one needs to maintain interleaved order... ***/
std::queue<int> order;
enum {
    ENT_SPAWN,
    ENT_MOVE,
    ENT_PROP
};
void nextSpawnEvent() {
    SpawnEntityEvent& evt=spawnQueue.front();
    // spawn your entity with evt data now
    spawnQueue.pop();
}
void nextMoveEvent();
void nextPropChangeEvent();

void nextEvent() {
    switch (order.front()) {
        case ENT_SPAWN: nextSpawnEvent();break;
        case ENT_MOVE: nextMoveEvent();break;
        case ENT_PROP: nextPropChangeEvent();break;
    }
    order.pop();
}

Or tag structures with metadata to end guessing. (This feels a bit cumbersome to me but does allow to keep a single event queue, also creating message codes should be re-usable for networking)

class EntityEvent {
public:
    EntityEvent(int t) {type=t;}
    unsigned char type;
};
enum {
    ENT_SPAWN,
    ENT_MOVE,
    ENT_PROP
};
class SpawnEntityEvent : public EntityEvent {
public:
    SpawnEntityEvent() : EntityEvent(ENT_SPAWN) {};
    std::string resource;
    double x,y,z;
};
class MoveEntityEvent : public EntityEvent {
public:
    MoveEntityEvent() : EntityEvent(ENT_MOVE) {};
    Entity* ent;
    double x,y,z;
};
class ChangeEntityPropEvent : public EntityEvent {
public:
    ChangeEntityPropEvent() : EntityEvent(ENT_PROP) {};
    Entity* ent;
    std::string key;
    std::string value;
};
std::queue<EntityEvent*> eventQueue;
share|improve this answer
    
Welcome to the site MickLH. We typically like our answers to be a bit more complete. You've got the start of an answer here, but it really needs a bit more explanation to be a complete answer. If you could add more information you could make this a decent answer. –  Byte56 Jul 1 '13 at 17:09
    
is it preferred that i make a quick edit with example code, or write an essay? –  MickLH Jul 1 '13 at 18:54
    
However you think it best to answer the question. Everyone has their own style and standards. –  Byte56 Jul 1 '13 at 19:21
    
Some description would be nice. How would your solution work? The first sentences is pretty much clear (at least to me). But what do you mean by tag structures with metadata to end guessing. Some kind of event type attribute? –  Stephen Jul 1 '13 at 21:16
2  
Thanks for updating your answer with more info. +1. –  Byte56 Sep 5 '13 at 15:54
show 1 more comment

I have spent a while thinking about this issue myself and it seems that it's a static vs dynamic programming type of choice. You can't have both advantages unfortunately.

When speaking about a language like C# it comes down to a choice of where to put casting + reflection logic or alternatively whether to use dynamic objects.

The best sample of two approaches would be NServiceBus and SignalR distributed messaging frameworks:

NServiceBus (casting somewhere "above"):

public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
{
    public void Handle(PlaceOrder message)
    {
        Console.Out.WriteLine(@"Order for ""{0}"" placed.", message.Product);
    }
}

SignalR (using dynamics in-place):

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.