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?
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.
You can now use a form of double-dispatch to select the right version. Have a
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
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).
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)
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)
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"):
SignalR (using dynamics in-place):