Events and Entity Component Systems

Started by
6 comments, last by NEXUSKill 10 years, 11 months ago

Hey guys,

I'm currently working on an HTML5 top-down simple RPG game, and as a learning experience I'm building my own little engine from scratch. After reading around a little I decided to go with the Entity Component System pattern because I like the flexibility and reusability it brings.

I have a few questions I hope you could help me with:

First, there seems to be some disagreement as to what an ECS actually is - some articles have it that components are just containers for data and subsystems handling all the logic, and other articles have it that components contain methods in them and communicate via some sort of shared memory or messaging system. What's the most widely accepted model of an ECS?

Secondly, I've been wondering about in-game events. I'll explain:

So I started developing an engine that follows the "components are only data containers" rule, and subsystems operate only on the entities that match their requirements (for example, subsystem A takes all entities that have "physics" and "render" components, and subsystem B takes all entities that have "physics" and "health" components).

I'd like the game to be easily scriptable, so that in the yet-to-exist level editor, I could link a particular item (e.g. vase) to another (e.g. mob spawner) and create some sort of correlation (e.g. when vase is broken the spawner spits out a monster). With that in mind, I added the following to my Entity class (which, until now, was just a container for components):

  • Event Trigger - has a "name" field, "predicate" field, and a "data" field. There would be a special subsystem that goes through all entities that have event triggers, check the predicate, and send the event, along with its data if the predicate evaluates to true, to anyone who's listening.
  • Event Listener - has a "name" field and a "callback" field. If the entity receives an event it is listening to, it will invoke the event listener's callback (which will be able to do something like changing the "spawner" component to be ready to spawn a monster the next time the appropriate subsystem looks at it).

This pretty much solves the problem I described above (vase + spawner, also allows for scripting events with relative ease). I don't like, however, that there are two ways to "change" components (subsystems and/or events). I usually like that there's only one way to do things, and that one way should be the best way. What do you guys think?

Now, the next problem deals with a different type of event. Say I have a player swinging his sword at a monster; when the "physics" subsystem recognizes that the sword collided with the monster, it should notify the "health" subsystem and the "sound" subsystem of the event, so that health could be decreased for the monster and so that a hit sound could be played.

So the obvious, brute force solution would be to have subsystems sending events to all other subsystems (which is what some ECS articles suggested). The physics system would send an event "entity-A collided with entity-B" every time two things would collide to all other subsystems. Then the health subsystem would check whether it cares about the event and do something about it.

I'm worried about this approach because it seems very computationally intensive and might be a hit for performance. Also, HTML5 doesn't have threads (except web workers but meh...), which would make this easier. I'm just afraid that the sheer amount of messages going between subsystems would be enormous. Also, this approach makes my engine have two completely distinct type of events (one that is inter-entity and one that is inter-subsystem) which is yucky.

What do you guys think? Any suggestions would be very welcome :)

Thanks,

Xaan

Advertisement

First, there seems to be some disagreement as to what an ECS actually is - some articles have it that components are just containers for data and subsystems handling all the logic, and other articles have it that components contain methods in them and communicate via some sort of shared memory or messaging system. What's the most widely accepted model of an ECS?

Good question! I think there has been a time in past in which "component system" was meant to be somehow the opposite of "entity system"... those terms now go toghether. You know, there's a programming bandwagon to jump on and people just write about it. Welcome to web2.0, where everyone is allowed to write things they think they are right.

Personally I don't think having components as pure PODs is viable. Unless we consider callbacks to be PODs (which might happen to be on some cases).

I don't think there's a widely accepted "model", it seems people can't even agree on terminology first, let alone on a model. I've read some articles in which they suggested to somehow "aggregate" the components into "entities" whose type would "emerge" from the aggregated components. Ok... good for them. I keep them separated instead, which seems to me similar to what component systems were meant to be in the beginning, it solves plenty of problems IMHO.

Except it really needs callbacks. Well, not quite. But they are very convenient.

Now, the next problem deals with a different type of event. Say I have a player swinging his sword at a monster; when the "physics" subsystem recognizes that the sword collided with the monster, it should notify the "health" subsystem and the "sound" subsystem of the event, so that health could be decreased for the monster and so that a hit sound could be played.

I wouldn't make "health" a component at all. This is how it would look in my system:

// What happens: Sword implements MeleeWeapon protocol. This is received by the hit entity.
public bool BeginTouch(bool base, CollisionInfo cinfo) {
  MeleeWeapon itHurts = safe_cast<MeleeWeapon>(cinfo.other);
  if(itHurts) health -= itHurts.GetDamage();
  return false;
}

I hope you can understand the logic behind it. Notice this doesn't stain your component system with vague, gameplay-specific things as "health".

I'm worried about this approach because it seems very computationally intensive and might be a hit for performance.

I think you shouldn't worry. As long as you keep everything event-driven and offload compute-intensive tasks to native code, you will find you can easily keep it under 2% of CPU time.

However, if you poll all conditions each frame then yes, this will hurt somehow. On desktop systems it might still be viable however.

Previously "Krohm"

I wouldn't make "health" a component at all. This is how it would look in my system:
// What happens: Sword implements MeleeWeapon protocol. This is received by the hit entity.
public bool BeginTouch(bool base, CollisionInfo cinfo) {
MeleeWeapon itHurts = safe_cast(cinfo.other);
if(itHurts) health -= itHurts.GetDamage();
return false;
}

Where is "health" stored? On what object does BeginTouch exist?

I hope you can understand the logic behind it. Notice this doesn't stain your component system with vague, gameplay-specific things as "health".

Pardon me, but isn't entity component system predestinated to represent such gameplay-specifiy things like "health"? I do agree that there shouldn't be a health-component by itself. In my current implementation, I have a collection of basic components like transformation, render, light etc.. offered for interaction with the engine, and then in the actual game I define gameplay-specifiy components - for my current tower defense project that is a tower component, a unit component, a projectile component etc... this is where I store health - alongside the gold recieved upon kill, the run speed, and the damage the unit deals when it reaches the players base, the health is stored as a member of the "Unit" component.

I can agree with most of the rest, especially that different people have lots of different views on ECS, but that one sentence just appeared a bit intriquing to me...

Hey guys,

I'm currently working on an HTML5 top-down simple RPG game, and as a learning experience I'm building my own little engine from scratch. After reading around a little I decided to go with the Entity Component System pattern because I like the flexibility and reusability it brings.

I have a few questions I hope you could help me with:

First, there seems to be some disagreement as to what an ECS actually is - some articles have it that components are just containers for data and subsystems handling all the logic, and other articles have it that components contain methods in them and communicate via some sort of shared memory or messaging system. What's the most widely accepted model of an ECS?

Secondly, I've been wondering about in-game events. I'll explain:

So I started developing an engine that follows the "components are only data containers" rule, and subsystems operate only on the entities that match their requirements (for example, subsystem A takes all entities that have "physics" and "render" components, and subsystem B takes all entities that have "physics" and "health" components).

I'd like the game to be easily scriptable, so that in the yet-to-exist level editor, I could link a particular item (e.g. vase) to another (e.g. mob spawner) and create some sort of correlation (e.g. when vase is broken the spawner spits out a monster). With that in mind, I added the following to my Entity class (which, until now, was just a container for components):

  • Event Trigger - has a "name" field, "predicate" field, and a "data" field. There would be a special subsystem that goes through all entities that have event triggers, check the predicate, and send the event, along with its data if the predicate evaluates to true, to anyone who's listening.
  • Event Listener - has a "name" field and a "callback" field. If the entity receives an event it is listening to, it will invoke the event listener's callback (which will be able to do something like changing the "spawner" component to be ready to spawn a monster the next time the appropriate subsystem looks at it).

This pretty much solves the problem I described above (vase + spawner, also allows for scripting events with relative ease). I don't like, however, that there are two ways to "change" components (subsystems and/or events). I usually like that there's only one way to do things, and that one way should be the best way. What do you guys think?

Now, the next problem deals with a different type of event. Say I have a player swinging his sword at a monster; when the "physics" subsystem recognizes that the sword collided with the monster, it should notify the "health" subsystem and the "sound" subsystem of the event, so that health could be decreased for the monster and so that a hit sound could be played.

So the obvious, brute force solution would be to have subsystems sending events to all other subsystems (which is what some ECS articles suggested). The physics system would send an event "entity-A collided with entity-B" every time two things would collide to all other subsystems. Then the health subsystem would check whether it cares about the event and do something about it.

I'm worried about this approach because it seems very computationally intensive and might be a hit for performance. Also, HTML5 doesn't have threads (except web workers but meh...), which would make this easier. I'm just afraid that the sheer amount of messages going between subsystems would be enormous. Also, this approach makes my engine have two completely distinct type of events (one that is inter-entity and one that is inter-subsystem) which is yucky.

What do you guys think? Any suggestions would be very welcome smile.png

Thanks,

Xaan

FWIW, I have used both ECS systems, where Components held data and logic and sent events to other components, and I've also worked with ECS where components are data only and systems provide the logic for components, or entities with multiple components. To suggest which one to use is partial personal preference, but also the type of game you will be making. IMO, games with many characters, enemies, items, etc. should stick with the components as data only model; it will make creating characters, enemies and items much simpler in the editor.

For your scripting issues, I wouldn't advise adding special interfaces to the entities; I'd look at creating components that provide that detail. Basically, your vase could have a trigger component; when something breaks the vase, the trigger component is activated, and the trigger subsystem can read the data from that component and perform the monster spawning. Something like that.

So the obvious, brute force solution would be to have subsystems sending events to all other subsystems (which is what some ECS articles suggested). The physics system would send an event "entity-A collided with entity-B" every time two things would collide to all other subsystems. Then the health subsystem would check whether it cares about the event and do something about it.

Why not, instead, modify the health component with a variable saying it has been hit by some component and should apply X damage. When the Damage system runs, it will handle the hit with playing the proper sound (if the entity was a "sword", it may play a slash sound, or if it's a punch, maybe a "grunt" sound) and decrements the health. Something like that. no events needed, just modifying data in components for a system to act upon it.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Where is "health" stored? On what object does BeginTouch exist?

BeginTouch is a callback provided to RigidBody component to signal contact. Health is contained in the registered object.

Pardon me, but isn't entity component system predestinated to represent such gameplay-specifiy things like "health"? ... that one sentence just appeared a bit intriquing to me...

You note two classes of components in your system, those provided by the engine and those defined in scripts... those two are very different beings and still go in the same terminology. What I got from the various "component" papers is that those "components" have special logic associated to them. Gameplay-specific concepts cannot have special logic associated by definition. In your message, the "core components" you provide as example are not tied to gameplay either (neither are components by Unity), they have a special meaning for the engine and this is the property that allows it to "dispatch" them. What you refer as script component was meant to be called property in my system. So far, properties have not been implemented and likely won't be any time soon.

In "evolve your hierarchy" Mick West writes:
In this final arrangement, an object is simply the sum of its parts. Figure 2 shows a scheme where each game entity is comprised of a collection of components. There is no "game entity object" as such. Each column in the diagram represents a list of identical components, each row can be though of as representing an objects. The components themselves can be treated as being independent of the objects they make up.

And this is the only thing I believe makes sense when talking about component systems. All the rest is to be discussed with a design document at hand. It's really thin air.

Previously "Krohm"

This is one of the biggest problem with component systems, how the components communicate when they don't know about each other.

This problem is often resolved with an internal message system. Unfortunately it does not work well for all cases.

A solution for the health problem, can be that all objects that wants collision information sends a reference to a collision data structure, which is part of the entity, to the physic system.
Then the physic system, fill collision data for entities with collision data.

Now you can just check this collision information in each update, and call the sound and health system.
The problem with this solution, is that some entities may want more information about the collision than others, so unused information may be stored.
Hence, this breaks the idea of component based design.

Another solution is to make a query based physic system. Where entities can query for collision information. You can then solve the problem with unused information by creating different types of queries. The query system can also be used for ray tracing etc.

Both solutions require update functions, so if you want a very strict implementation of ECS these solutions is not appropriate.

A component should be something that is self sufficient, the collision body structure, the graphic representation, the inventory... and so on.

They should be able to do what they do without needing to know what other components the entity has.

For the comunication between them, for adding them toghether to make something grater than the sum of its parts is the entity, the base implementation of which is a dumb component container.

The entity through inheritance can then become an actor, which combines its collision, controller and inventory system to pick something up from the floor and put it in a backpack, or perhaps that is even more specific, like character, which inherits from actor. The actor knows that it has a volume that contains it well for visibility purposes, a different volume (or structure of volumes) that represents it best for collision purposes and submits each of them to the corresponding system when it is initialized.
The properties of the actor, that make it either an archer or a knight, come from its content only, and may add specific functionality to it through behavior components.

Components are usefull but going full force with them is like trying to make something entirely out of sugar, sugar is great, surely, but without flour and other ingredients there is only so much you can do with it.
Systems are great, components are great, inheritance and composition are great, object oriented and data oriented designs are great and they should all work toghether to make a great game. Don't go evangelic on any new trend like ECS.

Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272


This topic is closed to new replies.

Advertisement