View Source ECS Design
entities-and-components
Entities and Components
Everything in your application is an Entity, but in ECS you won't work with these Entities directly - instead you will work with the individual attributes that an Entity might have. These attributes are given to an Entity by creating a Component, which holds, at minimum, the Entity's unique ID, but also can store a value. For example:
- You're running a 2-dimensional simulation of cars on a highway
- Each car gets its own
entity_id
e.g.123
- If the car with ID
123
is blue, we give it aColor
Component with value"blue"
- If the same car is moving west at 60mph, we might model this with a
Direction
Component with value"west"
and aSpeed
Component with value60
- The car would also have Components such as
XCoordinate
andYCoordinate
to locate it on the map
systems
Systems
Once your Entities are modeled using Components, you'll create Systems to operate on them. For example:
- Entities with
Speed
Components should have their locations regularly updated according to the speed and direction - We can create a
Move
System which reads theSpeed
andDirection
Components, calculates how far the car has moved since the last server tick, and updates the Entity'sXCoordinate
and/orYCoordinate
Component accordingly. - The System will run every tick, only considering Entities which have a
Speed
Component
one-to-many-associations
one-to-many associations
At some point, you might find yourself thinking of adding multiple Components of the same type to a single Entity. We'll call this a "one-to-many" association - as in, one Entity, many Components. For example:
Let's say you have a fantasy game where the hero wields a weapon in one hand, and a shield in the other hand. At first, you create a Component for each
Weapon.add(hero_entity, "Longsword")
Shield.add(hero_entity, "Buckler")
and move on to other features. However, later on you decide to implement a dual-wielding mechanic
where the hero can wield two swords at a time. You briefly consider creating a new Component type
called OffhandWeapon
to go alongside the primary Weapon
, but then remember that eventually the
hero will encounter monsters with more than two arms! Also there is a feature request for weapons
to be magically enchanted, and eventually you'd also like equipment to lose durability over time and
require repairs at the blacksmith. So, creating a new Component type for each weapon slot is only
a temporary workaround which is not a very robust solution.
The ideal solution here is to think about the weapons not as Components, but as separate Entities. When the hero equips a sword, create a new entity reference for that sword, and reference back to the hero entity with one of the sword's Components.
sword_entity = Ecto.UUID.generate()
Description.add(sword_entity, "Longsword")
EquippedBy.add(sword_entity, hero_entity)
Now if the hero gets a second sword, we can repeat the process:
another_sword_entity = Ecto.UUID.generate()
Description.add(another_sword_entity, "Shortsword")
EquippedBy.add(another_sword_entity, hero_entity)
Fetching a list of weapons equipped by the hero can then be done with EquippedBy.search(hero_entity)
To implement weapon durability:
Durability.add(sword_entity, 150)
Durability.add(another_sword_entity, 75)
Implementing magic enchantments presents the same situation: we could add a Component to the sword, but this only works for one simple enchantment per weapon. In order to allow multiple enchantments per weapon, with arbitrary enchantment complexity, we should think about each enchantment as an Entity.
enchantment_entity = Ecto.UUID.generate()
Description.add(enchantment_entity, "Firaga")
EnchantTarget.add(enchantment_entity, sword_entity)