185

I've been reading a lot about entity components and systems and have thought that the idea of an entity just being an ID is quite interesting.

However I don't know how this completely works with the components aspect or the systems aspect. A component is just a data object managed by some relevant system. A collision system uses some BoundsComponent together with a spatial data structure to determine if collisions have happened.

All good so far, but what if multiple systems need access to the same component? Where should the data live? An input system could modify an entities BoundsComponent, but the physics system(s) need access to the same component as does some rendering system.

Also, how are entities constructed? One of the advantages I've read so much about is flexibility in entity construction. Are systems intrinsically tied to a component? If I want to introduce some new component, do I also have to introduce a new system or modify an existing one?

Another thing that I've read often is that the 'type' of an entity is inferred by what components it has. If my entity is just an id how can I know that my robot entity needs to be moved or rendered and thus modified by some system?

Sorry for the long post (or at least it seems so from my phone screen)!

bio595
  • 1,961
  • 3
  • 12
  • 5

1 Answers1

354

There are a multitude of ways to represent and implement entity component systems, but here is an explanation of one way. Keep in mind there is no concrete definition of entity/component/system architectures, so this is just one implementation.

I'm going to introduce an analogy for entity/component/system architectures that might help. Let's think of an entity like a key.

The Entity

Entity key

Keys also have teeth (dark blue). The teeth of our entity key is the components that make it up. You can tell entities apart by their ID, even if they have the same teeth. So what do keys fit into? Locks. Locks are our systems. For example, a movement system.

The System

Movement system lock

The lock only works if our key has teeth for both position and velocity. This system only processes entities that have a position and a velocity. There are multiple ways to set up how these systems recognize which entities to process, but one way is to use a long. Each bit is reserved for a component type. For our example lets assume a 4 bit type instead of a 64 bit long. Our example entity would have all available components. So it's key would be 1111. Then, the system is looking for any entity that has a 11--. (The - represent don't care, because movement doesn't care if there's a sprite or health). It can check an entity with a simple AND operation. So our entity matches if ((1111 & 1100) == 1100). If I lost you there check out some more about bitwise operations.

As you can see, systems have access to outside resources. They can access the time, graphics, sound and so on. They are simply little processors that take one key at a time, and process data. You see that the movement system takes the velocity, delta time and position; then does some calculations and stores the result back into position.

The entity keys are really easy to generate. You can add or remove them at will. The entity doesn't care, it's just a way to group and hold the components. The components have no interdependence. The closest the components get to interacting with each other is when a system operates on them and uses data from one to update another, like our movement example.

Lets take a look at another system to help solidify the idea:

Drawing system lock

This is our drawing system. It looks for components that match 1-1-. This entity matches because: ((1111 & 1010) == 1010) Additionally, you can see that this system outputs information to the screen, by drawing the entity sprite at its position.

OK, one more. Let's look at another entity and see how it might fit into our example so far.

Non moveable entity key

As you can see, this entity has fewer components attached to it. By looking at the components it does have, it looks like it could be a static item like a rock. It just has a position and a sprite. It's not going to move and it's not going to be affected by any health changes. This entity would produce a key of 1010. So what systems operate on this entity? Lets check:

Against our movement system: ((1010 & 1100) != 1100) Nope. Looks like the movement system doesn't care about this entity, because it doesn't have the components required.

Against our drawing system: ((1010 & 1010) == 1010) Hey, that's a match. This entity will be operated on by the drawing system. The drawing system will draw the sprite at the position defined.


Hopefully you can see how easy it would be to now add another system that would take our components and operate on them. Let me ensure I've addressed your questions:

What if multiple systems need access to the same component? Where should the data live?

Typically, systems operate one after the other. They process all the entities that match their requirements, then the next system does the same and so on. The data lives with the entity. There shouldn't be anything stored in the system, it's just a lock that gets turned, the key is where the information stays and moves from lock to lock.

How are entities constructed? Are systems intrinsically tied to a component? If I want to introduce some new component, do I also have to introduce a new system or modify an existing one?

Entities are just bags of components. They have a unique ID and a list of components. Systems are only tied to components in the way described above. You can have components without systems that operate on them, but that's pretty pointless. Similarly you can have systems that are looking for components that no entities have. That's less pointless, because they may just be waiting for an entity to be created that matches their lock. So, yes, if you introduce a new component, you'd want to make a system that utilizes that component. Otherwise you're just adding teeth to your key for a lock that doesn't exist.

If my entity is just an id how can I know that my robot entity needs to be moved or rendered and thus modified by some system?

I think I answer this with the idea of a long key that defines the components contained in an entity. You know because the key fits the lock.

Phew! That was a long post! (Or at least it seems so from my large monitor.)

House
  • 73,224
  • 17
  • 184
  • 273
  • 24
    This key analogy is really helpful for understanding the whole idea now. Brilliant idea! Lol at your last paragraph :) – bio595 Jul 02 '12 at 02:42
  • 1
    I'm glad it helped! I have been thinking about a good way to explain these systems and it finally popped into my head when I saw your question. Thanks for the venue to present my idea :) And thanks Cypher, one up vote is enough. – House Jul 02 '12 at 05:27
  • 18
    +1 For the greatest and best explanation of the entity-component system I have ever seen. :O! – knight666 Jul 02 '12 at 06:57
  • 7
    -1 from me - not because this is a bad approach, but because it's being portrayed as THE approach. Yet there are many systems where there is no separation of components and services (eg. in Unity), and there are simpler ways for systems to know which entities to process (just add them when the entity is created). – Kylotan Jul 02 '12 at 09:42
  • 44
    @Kylotan I do say "There are multiple ways to set up how these systems recognize which entities to process, but one way is to use a long." Additionally, I usually reserve the down vote for answers that are not useful (as the hover text says). I think you'd spend a lot of time down voting if you did it for all the answers that didn't cover 100% of the topics they're addressing. – House Jul 02 '12 at 13:04
  • 1
    Sorry, but I still think that when you give an analogy and say that the purpose of it is to "better understand them as a whole", you are claiming to speak for "them as a whole". That, to me, is misleading, because there are several popular ways of approaching these systems that do not fit into your metaphor. – Kylotan Jul 02 '12 at 13:57
  • 6
    @CrappyCodingGuy: that is only true if you follow the approach he is describing. There are other approaches that work entirely differently! So when the answer suggested this was "the" way to do things, it was wrong. He has since edited it to reword things and so I have removed my -1. But I'm not going to apologise for providing constructive feedback on exactly what I felt the problem with this answer was, nor would contributing a second answer have fixed that problem. – Kylotan Jul 03 '12 at 15:47
  • 6
    OK everyone. Kylotan has a right to his opinion. And he's right there are multiple ways of doing this. It doesn't mean he has to provide a more complete answer. We may not agree that it was down vote worthy, but it's only internet points and it's not even down voted any more. No harm done. Thanks for your support. – House Jul 03 '12 at 16:10
  • @Kylotan, I meant no disrespect. You stated that this isn't the only implementation of an entity-system and I was merely curious to see the alternatives. – Cypher Jul 03 '12 at 20:32
  • 9
    Understood. http://gamedev.stackexchange.com/a/4966/1101 – Kylotan Jul 03 '12 at 21:14
  • @Kylotan Yes I can see what you are saying, It would seem that it is not a particularly well defined idea. – bio595 Jul 07 '12 at 01:47
  • 1
    I have no idea how this answer looked like initially but at current state it is excellent explanation how you can approach component based entity design. So +1 from me. And since I am once again in "gonna make me a game" mode I can say one thing: real problem is to decide what kind of components you really need :) – grapkulec May 08 '13 at 12:14
  • Thanks, that's really useful. But I have one question: what if you add one more component? Do you need to add one more bit to id of Entity and remake checks in every system? –  Oct 21 '13 at 18:57
  • @winch Ideally you'd be using something like a long for bit checks in the first place. This allows up to 64 different components, which is typically enough. – House Oct 21 '13 at 19:47
  • @Byte56 yeah, I'm just asking what should I do when I add one more component. Should I put the byte representing it in the beginning of id? Would previous checks(without considering that byte) work for this id too? –  Oct 21 '13 at 19:58
  • I suggest you post the question to the site. – House Oct 21 '13 at 20:01
  • 1
    Do you know what this is? Storing type information as bit field flags, instead of using concrete types. While this is insanely more efficient than RTTI, something still bothers me about this being considered an "OOP" approach by some. It isn't OOP, but if it works well, so what, maybe it's it's own paradigm. – bobobobo Nov 30 '13 at 02:35
  • Awesome answer, and with pretty illustrations, no less. I'm going to steal this for a lecture. – 3Dave Oct 14 '16 at 02:46
  • @David The drawings are alright, if I were to change it I'd make cut outs for the "don't care" components, do it makes more sense that they fit together. – House Oct 14 '16 at 03:06
  • Meh. I get that, but they appear to be effective as-is. I like the bitmask approach. I've been using a map of typeinfo as a filter. It's overly-complicated and, though it hasn't been a bottleneck, sucks perf-wise. Great work, thanks for sharing! – 3Dave Oct 14 '16 at 03:16
  • 1
    Is there a best practice for having the systems access things like the gametime, state, etc... Since you said that systems have access to all this kind of stuff. Do you 'compose' your systems inside some kind of game class or? I'm currently trying to figure out how to build a clean and easy to use architecture for small game projects. – jonathansty Nov 28 '16 at 10:13
  • 3
    @jonathansty Such access isn't specific to component based entity architecture. I'd make each system a class, sharing a common class. Check out best practices for exposing data between classes (you're sure to find thousands of 'best' ways to do it), or just make something that makes sense to you and how you work. – House Nov 28 '16 at 16:48
  • 1
    Bah, code examples are too specific and get out-dated. The high level concepts are timeless :) Glad you found it useful! Thanks. It's been a while since I looked at it, but this may be a good example implementation: https://thelinuxlich.github.io/artemis_CSharp/ – House Aug 25 '17 at 23:14
  • 1
    This analogy is confusing. The first key wouldn't fit either of the locks illustrated, but conceptually that first entity should be handled by both of those systems. – starikcetin Apr 27 '18 at 17:53
  • @S.TarıkÇetin I agree, not drawn the best. The locks should not have solid material where they don't care about what's being input. However, if you can get past that, it seems the analogy works well for people. – House Apr 30 '18 at 14:43
  • @MichaelHouse This is a fantastic answer. I'm only concerned about 1 thing. I'm not sure how well this scaled, because considering a 32bit or even a 64bit key, 32 or even 64 components are not enough to me.. However, I have no Idea. Is there a table somewhere of what all the most important components would be? Are they less than 32? – KeyC0de Oct 05 '19 at 15:16
  • @Nikos Is is just one way to "filter" components to systems. Keep in mind, this strategy would support 32 or 64 types of components, not just 32 or 64 components. If you have more than 64 types of components perhaps you're making them too atomic. Combine some components that are similar enough, and the systems are flexible enough to handle the slightly different processing required for the slightly different types of components. Alternatively, you could use two 32 or 64 bit integers. That's around 1024 or 4096 types. – House Oct 07 '19 at 02:39