Entity Component System questions/tips

Started by
20 comments, last by Randy Gaul 9 years, 10 months ago

Question is what a property means. Using a CES is a bit like programming. Do you want to use a vec3 or instead a float, another float, and a third float? Do you perhaps even want to use a Position instead a vec3? Do you perhaps even want to use a Placement instead of a Position and Orientation separately? Looking at DCC packages, animations can be modeled on each single component of position, Euler orientation, scaling, or whatever. That gives you best flexibility, but also burdens you to deal with them each separately.

I personally define data component (in the meaning of CES) as complete units with a semantic, e.g. I prefer a PlacementComponent. Such components provide properties, in the example a Placement with its own properties Position and Orientation, that can be accessed individually. Any client of the PlacementComponent can deal with the Placement property or, if knowing its composition, with Position and/or Orientation. The various manipulators like ParentingComponent, TrackingComponent, … do it this way.

So from a users point of view components are higher level constructs that may (depending on the component type) be configured to drive all or just a part of the target (e.g. the ParentingComponent should just drive the Position but not the Orientation). This allows for providing not so many component types and, due to the inherent semantic, does not demand a so deep abstract thinking as would be with two vec3 or even 6 floats.

Advertisement

One part that I would spend more time on is determining how to build the objects (layout). I know phil_t suggested using a text file and this answed your question but you need to have more questions in this area. I see very little point to using an ECS if you're going to hard code how your objects are created. You need somekind way to define your objects and then created them using that define / template.
Here is a really simple lua script I just rattled off, not how I would do it but it gets the point across.

defines =
{
truck_heavy_red =
{
physics = { weight = 10, max_speed = 25.0 },
render = { model = 'trucka', texture = 'foo.dds' },
}
}


game:loadDefines( defines )

game:createEntity( 'truck_heavy_red' )

Also the ids to index the array is fine and should be used in conjunction with somekind of weak reference system. You will want some references to an object to get invalidated when they are destoryed. Think of your tank having a reference to it's target. I use something like the method described in Game Gems I.


I would also have another class called Game that contains your World. I put stuff like my modes in there (GameMode, PausedMode, HelpMode etc). Also access to some systems like lua, game options and UI. I treat the World as the simulation only. Just a thought.

Okay, thanks guys. I think I have a better understanding that will help me get started. I think now I'll have to start coding and allow myself to get dirty and make mistakes to make the next step in learning. Thanks a lot!

Okay, I'm making some baby steps but now I've got a question. What are some of the ways I can select an entity based on the value of a component?

Example:

Say the user picks up a color bomb and uses it. They pick a color and all enemies with that color blow up.

Say I have a component (A) that has several properties, one of them is color.

Say I have a 'world' object responsible for the world and ideally, as game independant as possible.

Say I have a 'game' object that has the world and is responsible for game specfic logic.

My first thought is there's a function (J) responsible for impleting this task somewhere inside my game object and is called when nessessary. J calls a world method that returns a list of all entities with component A (function K). K queries the entity manager for said list and returns it (K is a middle man). Now J has a list of entities with component A and can filter out those that do not have the desired color and do its thing.

This seems either like an odd way to do it, or more complicated than it needs to be; so I'm curious as to the different ways to do this.

Here's how I would do it, based on my ECS implementation:

I'd have some code in the bomb system that checks to see if a bomb has been "used". If so, and if it's a color bomb, then it would need to obtain the list of all entities that have component A with a matching color. So your ECS needs a good way of saying "enumerate the entities that have this component".

In my current implementation, systems support this functionality. My systems can declaratively state "I care about all entities with component A". The system would have a list of all entity ids that have component A, and this list is kept updated by the EntityManager automatically. So the bomb system can simply ask the "Component A system" to enumerate this list (this could be done by some messaging system, or more explicitly by giving the bomb system a reference to the "Component A system").

So the bomb system would simply enumerate this list, get the A component for each entity (which is guaranteed to be there), and check for a matching color. If a match, then it "does its thing" with that entity.

I'm interested to see how others would approach this.

Alrighty, I have a question.

How do I make entities 'belong' to other entities? Such as 'this tire belongs to this car'.

In OOP:


Tires:
    class AllWeather....
    class Winter....
    class Slick....
    class Drag....

Car:
    tires = new Slick();

So if I want to get the tires the car is using I would do something like 'tires = car.tires'.

What are the ways I can do this in ECS? I'm thinking I should add a new component (belongsTo) that I can add to entities (such as tires) that contains the entity ID of the entity that the entity belongs to. Does anyone see any problems with me doing that or have any better ideas before I go to far down this path?

*** Edit ***

I have come up with another question (I'll use the same example as above, but I'd like to have answers to both questions).

Let's say rather than being entities, a tire is a component that is built from other components (this makes more sense as a tire isn't actually a game object per se but a component of a car). How do the various ECS implementation handle having multiple components that are the same (IE: a car has 4 slick tires installed)? And since we are on the subject of components made of components, is this a good idea or should it be avoided (IE: should I have a car entity make of a body & tires components, and each body and tire component is made of smaller components)? This seems like like a stepping stone toward OOP.

To sum up:

* What are the various ways to handle when an entity belongs to another entity? (This sounds to me like one of the entities should be a component, but the question stands.)

* What are the various ways to handle when an entity has multiple of the same component?

* Is having a component made from other components a good or bad thing?

In OOP:

...

Well, it looks like using inheritance is the OOP way to distinguish different types of tires. That is possible, but necessary only if you need to use different code for different types of tires (especially if you want it being expandable later on). A better way (if possible at all) is to describe the features of the different types of tires by parameters (keyword "data driven"); e.g. you have a small couple of friction co-efficient for some grounds.

What are the ways I can do this in ECS? I'm thinking I should add a new component (belongsTo) that I can add to entities (such as tires) that contains the entity ID of the entity that the entity belongs to. Does anyone see any problems with me doing that or have any better ideas before I go to far down this path?

ECS is a fancy name for composition where the "top level" is a kind of game object. Hence it is possible to go several ways. For example (without claiming to be complete):

1a.) Making the wheels being entities on their own is of course a valid solution. It would require to express the attachment as you already have stated. In the given case a relational component may be used: A Parenting component with the wheel as child and the vehicle as parent. Because the relation has explicit links to the both entity roles, both the vehicle and the wheels as entities are stored side by side.

1b.) The wheels are entities by their own again, but the relation is expressed as in a scene graph: The wheel entities are arranged inside the vehicle entity (means entities can be components). Perhaps this approach has the advantage that the vehicle occurs as a unit. However, there are disadvantages:

* Although there is an implicit expression of the child and parent roles, there is still the need to store the local placement of the child. If a Placement component is used to express the global placement, then a child component needs a LocalPlacement in parallel, but if a Placement component is used either as global or as local component, then its meaning depends on the use case of the entity.

* Parenting is not the only relation you may want to express (I'm using a dozen or so). So why should parenting be implemented by nesting, while all other relations then need to be explicit and connect in a general graph like structure?

2.) Wheels are not entities at all: Assume that there is a component called Chassis. Adding a Chassis to an entity declares it being a kind of vehicle. Let's assume there is a derived component type like FourWheels, obviously supporting 4 wheel vehicles. It is parameterized with e.g. wheel base, track width, tire-to-ground friction, and perhaps other things.

3.) A mix of 1a and 2: Wheels are given as own entities side by side to the vehicle. They are related to the vehicle each by a Parenting component. The vehicle entity has a FourWheels component that is the reverse relation from the vehicle to the wheels, controls the wheels' local Placement components, defines the physical parameters and hence tags the entity as vehicle.

--

I personally prefer option #3. It does not enforce a special handling of a kind of relation, still allows each single wheel to have its own graphical components (for example), and encapsulates behavior in the form of Chassis.

More questions, yay.

I'm looking for a way to efficently implement 'nodes'. 'Nodes' being nodes in Ash, or Aspects in Artemis; that is to say, they are a collection of components an entiy must have for a system to affect said entity. EG: The movement system affects entities with position and velocity, so entities with those components have a 'movement' node which the movement system then effects.

Background:

So, my ES implementation is setup like so:

  • I have a World object that has an entity manager and a system manager.
  • The system manager keeps track of the various systems (that's about all I have right now, as I'm just starting to work on systems now).
  • My entity manager manages entities and their components. It has a hash that takes in a component name and returns a component manager. It also has another hash that takes in a component name and return's a component ID. A component manager manages its (one type of) component. Basically, it keeps it's component storage (an array) as densely packed as possible (to save memory) and translates a component ID to the actual component data structure. The end result is that entity #5 may have position #0 (which the position component manager may translate to be x=100, y=50) and velocity #8 (which the velocity manager may translate to be x=-3, y=10).

The problem:

I originally designed the component manager as I did to keep the array densly packed to save memory and to speed up system execution. However, system execution is now complicated because the components that belong to an entity can be spread throughout the various component arrays (as seen above). This means, as it stands, the movement system can't take velocity at index 1 and apply it to the position component at index 1 because they can belong to different entities.

However, I really like the space savings offered by my current implementation of my component manager. If I have 3,000 entities and each entity only has 3 components, then, on average, each array is 1,000 big rather than the more naive each array is 3,000 big (as an example, I'm just throwing out numbers).

My solution:

The solution I've come up with is to implement 'nodes', data structures that contain components and have a one-to-one relationship with systems. EG: The movement system requires movement nodes, that contain the position and velocity components; the render system requires render nodes, that contain position and sprite components; etc.

Every time an entity is created or changed (components added/removed) a 'updateNodes' function is called that will update the nodes belonging to that entity. This means if an entity has a position, it does not have a movement node (requires position and velocity); however, when the entity has a velocity component added, a movement node is created which is then used in the movement system. Likewise for velocity being removed, the movement node is removed/destroyed and is no longer affected by the movement system.

Components in a node are pointers. This way if a component is altered (such as by the movement system), the component data is altered and is correct for all the other nodes that use that component (such as the render node, which is used by the render system and has the updated position value).

The problem I have with this potential approach is that it destroys caching. No longer can systems run along an array and update components and have prefetching work (for lack of a better term), because of the indirection I am now jumping around in memory and have lost a key aspect of Entity Component Systems.

To get around that, I could have nodes have a copy of the data; but now I run into the problem of 'what if a system changes a node's component, it's no longer the same for the other nodes that use the same component'.

So, as always, I'm curious as to what other people have done.

Hi there, I'll try sharing a little bit of experience I've had. Worrying about prefetching and cache coherency isn't really all that helpful for hobbyists. Generally bottlenecks for newer game developers come in the form of using graphics APIs poorly (draw calls usually), or using an N^2 collision detection broadphase, and one of these is a non-issue if Box2D (or Bullet) is used. If these two thing are taken care of many 2D games for PC can implemented in C++ in a pretty memory naive way before seeing any problems. I've seen some impressive looking 3D games come out of my university with fairly naive memory implementations.

So while it's fun to read popular blog posts about ECSs, the practicality of making an entire game engine with just components and entities gets pretty silly. Usually people end up using Box2D for physics, and this library for that. In the end a lot of components just end up as simple wrappers for pre-existing libraries anyway.

Usually the interesting parts of component based architecture are the ease of data driving and serializing things. Editors and the like become very game design friendly, and iteration times get pretty low. These aspects don't have much to do with performance, and usually middle-ware or libraries will have their own internal optimizations anyway.

Luckily I decided to pop into this forum for the first time today as I actually just finished writing an article you can take a look at for some further reading (it really just expounds upon what I've said here): http://www.randygaul.net/2014/06/10/sane-usage-of-components-and-entity-systems/

I'm not actually worried about efficiency, it's simply a problem I came across and what curious as to what other's have done to remedy the situtation.

The reason I am implementing my own solution and not using a framework such as Artemis, is that I am learning about ES/ECS. I have a much better understanding of something when I implement it so that when I use it I know what is going on in the background; or so I can make a more informed decision between frameworks; or any number of situations that I could find myself in where that understanding could be helpful.

So, while I may not be worried about efficiency, this is still a 'design' decision that can have serious repercussions down the line (if this were a real framework that I were to use). As such, I'm curious to what other's that done to provide a broader understanding; after all, someone could have thought of a solution that I never would have thought of.

This topic is closed to new replies.

Advertisement