Building an ECS #1: Types, Hierarchies and Prefabs

The Entity Index

using Type = vector<ComponentId>;
unordered_map<EntityId, Type> entity_index;
bool has_component(EntityId entity, ComponentId component) {
vector<ComponentId>& type = entity_index[entity];
for (auto c : type) {
if (c == component) return true;
}
return false;
}
using EntityId = uint64_t;
Player.add<Position>();
Player.add<Health>();
ComponentId Position_id = 1;
ComponentId Health_id = 2;
[1, 2]
using ComponentId = EntityId;
using EntityId = uint64_t;
using Type = vector<EntityId>;
unordered_map<EntityId, Type> entity_index;
  • An entity is represented by a unique integer
  • A type is a vector of entity (component) ids
  • The entity index stores entities and their type
EntityId Position_id = 10;
EntityId Health_id = 20;
EntityId Player = 30;
{
10: [],
20: [],
30: [10, 20]
}
EntityId EcsComponent_id = 1;
EntityId Position_id = 10;
EntityId Health_id = 20;
EntityId Player = 30;
{
1: [1],
10: [1],
20: [1],
30: [10, 20]
}
EntityId EcsId_id = 2;
{
1: [1, 2],
2: [1, 2],
10: [1, 2],
20: [1, 2],
30: [2, 10, 20]
}
{
EcsComponent: [EcsComponent, EcsId],
EcsId: [EcsComponent, EcsId],
Position: [EcsComponent, EcsId],
Health: [EcsComponent, EcsID],
Player: [EcsId, Position, Health]
}

“One of the biggest implications of this design is that we can add entities to entities”

Hierarchies

{
Foo: [],
Bar: [Foo]
}
{
Parent: []
Child1: [Parent]
Child2: [Parent]
Child3: [Parent]
}
Player: [Position, Health, MyPlatoon]
Player: [Position, Health, CHILDOF | MyPlatoon]
Type type = entity_index[Player];
for (auto el : type) {
if (el & CHILDOF) {
// element is a parent
} else {
// element is not a parent
}
}

Prefabs

{
Base: [Color],
InstanceA: [Position, INSTANCEOF | Base]
InstanceB: [Position, INSTANCEOF | Base]
}
InstanceA.get<Color>(); // Returns Base.Color
auto BlueSquare = flecs::entity()
.set<Color>({0, 0, 255})
.set<Square>({50});
auto SquareA = flecs::entity()
.add_instanceof(BlueSquare)
.set<Position>({10, 20});
auto SquareB = flecs::entity()
.add_instanceof(BlueSquare)
.set<Position>({30, 40});
{
BlueSquare: [Color, Square],
SquareA: [INSTANCEOF | BlueSquare, Position]
SquareB: [INSTANCEOF | BlueSquare, Position]
}
  • Adding a component to an instance will override the base
  • When overriding a shared component, the value of the base will be copied to the instance
auto BlueSquare = flecs::entity()
.set<Color>({0, 0, 255})
.set<Square>({50});
auto SquareA = flecs::entity()
.add_instanceof(BlueSquare)
.add<Color>()
.set<Position>({10, 20});
// Code for SquareB ...
{
BlueSquare: [Color, Square],
SquareA: [INSTANCEOF | BlueSquare, Color, Position]
}
auto BlueSquarePrefab = flecs::entity()
.set<Color>({0, 0, 255})
.set<Square>({50});
auto BlueSquare = flecs::type()
.add_instanceof(BlueSquarePrefab)
.add<Color>();
auto SquareA = flecs::entity()
.add(BlueSquare)
.set<Position>({10, 20});

Summary

// Entities, types and entity index
using EntityId = uint64_t;
using Type = vector<EntityId>;
unordered_map<EntityId, Type> entity_index;
// Type flags
const EntityId INSTANCEOF = 1 << 63;
const EntityId CHILDOF = 2 << 62;

--

--

--

I write lots of code.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Creating a Recipe API with Django Rest Framework.

Need to Outsource? Do This Instead.

Publishing jars to OSSRH repository

Encrypting Kubernetes Secrets With Sealed Secrets

Python convenient grammar — Comprehension

Switch to Ubuntu (for programmers)

Amazon Cognito

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sander Mertens

Sander Mertens

I write lots of code.

More from Medium

Saving Data in Unity3D Using BinaryReader and BinaryWriter

Optimization — Caching WaitForSeconds In Unity Coroutines

Checking the Ogre3D framework with the PVS-Studio static analyzer

Behind the Scenes: Derek Wright on Making a Difference Through Open Source