Trying to learn Entity Component System

This topic is 591 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I'm trying to learn how the Entity Component System works. I've looked around and probably the best explanation of it I have found is at http://gameprogrammingpatterns.com/component.html

However, the article leaves a few things unclear to me:

1. Should the Entity hold all of its different Components as individual objects or should it hold a List or HashMap of Components that is added to?
2. How exactly should the Entity be constructed? Do you go with overloaded constructors that take in a variable number of predefined Component types? Do you pass it a List of Components upon construction? Do you leave the constructor empty and add components to it later? Something else?
3. How should the Component hierarchy look and how do you accommodate for the potentially different parameters of update methods that different Components may have? For example, some Components may need some kind of graphics object passed to their update in order to work. I think you could give the base Component class/interface a number of different update methods each with their own suitable parameters for different kinds of implementing sub Components but, y'know, that's bloat.
4. How exactly does rendering work in such a system? Ideally I would like to update all the Entities in a List first, then after that render them. However, if the Entities are made up of some kind of RenderComponent for example then I think I would need to call RenderComponent.update() at the end of the Entity.update() for each Entity in the List. I don't know if I like the idea of that. I think rendering should happen only after everything has updated. Also, how would you decide which Entity should render? Not all Entities will necessarily have a RenderComponent.

If what I'm saying seems muddled or confused it's probably because it is. I'm still trying to get my head around ECS having been entrenched for so long in the OO hierarchy mindset. Any help with this would be great. Thank you.

Share on other sites

Should the Entity hold all of its different Components as individual objects or should it hold a List or HashMap of Components that is added to?

Use whatever container system you think works best for your implementation.  Generally the systems allow for an entity to have zero or more components.  If that means a collection of index values, references to other objects, or storing the instance directly, those are implementation details you can decide on yourself.

How exactly should the Entity be constructed? Do you go with overloaded constructors that take in a variable number of predefined Component types? Do you pass it a List of Components upon construction? Do you leave the constructor empty and add components to it later? Something else?

Generally they are created from data, using a factory method. With that pattern both the Entity and the Components should be serializable, or otherwise allow the engine to initialize the entity and the components to a sane state. You have many options, such as handling it all by reflection, or requiring fancy constructors, or something else entirely.

Then you have a data file of some type that indicates what all the stuff is that needs to be loaded. Exactly how it happens is up to you, but typically something that includes version numbers and properly contains all the mandatory values. It should also handle the cases for elements that are no longer present and elements that have no value stored.

Those are pesky little implementation details. Implement it however it best makes sense for your system.

How should the Component hierarchy look and how do you accommodate for the potentially different parameters of update methods that different Components may have? For example, some Components may need some kind of graphics object passed to their update in order to work. I think you could give the base Component class/interface a number of different update methods each with their own suitable parameters for different kinds of implementing sub Components but, y'know, that's bloat.

Hierarchies generally are shallow but wide. A game may have hundreds or even thousands of components, most will either be derived from the root component class directly, or under a tiny inheritance structure.  The engine itself will probably have a small number of specialized components. Perhaps there is a base RendererComponent that has a few children for meshes, planes, cubic and bicubic shapes, deformable meshes, and a few others.  Perhaps there is a base PhysicsComponent that has a few children based on how physics relates with them.

Regarding a component needing something attached to them, that is an unfortunate dependency to write but it happens sometimes.  Add a query function to Entity that returns either a Component of that type if it is found, or null if not.  Then an entity can override that query function if they need to.  Now it is easy enough, ask the Entity to give you a RenderableComponent, or query for whatever interface you require. However, it is generally better to find an alternate solution that doesn't require the tight coupling. Sending messages and using delegate/registration functions can both work as alternatives.

How exactly does rendering work in such a system? Ideally I would like to update all the Entities in a List first, then after that render them. However, if the Entities are made up of some kind of RenderComponent for example then I think I would need to call RenderComponent.update() at the end of the Entity.update() for each Entity in the List. I don't know if I like the idea of that. I think rendering should happen only after everything has updated. Also, how would you decide which Entity should render? Not all Entities will necessarily have a RenderComponent.

Rendering and updating are typically different processes.  Decouple your rendering from your processing.

Rendering should only take place if enough time has passed to actually render.  Updating should only take place if enough time has passed to actually update.  Sometimes neither will happen.  Sometimes only updating or only rendering will happen.  Sometimes both will happen. Sometimes you may have multiple updates happen, although that potentially gets into a death spiral on bad performing machines or slow code.

Often there are multiple functions available for updating objects.  There are updates that take place at regular timesteps for physics and other systems. There are updates that take place at irregular timesteps and may include large jumps.  There may also be functions before/after physics, before/after rendering, before/after network ticks, and so on. The details depend on your project.

As for which entities are rendering, generally you don't render entities but you instead render their renderable components that are active. Often engines will maintain a single data set for models and textures, then they'll pass out references or proxies to that data which are used by Components. When the scene's Entities are processed they can be culled in many ways. Frustum culling eliminates objects that are behind your or outside your viewing area.  Occlusion culling eliminates objects that are hidden behind other items. Consequently you would look through all the Entities in the scene graph, determine if it has any renderable components, determine if the renderable component should be culled or drawn. If it should be drawn it is added to the render queue.  After the scene graph is processed, the render queue is sorted in ways that make sense, such as identifying duplicate calls and merging them so you have a single instance to render, or prevent state changes for textures or shaders that you can run as a batch.

I'm still trying to get my head around ECS having been entrenched for so long in the OO hierarchy mindset.

The two are not at odds.  Entity/Component systems often rely heavily on object oriented functionality.  Unfortunately many people only learn a portion of what Object Oriented really means, thinking it is only about inheritance and visibility, when in fact it is much more broad, dealing with the concepts of a cluster of data that is acted upon or that takes actions, rather than a cluster of data that is data only.

Share on other sites

You will not find easy learn ECS from that site, it is explained really bad (I'm not even sure whoever wrote it actually ever used ECS seriously).

Basically ECS makes easier to create some "function" that works on specific sets of data, but makes harder specific game flow.

?If you want a HackNSlash game, it is enough to know that you need

?- Enemies damage hero (your method)
?- hero damage enemies (possibly another method, but could be the same as well)

ANd you know the only data you need is HP,Damage,Target.

?In regular programming you have

- either 1 parent class, all enemies and hero inherit that class
?- you compose your class by keeping inside a "Attacked" object, but you have to expose for every class a method that just delegates the job to attack class

?In regular ECS you have much less duplicated code

?- You just spawn something with that component (data)
- ?however now the boring part is actually triggering the attack (flow)

Of course if actually you change only stats in your enemies, then you just need 1 Enemy class that load a different graphics and use different stats (And you no longer need multiple classes and/OR ECS).

?ECS comes into play when you have many small stuff that clutter you class, I doubt you will use utility of ECS until you try to make a game that ends up being a tightly coupled mess, when you do that you realize that is the moment you should really start using ECS, also it is easier to do ECS if you have already a code/clear gameplay into mind. Actually if you start needed more damageable stuff (destroyable objects, immortal NPCS etc. it is just much more convenient using ECS than polluting 1 class with Switch statements)

Edited by DemonDar

Share on other sites

I'm sorry but I don't really understand. Would you mind showing me Java code of what the structure of a basic ECS would look like, just so I can get a feel for it? I have tried writing some code for this, but it's a bit of a mess and everything right now feels very disconnected. I can't figure out how to get the things to work with one another.

Share on other sites

Go download Unreal or Unity.  Play with them a bit.  You'll quickly discover what it feels like, more or less.

You have game objects.  You attach components to them.  They include rendering components, physics components, UI control components, locomotion components, AI components, pathfinding components, camera components, particle generation components, audio components, and other components.

You want to build a turret so you make a turret component that turns the attached gameobject toward a goal, then fires a projectile.  If you want to build a tank you put make an object that has a locomotion component that drives like a tank, then add another game object on top of the base that has the turret component.  If you want your projectile to do something you attach a component like an DamageOnImpactComponent, or maybe ExplodeOnImpactComponent.

Share on other sites

I worked on it a bit more and this is what I have come up with so far for a basic ECS structure. There are a few things I'm missing in terms of my understanding. The comments in the code show where I'm unsure.

This is the main class. I have a List of Entities which are iterated over every frame and a method to create them.

public class EntityComponentSystem extends ApplicationAdapter
{
private List<Entity> entities;

@Override
public void create()
{
entities = new ArrayList<Entity>();

createEntities();
}

@Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

for(Entity entity : entities)
{
entity.update();
}

// Should rendering be done here? If so how?
}

void createEntities()
{
{
new PlayerInputComponent(Input.Keys.W, Input.Keys.S, Input.Keys.A, Input.Keys.D, Input.Keys.E),
new GraphicsComponent(new Texture("image.png"))
},
new Vector2(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2), 5, 0));
}
}


The Entity holds a List of Components. At the moment it also holds a position, speed and angle which are public. This is because I don't know how else the Components that need them, such as PlayerInputComponent, can access these members. How might these be more appropriately broken out into their own Components so that all Components that require them can access them without leaving them exposed?

Is the way I have set the constructors up correct?

You see as well I have a potential problem in update() if the various Components do not all have a uniform update() that just takes an Entity. If some Components need more parameters, how do I accommodate for that?

Also I'm unsure if the Entity should hold its own render() method.

public class Entity
{
private List<Component> components;
public Vector2 position;
public double speed, angle;

public Entity()
{
components = new ArrayList<Component>();
position = new Vector2(0, 0);
speed = 0;
angle = 0;
}

public Entity(Vector2 position)
{
components = new ArrayList<Component>();
this.position = position;
speed = 0;
angle = 0;
}

public Entity(Component[] components)
{
this.components = new ArrayList<Component>();
position = new Vector2(0, 0);
speed = 0;
angle = 0;

for(Component component : components)
{
}
}

public Entity(Component[] components, Vector2 position)
{
this.components = new ArrayList<Component>();
this.position = position;
speed = 0;
angle = 0;

for(Component component : components)
{
}
}

public Entity(Component[] components, Vector2 position, double speed, double angle)
{
this.components = new ArrayList<Component>();
this.position = position;
this.speed = speed;
this.angle = angle;

for(Component component : components)
{
}
}

public void update()
{
for(Component component : components)
{
// All Component update() methods must have uniform parameters for it to work this way
// That might not be feasible if some Components need other resources such as
// the GraphicsComponent which might need a SpriteBatch passed to it
component.update(this);
}
}

// Should Entity contain a render() method?

public void addComponent(Component component)
{
}

public void addComponent(Component component, int position)
{
}

public void removeComponent(int position)
{
components.remove(position);
}
}


The Component interface.

public interface Component
{
void update(Entity entity);
}


You see here how the PlayerInputComponent needs certain other aspects not belonging to it such as position, speed and angle. So far I can only make this work if they belong to Entity and are public. How can I fix this?

public class PlayerInputComponent implements Component
{
private int up, down, left, right, rotate;

public PlayerInputComponent(int up, int down, int left, int right, int rotate)
{
this.up = up;
this.down = down;
this.left = left;
this.right = right;
this.rotate = rotate;
}

@Override
public void update(Entity entity)
{
if(Gdx.input.isKeyPressed(up))
{
entity.position.y += entity.speed;
}

if(Gdx.input.isKeyPressed(down))
{
entity.position.y -= entity.speed;
}

if(Gdx.input.isKeyPressed(left))
{
entity.position.x -= entity.speed;
}

if(Gdx.input.isKeyPressed(right))
{
entity.position.x += entity.speed;
}

if(Gdx.input.isKeyPressed(rotate))
{
entity.angle += 0.1;
}
}
}


The GraphicsComponent just takes a Texture but I'm not sure what the point of that would be if it doesn't render it. Should it render anything itself?

public class GraphicsComponent implements Component
{
private Texture texture;

public GraphicsComponent(Texture texture)
{
this.texture = texture;
}

@Override
public void update(Entity entity)
{
// Should rendering be done here with a SpriteBatch?
// If so how would you give this Component a SpriteBatch?
}
}


For both PlayerInputComponent and GraphicsComponent, is it okay that their constructors takes parameters? Is it generally okay for Components to take parameters in their constructors?

That's all I've done so far. Basically I don't know how to properly encapsulate certain data in the Components and have it be accessed by other Components that need it, in a correct manner. I'm also at the moment clueless as to how rendering can be done and how to accommodate different Component update() methods.

Hopefully someone can clarify this stuff for me. It would be much appreciated.

Share on other sites

You're missing the S en your ECS. Systems hold that 'update' logic, not components.

In ECS, entities are lightweight (maybe just an int ID), components are lightweight (only data), and systems contain the logic that operates on both (your 'update' there). In component-oriented, components may be more heavyweight, containing both logic and data. Then again this is a bit of

Anyway, turns out that most logic doesnt only needs only one component, so even if you do put your logic in the component, it starts to depend on other components to work because nothing is that simple. Then you got some piece of code using 3 different components, and you wonder where the 'update' function belongs to really.

I got my own ECS I've been maintaining for a while https://github.com/dustContributor/dustArtemis And a journal (in my signature) that explains some of the stuff I've worked in there. There was a tiny ECS lib called "Artemis" that spawned a bunch of forks, mine among them.

Basically the structure of dustArtemis is composed of:

World: The whole simulation with all the entities, systems and components.

Entities: Which are plain ints.

EntityObservers/EntitySystems: These process a subset of the world's entities (or none at all).

Aspects: Which define the "shape" of an entity, ie, which components an must possess for it being processed in a particular observer.

Componens: Which can be anything, they're tagged with a "Component" interface you'd implement for making one.

When you create an observer, you can define an aspect it expects entities to match. There is a filtering stage where the World notifies each observer about the added/removed entities since the last update, and each respectively filters them using their Aspect instance. So for instance, my spatial submitter, wich is the system that creates render tasks and queues them for rendering, has an aspect like this:

Aspect.all(Spatial.class, Geometry.class, Material.class)

This means that **only** entities with all of those components will make it to the system's active entity list.

The filtering stage is very important, otherwise you end up with systems iterating over all the entities in the simulation doing silly logic like:

Spatial s = entity.get(Spatial.class);
Geometry g = entity.get(Geometry.class);
Material m = entity.get(Material.class);

if( s != null & g != null && m != null)
// do stuff

Whereas with filtering you got:

Spatial s = spatials.get(entityId);
Geometry g = geometries.get(entityId);
Material m = materials.get(entityId);

// do stuff

No checks since the entities have been already filtered and only make it to that point if they match the Aspect criteria.

Components are stored in a "component manager" that shells out ComponentMapper<ComponentType> instances. These objects can map an entity (an int) to a component. In the example above you'd have something like:

private ComponentMapper<Spatial> spatials;

Defined in your system, which gets injected at runtime with the proper instance.

I'm against making entities a "full" object because since Java lacks structs, you're unable to define a compact flat shape for that object. You either end up with some HashMap or ArrayList inside listing all the entity's components, HashMap being a particularly heavy structure for such low amount of data you'll have per entity, and that just adds layers of indirection to it. Besides, HashMap accesses start to pile up and appear on the profiler (for some workloads) if you're fetching each individual component with them.

Those are a few surface details, you're welcome to look at the code. I'm shifting some roles around in the 'entity-filter' branch right now, so that one will be changing a bit. 'master' should be fine.

Edited by TheChubu

1. 1
2. 2
3. 3
4. 4
5. 5
Rutin
11

• 12
• 16
• 9
• 14
• 10
• Forum Statistics

• Total Topics
632658
• Total Posts
3007687
• Who's Online (See full list)

There are no registered users currently online

×