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.

How do game engine entities communicate?

Two use cases:

  1. How would entity_A send a take-damage message to entity_B?
  2. How would entity_A query entity_B's HP?

Here's what I've encountered so far:

  • Message queue
    1. entity_A creates a take-damage message and posts it to entity_B's message queue.
    2. entity_A creates a query-hp message and posts it to entity_B. entity_B in return creates an response-hp message and posts it to entity_A.
  • Publish/Subscribe
    1. entity_B subscribes to take-damage messages (possibly with some preemptive filtering so only relevant message are delivered). entity_A produces take-damage message that references entity_B.
    2. entity_A subscribes to update-hp messages (possibly filtered). Every frame entity_B broadcasts update-hp messages.
  • Signal/Slots
    1. ???
    2. entity_A connects an update-hp slot to entity_B's update-hp signal.
  • Something better?

Do I have a correct understanding of how these communication schemes would tie into a game engine's entity system?
How do entities in commercial game engines communicate?

share|improve this question

9 Answers 9

up vote 58 down vote accepted

Good question! Before I get to the specific questions you asked, I'll say: don't underestimate the power of simplicity. Tenpn is right. Keep in mind that all you're trying to do with these approaches is find an elegant way to defer a function call or decouple the caller from the callee. I can recommend coroutines as a surprisingly intuitive way to alleviate some of those problems, but that's a little off-topic. Sometimes, you're better off just calling the function and living with the fact that entity A is coupled directly to entity B. See YAGNI.

That said, I've used and been happy with the signal/slot model combined with simple message passing. I used it in C++ and Lua for a fairly successful iPhone title that had a very tight schedule.

For the signal/slot case, if I want entity A to do something in response to something entity B did (e.g. unlock a door when something dies) I might have entity A subscribe directly to entity B's death event. Or possibly entity A would subscribe to each of a group of entities, increment a counter on each event fired, and unlock the door after N of them have died. Also, "group of entities" and "N of them" would typically be designer defined in the level data. (As an aside, this is one area where coroutines can really shine, e.g., WaitForMultiple( "Dying", entA, entB, entC ); door.Unlock();)

But that can get cumbersome when it comes to reactions that are tightly coupled to C++ code, or inherently ephemeral game events: dealing damage, reloading weapons, debugging, player-driven location-based AI feedback. This is where message passing can fill in the gaps. It essentially boils down to something like, "tell all the entities in this area to take damage in 3 seconds," or "whenever you complete the physics to figure out who I shot, tell them to run this script function." It's difficult to figure out how to do that nicely using publish/subscribe or signal/slot.

This can easily be overkill (versus tenpn's example). It can also be inefficient bloat if you have a lot of action. But despite its drawbacks, this "messages and events" approach meshes very well with scripted game code (e.g. in Lua). The script code can define and react to its own messages and events without the C++ code caring at all. And the script code can easily send messages that trigger C++ code, like changing levels, playing sounds, or even just letting a weapon set how much damage that TakeDamage message delivers. It saved me a ton of time because I wasn't having to constantly fool around with luabind. And it let me keep all of my luabind code in one place, because there wasn't much of it. When properly coupled, you can use embedded languages like Lua to easily add new features/monsters/weapons/levels/etc to the game without ever recompiling the C++ code.

Also, my experience with use case #2 is that you're better off handling it as an event in the other direction. Instead of asking what the entity's health is, fire an event/send a message whenever the health makes a significant change.

In terms of interfaces, btw, I ended up with three classes to implement all of this: EventHost, EventClient, and MessageClient. EventHosts create slots, EventClients subscribe/connect to them, and MessageClients associate a delegate with a message. Note that a MessageClient's delegate target doesn't necessarily need to be the same object that owns the association. In other words, MessageClients can exist solely to forward messages to other objects. FWIW, the host/client metaphor is kind of inappropriate. Source/Sink might be better concepts.

Sorry, I kinda rambled there. It's my first answer :) I hope it made sense.

share|improve this answer
    
Thanks for the answer. Great insights. The reason I'm over designing the message passing is because of Lua. I'd like to be able to create new weapons without new C++ code. So your thoughts answered some of my unasked questions. –  deft_code Jul 25 '10 at 5:54
    
As for the coroutines I too am a great believer in coroutines, but I never get to play with them in C++. I had a vague hope of using coroutines in the lua code to handle blocking calls (eg, wait-for-death). Was it worth the effort? I'm afraid that I may be blinded by my intense desire for coroutines in c++. –  deft_code Jul 25 '10 at 5:57
    
Lastly, What was the iphone game? Can I get more information on the entity system you used? –  deft_code Jul 25 '10 at 5:58
2  
The entity system was mostly in C++. So there was, for example, an Imp class that handled the Imp's behavior. Lua could change Imp's parameters at spawn or via message. The goal with Lua was to fit in a tight schedule, and debugging Lua code is very time-consuming. We used Lua to script levels (which entities go where, events that happen when you hit triggers). So in Lua, we'd say things like, SpawnEnt( "Imp" ) where Imp is a manually registered factory association. It would always spawn into one global pool of entities. Nice and simple. We used a lot of smart_ptr and weak_ptr. –  BRaffle Jul 27 '10 at 13:03
1  
So BananaRaffle: Would you say this is an accurate summary of your answer: "All 3 of the solutions you posted have their uses, as do others. Don't look for the one perfect solution, just use what you need where it makes sense." –  Ipsquiggle Sep 8 '10 at 16:19
// in entity_a's code:
entity_b->takeDamage();

You asked how comercial games do it. ;)

share|improve this answer
8  
A down vote? Seriously, that's how it's normally done! Entity systems are great but they don't help hit the early milestones. –  tenpn Jul 22 '10 at 8:18
    
I make Flash games professionally, and this is how I do it. You call enemy.damage(10) and then you look up any info you need from public getters. –  Iain Jul 22 '10 at 12:29
7  
This is seriously how commercial game engines do it. He's not joking. Target.NotifyTakeDamage( DamageType, DamageAmount, DamageDealer, etc. ) is usually how it goes. –  A.A. Grapsas Jul 22 '10 at 15:16
3  
Do commercial games misspell "damage" too? :-P –  Ricket Aug 1 '10 at 17:12
11  
Yes, they do misspel damage, among other things. :) –  LearnCocos2D Aug 7 '10 at 14:18

A more serious answer:

I've seen blackboards used a lot. Simple versions are nothing more than struts that are updated with things like an entity's HP, which entities can then query.

Your blackboards can either be the world's view of this entity (ask B's blackboard what its HP is), or an entity's view of the world (A queries its blackboard to see what the HP of A's target is).

If you only update the blackboards at a sync point in the frame, you can then read from them at a later point from any thread, making multithreading pretty simple to implement.

More advanced blackboards may be more like hashtables, mapping strings to values. This is more maintainable but obviously has a run-time cost.

A blackboard is traditionally only one-way communication - it wouldn't handle the dishing out of damage.

share|improve this answer
    
I had never heard of the blackboard model before now. –  deft_code Jul 22 '10 at 8:24
    
They are also good for reducing dependencies, the same way an event queue or publish/subscribe model does. –  tenpn Jul 22 '10 at 10:55
1  
This is also the canonical “definition” of how an “ideal” E/C/S system “should work.” The Components make up the blackboard; the Systems are the code acting upon it. (Entities, of course, are just long long ints or similar, in a pure ECS system.) –  BRPocock Dec 22 '11 at 21:43

If your game is single player, just use the target objects method (as tenpn suggested).

If you are (or want to support) multiplayer (multiclient to be exact), use a command queue.

  • When A does damage to B on client 1 just queue the damage event.
  • Synchronize the command queues via the network
  • Handle the queued commands on both sides.
share|improve this answer
2  
If you're serious about avoiding cheating, A doesn't daamge B on the client at all. The client owning A sends an "attack B" command to the server, which does exactly what tenpn said; the server then syncs that state with all relevant clients. –  user744 Aug 1 '10 at 11:46
    
@Joe: Yes, if there is a server that is a valid point to consider, but sometimes it's ok to trust the client (e.g. on a console) to avoid heavy server load. –  Andreas Aug 6 '10 at 6:39

I have studied this issue a bit and I have seen a nice solution.

Basically it's all about subsystems. It is similar to the blackboard idea mentioned by tenpn.

Entities are made of components, but they are only property bags. No behavior is implemented in entities themselves.

Let's say, entities have a Health component and a Damage component.

Then you have some MessageManager and three subsystems: ActionSystem, DamageSystem, HealthSystem. At one point ActionSystem does its calculations upon the game world and generates an event:

HIT, source=entity_A target=entity_B power=5

This event is published to the MessageManager. Now at one point in time the MessageManager goes through the pending messages and finds that the DamageSystem has subscribed to HIT messages. Now the MessageManager delivers the HIT message to the DamageSystem. The DamageSystem goes through its list of entities which have Damage component, calculates the damage points depending on the hit power or some other state of both entities etc. and publishes event

DAMAGE, source=entity_A target=entity_B amount=7

The HealthSystem has subscribed to the DAMAGE messages and now when the MessageManager publishes the DAMAGE message to the HealthSystem, the HealthSystem has access to both entities entity_A and entity_B with their Health components, so again the HealthSystem can do its calculations (and maybe publish corresponding event to the MessageManager).

In such a game engine, the format of messages is the only coupling between all the components and subsystems. The subsystems and entities are completely independent and unaware of each other.

I don't know whether some real game engine has implemented this idea or not, but it seems pretty solid and clean and I hope someday to implement it myself for my hobbyist level game engine.

share|improve this answer

Why not have a global message queue, something like:

messageQueue.push_back(shared_ptr<Event>(new DamageEvent(entityB, 10, entityA)));

With:

DamageEvent(Entity* toDamage, uint amount, Entity* damageDealer);

And at the end of the game loop/event handling:

while(!messageQueue.empty())
{
    Event e = messageQueue.front();
    messageQueue.pop_front();
    e.Execute();
}

I think this is the Command pattern. And Execute() is a pure virtual in Event, which derivatives define and do stuff. So here:

DamageEvent::Execute() 
{
    toDamage->takeDamage(amount); // Or of course, you could now have entityA get points, or a recognition of damage, or anything.
}
share|improve this answer

Just make the call. Do not do request-hp folllowed by query-hp -- if you follow that model you will be in for a world of hurt.

You might want to have a look at Mono Continuations as well. I think it'd be ideal for NPCs.

share|improve this answer

I would say: Use neither, as long as you don't explicitly need instant-time feedback from the damage.

The damage-taking entity/component/whatever should push the events to either a local event-queue or a system on an equal level that holds damage-events.

There should then be an overlaying system with access to both entities that requests the events from entity a and passes it to entity b. By not creating a general event-system that anything can use from anywhere to pass an event to anything at any time, you create explicit data-flow which always makes code easier to debug, easier to measure performance, easier to understand and read and often leads to a more well-designed system in general.

share|improve this answer

So what happens if we have player A and B trying to hit eachother in the same update() cycle? Suppose the Update() for player A happens to occur before the Update() for player B in Cycle 1 (or tick, or whatever you call it). There's two scenario's I can think of:

  1. Immediate processing through a message:

    • player A.Update() sees the player wants to hit B, player B gets a message notifying the damage.
    • player B.HandleMessage() updates the hitpoints for player B (he dies)
    • player B.Update() sees player B is dead.. he can't attack player A

This is unfair, player A and B should hit eachother, player B died before hitting A just because that entity/gameobject got update() later.

  1. Queueing the message

    • Player A.Update() sees the player wants to hit B, player B gets a message notifying the damage and stores it in a queue
    • Player A.Update() checks its queue, it's empty
    • player B.Update() first checks for moves so player B send a message to player A with damage aswell
    • player B.Update() also handles messages in the queue, processes the damage from player A
    • New cycle (2): Player A wants to drink a health potion so Player A.Update() is called and the move is processed
    • Player A.Update() checks the message queue and processes damage from player B

Again this is unfair.. player A is supposed to take the hitpoints in the same turn/cycle/tick!

share|improve this answer
4  
You're not really answering the question but I think your answer would make an excellent question itself. Why not go ahead and ask how to resolve such an "unfair" prioritization? –  bummzack Mar 22 '11 at 7:15
    
I doubt that most games care about this unfairness because they update so frequently that it is rarely an issue. One simple workaround is to alternate between iterating forwards and backwards through the entity list when updating. –  Kylotan Mar 22 '11 at 20:21
    
I use 2 calls so I call Update() to all entities, then after the loop I iterate again and call something like pEntity->Flush( pMessages ); . When entity_A generates a new event, it is not read by entity_B in that frame (it has the chance to take the potion too) then both receive damage and after that they process the message of the potion healing which would be the last in the queue. Player B still dies anyway as the potion message is the last in the queue :P but it may be useful for other kind of messages such as clearing pointers to dead entities. –  Pablo Ariel Aug 31 '11 at 22:58
    
I think at the frame levels, most game implementations are simply unfair. like Kylotan said. –  v.oddou Dec 17 at 1:56

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.