Entity Component Systems notes from someone who doesn't write games and didn't know what ECS was.
- State shape is more explicit, so much more likely to make good sense as an app grows
- ECS encourages much less plumbing for data flow between parts of an app, since systems can just read whatever they want.
- For big stateful apps, OO seems to encourage an 8-to-1 ratio of data plumbing compared to biz logic
- easier to safely share behavior
- Supports state serialization for record/replay debugging
Note on boilerplate and noise. The ECS code examples in the links below have a lot of perf-and-render-related noise, but the state management itself is simple.
These notes will make the most sense if read after at least two of these:
- Wikipedia page https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system
- Specs ECS Hello World: https://slide-rs.github.io/specs/02_hello_world.html
- RustConf 2018: Rust for Game Development (long!):https://kyren.github.io/2018/09/14/rustconf-talk.html
Amethyst Game Engine (uses Specs ECS):
- Specs intro: https://slide-rs.github.io/specs/
- Amethyst Pong example:
Unity ECS example:
There are no real API docs for Unity ECS yet as of Dec 2018 because it is so new.
- Two-stick shooter example:
- explanation: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/5492abccd55e2f66194a06c671959d79cb81c2e1/Documentation/content/two_stick_shooter.md
- source: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/5492abccd55e2f66194a06c671959d79cb81c2e1/Samples/Assets/TwoStickShooter/Pure/Scripts/
- components: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/5492abccd55e2f66194a06c671959d79cb81c2e1/Samples/Assets/TwoStickShooter/Pure/Scripts/ComponentTypes.cs
- systems: https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/5492abccd55e2f66194a06c671959d79cb81c2e1/Samples/Assets/TwoStickShooter/Pure/Scripts any file that ends in "System.cs"
- basically just IDs
- systems specify that they want read-only vs. write access to particular components. This is done using types in Specs and C# attibutes in Unity ECS.
- each system operates on only a subset of entities. They do this by specifying which components they care about.
- In the Unity ECS example,
PlayerMoveSystem
usesData
to say "I operate only on entites that have all of these components:PlayerInput
,Position
, andHeader
: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/5492abccd55e2f66194a06c671959d79cb81c2e1/Samples/Assets/TwoStickShooter/Pure/Scripts/PlayerMoveSystem.cs#L10 - In the Specs ECS example, the
Paddle
system usesjoin()
to say "I operate only on entities that have all of these components:Paddle
andTransform
: https://github.com/amethyst/amethyst/blob/v0.10.0/examples/pong/systems/paddle.rs#L26
- In the Unity ECS example,
The Unity ECS example just uses "system 1 writes to this state that system 2 knows to read." This works alongside a C# attribute (like a decorator) that specifies that one system must run "after" another. See the explanation here: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/5492abccd55e2f66194a06c671959d79cb81c2e1/Documentation/content/two_stick_shooter.md#player-input-movement-and-shooting.
The Amethyst/specs pong example works the same way: writing/reading shared data. But more advanced techniques are possible:
- Specs has modification events:
- Amethyst has Event Channel and everything specs has: https://www.amethyst.rs/book/master/concepts/event-channel.html
- entities are basically just ids
- bags of state (components) are not tied to anything like a class in OO, they are free-floating.
- Systems are also not tied to anything like a class, though you can accomplish something similar, but simpler and easier to change: systems specify that they operate on things that have certain sets of components. That is, the PlayerMove system This is in stark contrast to methods. So bundles of state and bundles of behaviors are many-many. This is sort-of-kind-of like using multiple inheritance/mixins like crazy, but name conflicts on this/self are not possible and the interactions are more organized.
- ECS is much less preoccupied than OOP with encapsulation, subsetting state, information-hiding, etc. All state is essentially global and essentially mutable, though there are ways of systems saying what they want to read and what they want to write.
- Much of the motivation for ECS seesm to come from performance concerns (parallelizability, caching, SIMDability) but there may be maintainability advantages as well.
- SQL, as explained in the Rust talk linked above:
- "entities" ~= "keys"
- "components" ~= "tables"
- "sysems" ~= "queries"
- Eve language. If you squint, the chunks of code under "Game logic" are basically ECS systems: http://play.witheve.com/#/examples/flappy.eve. Tags are like components.
- Honorable mention even though not very similar: https://github.com/blackthorne/smart-pacman/blob/master/pacman.pl#L172
- Redux also has global state. But the way Redux reducers are used still couples chunks of state (the subset of state each reducer managers) with behavior (~= reducer logic). (aside: the immutable data part of Redux seems orthogonal to it's other characteristics. Vuex is basically the same thing, but with mutatation.)
- A major difference is that ECS state slices (components) are 1:many with behavior (systems).
- ECS is missing the notion of Redux "actions."
- React hooks, which are reusable bundles of state and behavior independent of classes, where the state change logic is aware of different phases (mounting, updating, etc.). Differences are that hook state and behavior are 1:1 and hook state is usually not global.
- Ideas in "Purely Functional Retro Games," though maybe that author just landed on something functionally identical to Redux: https://prog21.dadgum.com/23.html
- Maybe ECS is comparable to OOP but using a combination of component-like and system-like mixins for everything, empty class bodies, a tweak to avoid name conflicts, and a mixin registration system.
- FRP is either similar to ECS or the complete opposite.