Home ยป What is the role of “systems” in a component-based entity architecture?

What is the role of “systems” in a component-based entity architecture?


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.)

Related Solutions

When should I not kill -9 a process?

Generally, you should use kill (short for kill -s TERM, or on most systems kill -15) before kill -9 (kill -s KILL) to give the target process a chance to clean up after itself. (Processes can't catch or ignore SIGKILL, but they can and often do catch SIGTERM.)...

Default value for UUID column in Postgres

tl;dr Call DEFAULT when defining a column to invoke one of the OSSP uuid functions. The Postgres server will automatically invoke the function every time a row is inserted. CREATE TABLE tbl ( pkey UUID NOT NULL DEFAULT uuid_generate_v1() , CONSTRAINT pkey_tbl...

comparing five integers with if , else if statement

try this : int main () { int n1, n2, n3, n4, n5, biggest,smallest; cout << "Enter the five numbers: "; cin >> n1 >> n2 >> n3 >> n4 >> n5 ; smallest=biggest=n1; if(n2>biggest){ biggest=n2; } if(n2<smallest){ smallest=n2;...

How to play YouTube audio in background/minimised?

Here's a solution using entirely free and open source software. The basic idea is that although YouTube can't play clips in the background, VLC for Android can play clips in the background, so all we need to do is pipe the clip to VLC where we can listen to it...

Why not use “which”? What to use then?

Here is all you never thought you would ever not want to know about it: Summary To get the pathname of an executable in a Bourne-like shell script (there are a few caveats; see below): ls=$(command -v ls) To find out if a given command exists: if command -v...

Split string into Array of Arrays [closed]

If I got correct what you want to receive as a result, then this code would make what you want: extension Array { func chunked(into size: Int) -> [[Element]] { return stride(from: 0, to: self.count, by: size).map { Array(self[$0 ..< Swift.min($0 + size,...

Retrieving n rows per group

Let's start with the basic scenario. If I want to get some number of rows out of a table, I have two main options: ranking functions; or TOP. First, let's consider the whole set from Production.TransactionHistory for a particular ProductID: SELECT...

Don’t understand how my mum’s Gmail account was hacked

IMPORTANT: this is based on data I got from your link, but the server might implement some protection. For example, once it has sent its "silver bullet" against a victim, it might answer with a faked "silver bullet" to the same request, so that anyone...

What is /storage/emulated/0/?

/storage/emulated/0/Download is the actual path to the files. /sdcard/Download is a symlink to the actual path of /storage/emulated/0/Download However, the actual files are located in the filesystem in /data/media, which is then mounted to /storage/emulated/0...

How can I pass a command line argument into a shell script?

The shell command and any arguments to that command appear as numbered shell variables: $0 has the string value of the command itself, something like script, ./script, /home/user/bin/script or whatever. Any arguments appear as "$1", "$2", "$3" and so on. The...

What is pointer to string in C?

argv is an array of pointers pointing to zero terminated c-strings. I painted the following pretty picture to help you visualize something about the pointers. And here is a code example that shows you how an operating system would pass arguments to your...

How do mobile carriers know video resolution over HTTPS connections?

This is an active area of research. I happen to have done some work in this area, so I'll share what I can about the basic idea (this work was with industry partners and I can't share the secret details ๐Ÿ™‚ ). The tl;dr is that it's often possible to identify an...

How do I change the name of my Android device?

To change the hostname (device name) you have to use the terminal (as root): For Eclair (2.1): echo MYNAME > /proc/sys/kernel/hostname For Froyo (2.2): (works also on most 2.3) setprop net.hostname MYNAME Then restart your wi-fi. To see the change, type...

How does reverse SSH tunneling work?

I love explaining this kind of thing through visualization. ๐Ÿ™‚ Think of your SSH connections as tubes. Big tubes. Normally, you'll reach through these tubes to run a shell on a remote computer. The shell runs in a virtual terminal (tty). But you know this part...

Difference between database vs user vs schema

In Oracle, users and schemas are essentially the same thing. You can consider that a user is the account you use to connect to a database, and a schema is the set of objects (tables, views, etc.) that belong to that account. See this post on Stack Overflow:...