EnTT

Language: CPP

Game Development / Utilities

EnTT was created by Michele Caini to provide a modern, high-performance ECS framework for C++. Unlike traditional object-oriented game engines, ECS focuses on separating data (components) from behavior (systems), enabling better performance and scalability. EnTT has gained popularity in both indie and professional game development as well as simulations and modeling.

EnTT is a fast and flexible C++ Entity-Component-System (ECS) library used in game development and simulation. It provides efficient memory management, entity lifecycle handling, component storage, and iteration utilities for building scalable and data-driven applications.

Installation

cmake: find_package(EnTT CONFIG REQUIRED)
vcpkg: vcpkg install entt
conan: conan install entt

Usage

EnTT allows developers to create entities, assign components, and iterate over them efficiently. It leverages modern C++ templates and compile-time features to minimize overhead while maximizing flexibility.

Creating entities and components

#include <entt/entt.hpp>
#include <iostream>

struct Position { float x, y; };
struct Velocity { float dx, dy; };

int main() {
    entt::registry registry;
    auto entity = registry.create();
    registry.emplace<Position>(entity, 0.f, 0.f);
    registry.emplace<Velocity>(entity, 1.f, 1.f);
    std::cout << "Entity created with Position and Velocity." << std::endl;
    return 0;
}

Creates an entity and assigns `Position` and `Velocity` components.

Iterating over components

registry.view<Position, Velocity>().each([](auto entity, auto& pos, auto& vel) {
    pos.x += vel.dx;
    pos.y += vel.dy;
});

Iterates over all entities with both `Position` and `Velocity` components and updates positions.

Systems and processing

auto view = registry.view<Position, Velocity>();
for (auto entity : view) {
    auto &pos = view.get<Position>(entity);
    auto &vel = view.get<Velocity>(entity);
    pos.x += vel.dx;
    pos.y += vel.dy;
}

Implements a basic system to update positions based on velocity.

Signals and events

struct CollisionEvent { entt::entity a, b; };

entt::dispatcher dispatcher;
dispatcher.sink<CollisionEvent>().connect([](const CollisionEvent& e){
    std::cout << "Collision detected!" << std::endl;
});

dispatcher.trigger(CollisionEvent{entity1, entity2});

Uses EnTT’s dispatcher system to handle game events like collisions.

Groups for optimization

auto group = registry.group<Position, Velocity>();
for (auto entity : group) {
    auto &[pos, vel] = group.get<Position, Velocity>(entity);
    pos.x += vel.dx;
    pos.y += vel.dy;
}

Groups entities by tightly-coupled components for faster iteration.

Error Handling

Accessing non-existent component: Check with `registry.all_of<T>(entity)` before accessing components.
Performance issues with sparse components: Use groups or compact storage to improve memory locality.
Entity lifecycle mismanagement: Always destroy unused entities with `registry.destroy(entity)`.

Best Practices

Favor composition (components + systems) over inheritance for scalability.

Use groups or views for performance-sensitive iteration.

Keep components lightweight and focused on data only.

Leverage EnTT’s signals and dispatcher for decoupled event handling.

Profile your ECS usage to avoid creating too many small entities unnecessarily.