Taking advantage of my Christmas holidays, one topic I had pending exploring was the Entity component system (ECS) architecture pattern. It is used primarily in video-games, but it is fascinating because of the radical departure from object inheritance, heavily leaning into object composition instead.
One of the best ways to learn is to a) read and then b) experiment, so I went and built a minimal (but representative) vanilla Javascript ECS implementation.
For the impatient, the code is available here: https://github.com/Kartones/ecs, and a basic HTML demo is here: https://kartones.net/demos/028/.
I'll remark on a few technical details, but if you want to see how everything fits, the code is compact and easy to read.
I created three systems:
InputSystem
: To handle (keyboard) input. It interacts withPositionComponent
components, by modifying their velocity "vectors" and/or changing positions when theSpacebar
is pressed (resets all entities to new random positions).MovementSystem
: UpdatesPositionComponent
components movement logic: update position based on velocity, or stop if at a boundary (edge)RenderSystem
: Initializes the screen (an HTML canvas) and tellsRenderComponent
components to render based on theirPositionComponent
values.
As you might have noticed, there are two components:
PositionComponent
: Keeps(x,y)
and a velocity vectorRenderComponent
: Keeps the "sprite" data
And finally, a few managers:
EntityManager
: Entities do not exist as a class; they are mere identifiers. The sole purpose ofEntityManager
is to generate the entity IDs and keep track of themComponentManager
: Keeps track of all components of each entity. When a system is going through an update sweep, queries this manager to ask for all components required; e.g., theRenderSystem
asks forPositionComponent
andRenderComponent
, ensuring that any entity has both (or will be skipped)SystemManager
: A simple list of all configured systems, plus anupdate()
method that, surprise, triggers an update cycle on all systems
And that is mostly everything! At first, I tried to plug in Phaser for rendering graphics and handling input, but it is already an ECS! So, instead of doing weird code to access some of the systems, I discarded it and implemented a simple keyDown handler for the input and an HTML canvas for the renderer. Still, Phaser looks so nice and well-structured, that I'll try it as a complete game engine in the future.