I’ve been playing with Amethyst a little bit. It’s a library that leverages an Entity-Component-System Architecture (which I’ll refer to as ECS).

I think ECS is interesting (even if you, like me, don’t intend to do serious game dev) because:

  • It’s completely unintuitive at first
  • It flies in the face of (college taught) object oriented design
  • It shows the difference between composition and inheritance
  • It can improve your software design
  • It can improve performance

Entities

Game entities are the basic nouns in your game. For example, maybe you have a player, a block of terrain, a sheep, an arrow.

A key part of an ECS is that these entities are incredibly simple, they’re just an ID (like an integer).

This is opposed to some other approaches that might have entities represented by a nested inheritance structure.

Components

Obviously you can’t do much without data associated with the identifier, so we add that through components.

Components are just dumb data. They do not contain logic. The most common component for a 2D game is likely a position component which just contains an X-value and a Y-value.

Now how do we find the position of a given entity? We can just use the entity ID as an index into a big array of all positions containing several 100 position components.

System

We have a way to represent an entity and data about that entity, now we just need to add in logic.

This is done through systems. Systems are essentially just functions that can ask for groupings of components to operate over. They can potentially mutate the components.

For example, assume we have hundreds of ball entities in our environment. Each ball has a position and a velocity component. We can also have a few static box entities, which only have a position.

A basic system for running movement simulation would request all components of type position and velocity, grouped by entity, and for each entity update the velocity based on the effect of gravity, then update position based on the current velocity.

This would mean only the ball entities actually move, while the platforms stay static.

If we assume each entity has a collision_shape component and an is_dead component, we can even develop a system that requests groups of (position, collision_shape, is_dead), and checks if the collision shapes collided. If they have, it can mark both collided objects as dead. Another renderer system could read is_dead and not render the entity if so.

Performance

What did all this reorganizing buy us? Well, for one thing, if there’s anything computer architecture likes, it’s predictable, sequential access patterns. Fundamentally this is because sequential iteration over a vector makes it really easy to figure out where future data is going to come from. This allows your CPU to prefetch data and prevent cache misses.

Now, rather conveniently, all of our data is in these nice long sequential vectors.

Organization

Another nice part of ECS is that adding and reusing functionality across entities is really easy. For example, you likely will have an entity for your camera. Do you want a camera that moves according to physics rules you implemented for a ball? Well, you can do that! Just slap a velocity component on there and now you’ve got your motion system running on your camera entity! Yes that’s a weird example, but it demonstrates the flexibility inherent in this sort of architecture. Any entity can have any component with minimal refactor.