ECS in dacha
Entity-component-system is a way of organising a game that separates what a thing is
from what a thing does. Instead of a deep class hierarchy where Player extends Character extends GameObject, you have plain entities that own bags of data, and separate
logic units that operate on every entity holding a particular combination of data.
dacha follows that idea, with its own vocabulary and one addition.
Actor, not entity
Section titled “Actor, not entity”The thing a game is made of is called an actor. It owns components, it can own child actors, and it has an identity and a name.
The word “entity” is taken: Entity is an internal base class that actors, scenes and the
world all extend, which is where the hierarchy and event-target behaviour come from. When
this documentation says actor, it means the game object.
Components hold data and nothing else
Section titled “Components hold data and nothing else”A component is a bag of values attached to an actor. Transform holds a position,
rotation and scale. Sprite holds which image to draw. Collider holds a shape.
None of them contain logic. That is not stylistic preference, it is a structural requirement: the whole game is described as data that a visual editor reads and writes. A component with behaviour in it could not be serialised into that file, and the editor could not offer it in the inspector.
If you find logic creeping into a component, it belongs in a system or a behavior.
Systems hold the logic
Section titled “Systems hold the logic”A system runs every frame and acts on the actors it cares about. It does not keep a list by hand; it declares which components it needs and receives the matching actors, including ones spawned later:
import { ActorQuery, Transform } from 'dacha';
import { Velocity } from './velocity.component';
const query = new ActorQuery({ scene, filter: [Transform, Velocity],});
for (const actor of query.getActors()) { const transform = actor.getComponent(Transform); const velocity = actor.getComponent(Velocity); // …}The filter can also be a predicate when a component list is not expressive enough:
const query = new ActorQuery({ scene, filter: (actor) => actor.getComponent(Transform).world.position.y > 100,});Systems come in two kinds depending on whether they survive a scene change. That distinction has its own page.
Behaviors are the addition
Section titled “Behaviors are the addition”A textbook ECS has no equivalent of a behavior. A behavior attaches to a single actor and runs for that actor alone.
The reason it exists is that not all logic is naturally a sweep over many actors. A door that opens when approached, a camera that follows a target, a boss with a scripted sequence: writing each as a system that queries exactly one actor is awkward, and writing it as a behavior is not. See behaviors.
Use a system when the logic applies to a class of actors. Use a behavior when it applies to one.
Why the shape is the way it is
Section titled “Why the shape is the way it is”Almost every constraint here follows from one decision: the game is described by data that a visual editor edits. Components have to be serialisable, so they hold no logic. Systems have to be listed and configured in that file, so they are named and take options. Actors have to be arrangeable in a tree, so they form a hierarchy.
The payoff is that the editor is not a separate authoring tool bolted onto a code-first engine. It edits the same thing the engine runs.