Entity Component System questions/tips

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

Hey guys, I've been doing a lot of reading on Entity Component Systems and I figured I'd give it a go. I have a basic design layout and I was hoping to get some feedback, both to help out a beginer with this new design implementation and to so I can see people's thought processes.

Here's what I've come up with (sorry, no code right now):

Entities:

  • Just a Universally Unique ID (UUID).
  • Since an entity is a UUID, this will be implemented within an entity manager (see below).

Entity Manager:

  • Manages entity creation and removal.
  • Entities are stored in an array (maybe, this seems redunant as the UUID will act the array index but I will need someway to store created entities, right?).

  • Each entity has it's own creation method where an entity is created and components are created and assigned to the newly created enity. This is so it's a simple function call to create as many tanks, jets, unicorns, sharks with freaking lazer beams, etc as desired.
  • This means the entity manager will be keeping track of components and whom they belong to. To keep it fast and simple, I figure an array for each component where the entity's UUID can index into said array(s) to get it's components. This way systems can simply iterate over the array(s) it needs and to make use of pre-fetching and caches.

  • Entity removal will cause the UUID to be thrown into a list of some sort so that it can be recycled (on entity creation). This will prevent the UUID from becoming impossibly large should a large number of entities be created then destroyed.

Components:

  • Data only.
  • A list of components is held by the entity manager (see above).

Systems:

  • One system per aspect (physics, AI, etc). These are independed of each other so it should be trival to put them in different threads if desired.

System manager:

  • Primary use is to be a black box to hide the data structure containing the systems (array, vector, linked list, who knows).
  • This will also make it easy to have each system in a thread (if desired), in the add method just start a thread for the system, or add it to a data structure that is iterated over every frame, or whatever).
  • To keep it simple and easy for now, it'll be a vector.

  • This can also provide a way to prioritize systems, pause systems or outright remove them (though I'm thinking this is probably a stupid idea; I can't really think when I'd want to remove a system outright or even to just pause it).

  • I'm not even sure If need this. It's a 'nice-to-have' in that it helps seperate world functionality (see below) from system specific functionality. But this could be my OOP background nagging me.

World:

  • There's only one world so this will be an object (as in OOP).
  • Will contain an entity manager and system manager.
  • Will have methods to load & save the world.
  • Will contain the main loop.
  • Will behave as everyone elses world object (I'm sure).

I think this is a pretty decent design (to start with) but I'm curious as to what you guys think. After all I could be heading toward a world of hurt and not realize it or be on my way.

I do have one question though. My entity manager contains the list of components used by the entities. This is because I would like to go EntityManager.tank(...), EntityManager.SharkWithFreakingLaserBeams(...), etc rather than manually type: create entity; create componenets; assign components to entity; each time I want to create something (the only difference is what components are created because it depends on the entity, so why not automate it?). However, this leaves me in a bit of a pickle for systems. Since they are two are seperated objects, the system manager does not have access to the list of componenets that it needs to iterate over and do its thing.

What would be considered the best idea to solve this (yes, I know there's a hundred different ways I can solve it). I'm thinking of something like:

  • The entity manager has a 'get components' function(s).
  • The components are fed into the system manager's update function.
  • Each system takes the components it needs to work on and iterates though them, doing it's thing.

That's probably the easiest solution I have, for the design I have; but I don't think it's upgradable. IE: What if I want to change the system manager from a data structure to a thread creation implementation. Ideally I can simply make the change in the add/remove methods of the system manager, but now the solution to give the system manager the list of components goes out the window since the system manager no longer has an update function (each system is in it's own thread so it'll be best to let it deteremine when it needs to update since some systems will be likely to run at different times. EG: render all the time, update at the frame rate).

Advertisement

It looks quite a bit like Artemis ECS Framework

You might want to check the sources, it can give you ideas on how to handle indexing, sending entities to each system, etc. Have in mind that most of it is quite thread unfriendly (ie, quite a bit of the World/Entity methods have lots of side effects).

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

You have to remember the other systems that will be involved in your game like the AI System etc.
The simple case of Actor/Game Object/Component/ class is just a way to generalize the way you organize a Scene/GameWorld.
The book Game Engine Architeture goes in detail about that and if you've not read I recommend you do cool.png .


This is because I would like to go EntityManager.tank(...), EntityManager.SharkWithFreakingLaserBeams(...), etc rather than manually type: create entity; create componenets; assign components to entity;

There's no reason to clutter your EntityManager with these methods. Your EntityManager should lean more towards the engine side of things, while the game-specific code like assembling prefabs like tanks can exist somewhere else. Or you could make some simple text format that can be parsed to create prefabs.


However, this leaves me in a bit of a pickle for systems. Since they are two are seperated objects, the system manager does not have access to the list of componenets that it needs to iterate over and do its thing.

The systems can just request the component lists they need from the entity manager (or the code that creates the individual systems can pass the necessary component lists - but then changing the implementation of a system might requires code changes in two places).


each system is in it's own thread so it'll be best to let it deteremine when it needs to update since some systems will be likely to run at different times

Your systems will need to be updated in an explicit order, so I don't think you can just say "they will run at different times on different threads". If you end up putting some systems on a background thread, you'll need to think very carefully about how you do it.

You might consider not building any general threading functionality into systems/component lists at all (it seems like this would require very complex and and potentially performance-impacting sync mechanisms), and instead make it an implementation detail of each system.

You shouldn't be too concerned about "recycling" UUIDs. One "popular" size for UUIDs is 128 bits which is something like 3*1038 numbers. Even a 32 bit id will provide 4 billion IDs. How big a game are you making, anyway?! biggrin.png

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Okay, so I've made some changes due to concerns that were brought up and that I thought of.

  • I've added a component manager. This makes sense because the entity/component/system managers ARE objects so they should adhere to OOP best pratices (specficly about Single Responsiblity). So now the entity manager only worries about entities, and the component manager only worries about components.
  • I've added a 'world' object whose concern is the world (go figure) and has instances of the various managers.
  • I have a 'build world' file where I create an instance of the world and inside this file holds the various 'create game objects' functions (create an entity and it's assosiated components).

This way instances of the various managers and the world can be reused from game-to-game and not have game specfic code in them. This is also in line of the Artemis framework (I learn best by doing, so if I can build my own framework, I'll have a better understanding of what is probably going on under the hood of other frameworks when I choose to use them; and so I can compare my framework to something else in case I get stuck on something).

I also changed entities from UUID to simple IDs. The reason I am recyling them is that I'm going to use the ID as an array index. So I'm worried about array sizes for all the components, not running out of numbers. IE: I'd rather not have a UUID of 3,000,000 because I would need to have an array with a size of at least 3,000,000 for each component. So, since I'm recylcing IDs, they should be IDs and not UUIDs.

2 questions has come up now that I'm starting to do some indepth desiging / initial coding.

1)

How do you guys handle the granularity of components? I'm thinking (for now) I'll have extremely rough granularity and break down components as required. What are your thoughts?

EG: Let's say I'm making an old school Rainbow Six game. So I have maps for various buildings with various levels each.

OOP states (at least for me) as a quick and dirty breakdown:

  • You have an object for a map (Map). It has a level, a heading (so the map can be rotated), a floorplan and a position on screen.
  • You have an object for a collection of maps (Atlas) that represents a building. It has a position on screen.
  • You have an object for a collection of atlases (Atlases) that represents the world (the collection of buildings). It has a position on screen.

ECS states (according to the extremely little experience I have with it):

  • You have 'x' number of entities, 1 for each level for each building, 1 for each building and 1 for the final map that shows all the buildings.
  • Components are: Position (used for everything) and a 'map' component that contains the map's level, heading and floor plan.

Now I know this sounds weird (at least to me) but I'm thinking of making compoments with low granularity then making them higher as needed. So the map component would have also had a position but since other objects have that component (and aren't a map of a specific level of a specific building), position has been broken out. If I make a new game object with a level and heading, I'd break those out of the map component so the map and the new game object can use those components.

Or do you guys program with high granularity right off the bat; so the list of components are: position, level, heading, floorplan; and a map entity has all of them and everything else just has position.

2)

My other question is what is the 'best' way to handle flags? For example, say I build a game with no health so to speak; things are either dead or alive. Should there be a component with an 'isAlive' flag; or does it make since to use the component itself as the flag; so if a thing has an 'alive' (empty) component, it's alive?

Again, I realizeI can program it in any way I want (and I will) but I'd like to know other people's thoughts and experiences so I can avoid any traps that might be lurking around.


I also changed entities from UUID to simple IDs. The reason I am recyling them is that I'm going to use the ID as an array index. So I'm worried about array sizes for all the components, not running out of numbers. IE: I'd rather not have a UUID of 3,000,000 because I would need to have an array with a size of at least 3,000,000 for each component. So, since I'm recylcing IDs, they should be IDs and not UUIDs.

In my framework, I use both notions. Recyclable "live ids" (which are essentially array indices), and "unique ids". Unique ids are used whenever there are relationships between entities (visual hierarchy, team classifications, etc...) that need to be preserved across serialization/deserialization. I have an open world, so I need to be able to serialize "un-used" portions of the world as needed. When I deserialize them, the recycled ids would be completely different, so I can't use those to describe entity-entity relationships.


My other question is what is the 'best' way to handle flags? For example, say I build a game with no health so to speak; things are either dead or alive. Should there be a component with an 'isAlive' flag; or does it make since to use the component itself as the flag; so if a thing has an 'alive' (empty) component, it's alive?

I would have a component with an isAlive flag. I suspect as you work on your game, you'll find other things you might to throw in that component that would apply to all creatures/people/etc in the game.


ECS states (according to the extremely little experience I have with it):

You have 'x' number of entities, 1 for each level for each building, 1 for each building and 1 for the final map that shows all the buildings.
Components are: Position (used for everything) and a 'map' component that contains the map's level, heading and floor plan.

This sounds like a level description. This doesn't necessarily need to be expressed as entities. A level description could be used to drive the creation of entities though.

I don't know much about the Rainbow Six games though, so I could be off here. For example, I'm trying to think of reasons why you'd need to have a building be an entity (instead of just part of the level model file). Perhaps you want to be able to mark the building as "cleared of enemies", because that is somehow important to gameplay. Or an enemy needs to know which building it "belongs to" for some reason. Those would be valid reasons to have a building entity (and corresponding component that describes things unique to buildings, such as "isClearedOfEnemies").

I think a more interesting thing to think about is the characters in your game, and the actions they need to perform. Map out different scenarios you need to happen in your game, and figure out the necessary components/system logic/scripts to make those happen.

I'm not sure that "Levels" and "Buildings" should be considered entities. I think that's trying to fit a square peg in a round hole.

You're going to get the best results using the entity/component system for game-objects.

If I'm writing a platformer, my level will probably be expressed as a tiled map, and the level itself won't be an entity. Rather, within the level, I will have objects that are entities. Considering, most of my level will be "empty", the actual platforms and other objects that live within the level will be my entities. So, the box that can be picked-up and moved, the gun that can be picked up, etc.

I wouldn't bother using ECS for your starting screen, for example. I think that might not fit in real well either.

Don't try to fit every piece of your game into the ECS pattern, it will be too difficult.

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)

That was a bad example. I was using it to highlight the problem, not be the problem itself.

The question is how do you design components? Do you make them very small right off the bat? Or do you make them as big as you can and then break them apart as required?

IE: Do you make a component A with properties V, W, X, Y, Z; then later on find out that you need properties Y & Z for another object that is not the same 'type' so then you refactor so you now have component A with properties V, W, X and component B with Y & Z. Then later you need property X for another object not the same 'type' so you refactor such that component A has properties V, W, component B has Y & Z and component C has property X; and so on.

Or do you just make a new component for each property right off the bat and not refactor?


Do you make them very small right off the bat? Or do you make them as big as you can and then break them apart as required?

Neither :-). Although I tend to think the latter would be the more common result (i.e. you end up having to break apart one component into many as a result of refactoring).

You should design the components so that a component represents a "concept" that is common across a range of entities, and that the logic for a particular scenario/system only needs to know about the data in that one particular component (and all the data in that component is relevant for the scenario). Obviously this doesn't work perfectly in practice, since you'll find that most code needs to know about position, for instance. So in that case position needs to be broken out into its own component.

(NB: I have seen frameworks described where there are no components, just individual properties. In that case though, the goal isn't to eventually combine the properties into components).

This topic is closed to new replies.

Advertisement