Component-based entity system questions

Started by
12 comments, last by Doggolainen 13 years ago
I handle my own components quite differently, limiting communication with an object to the passing of messages or requests, and never asking for direct access to a component for any reason. In my system, you don't really care whether an object has a HealthComponent or not; you just pass it messages along the lines of ModifyHealth, and let any interested components handle the message as they see fit. If no component wants to handle the message, then it is silently ignored. And it is possible for multiple components to handle the same message.

The only means of working with an entity that I provide are handleMessage() and handleRequest(); there is no way to get access directly to any component.

So as an example, Entity A makes a damaging attack against Entity B. Instead of querying Entity B for its HealthComponent and modifying it directly, I instead calculate the damage numbers and pass them as arguments in a "ModifyLife" message sent to Entity B. Entity B has a HealthComponent that registers to listen for "ModifyLife" messages, and when the message is received it will handle applying the damage accordingly. Now, since Entity B is a non-aggressive monster, its MonsterController component also registers to listen for "ModifyLife", and upon receipt of a "ModifyLife" message, it will signal to enter a defensive combat state. Exactly what this defensive state is, of course, depends on the exact implementation of the MonsterController. Some will respond with fight, some will respond with flight, etc... Also, since Entity B is a selectable enemy that the player can target, it also has a third component which listens for "ModifyLife" messages: a CharacterNameplateDisplay component, which will modify the on-screen lifebar representation to reflect the decrease in life.

Now, what if I want to make Entity B into a sort of booby-trap exploding decoy? Instead of adding a HealthComponent, CharacterNameplateDisplay component, etc... instead I add a ExplodesOnHit component. Now, without any change to the sequence of actions required on Entity A's part (I still generate an attack, roll damage numbers, and pass a "ModifyLife" message as before) I have completely changed the way Entity B responds to an attack. The ExplodesOnHit component registers to listen for "ModifyLife", only instead of performing life calculations, it just triggers an exploding response, querying the environment for all objects in a radius, calculating a damage value and delivering this damage value as "ModifyLife" messages sent to all objects in the radius, then triggering a special effect ("Explode") and finally triggering DestroySelf message to despawn the trapped object.

In this way, I decouple the action (making an attack) from the response to the action (modifying health level, modifying lifebar display, exploding), and not once do I ever care exactly what component or type of component is needed to handle the message.

I wrote a bit more detailed explanation in my journal here.
Advertisement

I handle my own components quite differently, limiting communication with an object to the passing of messages or requests, and never asking for direct access to a component for any reason. In my system, you don't really care whether an object has a HealthComponent or not; you just pass it messages along the lines of ModifyHealth, and let any interested components handle the message as they see fit. If no component wants to handle the message, then it is silently ignored. And it is possible for multiple components to handle the same message.

The only means of working with an entity that I provide are handleMessage() and handleRequest(); there is no way to get access directly to any component.

So as an example, Entity A makes a damaging attack against Entity B. Instead of querying Entity B for its HealthComponent and modifying it directly, I instead calculate the damage numbers and pass them as arguments in a "ModifyLife" message sent to Entity B. Entity B has a HealthComponent that registers to listen for "ModifyLife" messages, and when the message is received it will handle applying the damage accordingly. Now, since Entity B is a non-aggressive monster, its MonsterController component also registers to listen for "ModifyLife", and upon receipt of a "ModifyLife" message, it will signal to enter a defensive combat state. Exactly what this defensive state is, of course, depends on the exact implementation of the MonsterController. Some will respond with fight, some will respond with flight, etc... Also, since Entity B is a selectable enemy that the player can target, it also has a third component which listens for "ModifyLife" messages: a CharacterNameplateDisplay component, which will modify the on-screen lifebar representation to reflect the decrease in life.

Now, what if I want to make Entity B into a sort of booby-trap exploding decoy? Instead of adding a HealthComponent, CharacterNameplateDisplay component, etc... instead I add a ExplodesOnHit component. Now, without any change to the sequence of actions required on Entity A's part (I still generate an attack, roll damage numbers, and pass a "ModifyLife" message as before) I have completely changed the way Entity B responds to an attack. The ExplodesOnHit component registers to listen for "ModifyLife", only instead of performing life calculations, it just triggers an exploding response, querying the environment for all objects in a radius, calculating a damage value and delivering this damage value as "ModifyLife" messages sent to all objects in the radius, then triggering a special effect ("Explode") and finally triggering DestroySelf message to despawn the trapped object.

In this way, I decouple the action (making an attack) from the response to the action (modifying health level, modifying lifebar display, exploding), and not once do I ever care exactly what component or type of component is needed to handle the message.

I wrote a bit more detailed explanation in my journal here.




I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?


I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?


Well, since all my high-level logic is handled in Lua rather than C++, it's not really an issue. I just populate a table and pass that as the arguments for the message.

[quote name='Doggolainen' timestamp='1300317792' post='4786791']
I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?


Well, since all my high-level logic is handled in Lua rather than C++, it's not really an issue. I just populate a table and pass that as the arguments for the message.
[/quote]

Ive made some progress in my "engine" which I nowadays call a game. I've decided to make a few games and then take parts out of them to make an engine (thanks to this forum Ive realized it was a better approach for me).

Anyhow, I've got my self a world divided into tiles. Think of it as and old-school top-view game, where an entity only can move between tiles. I've got query functions in my gameworld such as "GetEntititiesAt(Coordinate position)" which would return all the entities stored in a tile at that position. But how am I suppose to handle return parameters using my message systems?

I'm thinking of implementing some kind of broadcast system. So that if a component needs to know of its surroundings, I let them subsrcibe on the "GetSurroundings" message. And then let the gameworld/someComponent fire that message each frame to let every subrscriber know of their surrounding. Either that or some kind of callback function, so that when an entity asks for its surroundings, they could pass a reference to a list or something which will be filled.

This is the first problem of this type Ive ran into so far, and I expect to see more of it, so I want to have a method of dealing with it asap.

My question in short terms: Do you have messages that also returns stuff?
If yes: How do you handle if several objects would subscribe on the same message?
If no: How do you do it?


//Doggolainen


Edit:
@JJTippets: A quote from your blog that pretty much hits spot on what this topic is about.
http://www.gamedev.net/blog/33/entry-2249433-how-goblinson-crusoe-works-the-tldr-version/

[color=#1C2837][size=2]There is also a function,[color=#1C2837][size=2] [color=#1C2837][size=2]handleRequest()[color=#1C2837][size=2], which acts similarly to[color=#1C2837][size=2]handleMessage()[color=#1C2837][size=2], but which will return a list of results. This is used for queries, and I am unsure on whether or not it is actually needed, as I haven't really used it much.[color=#1C2837][size=2]

[/quote]

This topic is closed to new replies.

Advertisement