Flecs: An Open Source Library Based on C++

Flecs is a high-performance Entity Component System (ECS) designed specifically for game development and real-time simulation systems. It is written in C99 and provides a C++11 wrapper, emphasizing performance, low overhead, and a modular architecture.

📦 Core Features Overview

The table below summarizes the main features of Flecs:

Feature Category Details
Architecture Pattern Entity Component System (ECS), Data-Oriented Design
Performance Characteristics High performance, cache-friendly, low memory overhead
Language Support Native C99, with bindings for C++11, Lua, Python, etc.
Modularity Modular system with pluggable features
Tool Ecosystem Flecs Hub provides ready-to-use modules (e.g., 2D/3D transformations, serialization, etc.)

🛠️ Installation and Project Configuration

Installation Methods
Flecs can be installed in several ways:

  1. Package Manager Installation:
    
    # Vcpkg
    vcpkg install flecs

Conan

conan install flecs/3.1.0


2. **Source Integration**:
```bash
git clone https://github.com/SanderMertens/flecs.git
cd flecs
mkdir build && cd build
cmake ..
make

Using in Code

// C++ version
#include <flecs.h>

// C version
#include <flecs.h>

Using CMake Integration

cmake_minimum_required(VERSION 3.10)
project(MyGame)

find_package(flecs REQUIRED)

add_executable(my_game main.cpp)
target_link_libraries(my_game flecs::flecs)

🚀 Basic Usage and Core Concepts

1. Basic ECS Concepts

#include <flecs.h>
#include <iostream>

// Define components
struct Position {
    float x, y;
};

struct Velocity {
    float x, y;
};

struct Health {
    int current;
    int max;
};

int main() {
    // Create world
    flecs::world world;

    // Register components
    world.component<Position>();
    world.component<Velocity>();
    world.component<Health>();

    // Create entity and add components
    auto player = world.entity("Player")
        .set<Position>({10.0f, 20.0f})
        .set<Velocity>({1.0f, 0.0f})
        .set<Health>({100, 100});

    // Create more entities
    for (int i = 0; i < 5; ++i) {
        world.entity()
            .set<Position>({static_cast<float>(i), 0.0f})
            .set<Velocity>({0.5f, 0.5f});
    }

    // Create movement system
    world.system<Position, Velocity>("MoveSystem")
        .each([](flecs::entity e, Position& p, Velocity& v) {
            p.x += v.x;
            p.y += v.y;
            std::cout << "Entity " << e.name() << " moved to (" 
                      << p.x << ", " << p.y << ")" << std::endl;
        });

    // Run system
    for (int i = 0; i < 3; ++i) {
        std::cout << "Frame " << (i + 1) << ":" << std::endl;
        world.progress();
    }

    return 0;
}

2. Queries and Filters

#include <flecs.h>

void query_example() {
    flecs::world world;

    // Register components
    world.component<Position>();
    world.component<Velocity>();
    world.component<Health>();

    // Create some test entities
    world.entity("Player1")
        .set<Position>({0, 0})
        .set<Velocity>({1, 0})
        .set<Health>({100, 100});

    world.entity("Player2")
        .set<Position>({5, 5})
        .set<Health>({80, 100});

    world.entity("Enemy1")
        .set<Position>({10, 10})
        .set<Velocity>({-1, 0});

    // Create query: find all entities with Position and Velocity
    auto moving_query = world.query<Position, Velocity>();

    std::cout << "Moving entities:" << std::endl;
    moving_query.each([](flecs::entity e, Position& p, Velocity& v) {
        std::cout << " - " << e.name() << " at (" << p.x << ", " << p.y << ")" << std::endl;
    });

    // Create filter: find entities with Position but without Velocity
    auto static_filter = world.filter_builder<Position>()
        .without<Velocity>()
        .build();

    std::cout << "Static entities:" << std::endl;
    static_filter.each([](flecs::entity e, Position& p) {
        std::cout << " - " << e.name() << " at (" << p.x << ", " << p.y << ")" << std::endl;
    });
}

💡 Practical Application Examples

1. Game Character System

#include <flecs.h>
#include <string>

// Game component definitions
struct Transform {
    float x, y, z;
    float rotation;
};

struct Sprite {
    std::string texture_path;
    int width, height;
};

struct PhysicsBody {
    float velocity_x, velocity_y;
    float acceleration;
    bool on_ground;
};

struct PlayerController {
    float move_speed;
    float jump_force;
};

struct EnemyAI {
    enum class State { PATROL, CHASE, ATTACK };
    State current_state;
    float detection_range;
};

class GameWorld {
private:
    flecs::world world_;

public:
    GameWorld() {
        setup_components();
        setup_systems();
        create_entities();
    }

    void setup_components() {
        world_.component<Transform>();
        world_.component<Sprite>();
        world_.component<PhysicsBody>();
        world_.component<PlayerController>();
        world_.component<EnemyAI>();
    }

    void setup_systems() {
        // Physics system
        world_.system<Transform, PhysicsBody>("PhysicsSystem")
            .each([](flecs::entity e, Transform& t, PhysicsBody& pb) {
                // Apply velocity
                t.x += pb.velocity_x;
                t.y += pb.velocity_y;

                // Simple gravity simulation
                if (!pb.on_ground) {
                    pb.velocity_y -= 9.8f * 0.016f; // Assume 60fps
                }

                // Ground detection (simplified)
                if (t.y <= 0) {
                    t.y = 0;
                    pb.on_ground = true;
                    pb.velocity_y = 0;
                } else {
                    pb.on_ground = false;
                }
            });

        // Player input system
        world_.system<PhysicsBody, PlayerController>("InputSystem")
            .each([](flecs::entity e, PhysicsBody& pb, PlayerController& pc) {
                // Here you can integrate actual input handling
                // Simplified example: assume input
                bool move_left = false;
                bool move_right = true;
                bool jump = false;

                if (move_left) pb.velocity_x = -pc.move_speed;
                if (move_right) pb.velocity_x = pc.move_speed;
                if (!move_left && !move_right) pb.velocity_x = 0;

                if (jump && pb.on_ground) {
                    pb.velocity_y = pc.jump_force;
                    pb.on_ground = false;
                }
            });

        // Enemy AI system
        world_.system<Transform, EnemyAI>("AISystem")
            .each([](flecs::entity e, Transform& t, EnemyAI& ai) {
                // Simplified AI logic
                switch (ai.current_state) {
                    case EnemyAI::State::PATROL:
                        // Patrol logic
                        break;
                    case EnemyAI::State::CHASE:
                        // Chase player logic
                        break;
                    case EnemyAI::State::ATTACK:
                        // Attack logic
                        break;
                }
            });

        // Render system (pseudo code)
        world_.system<Transform, Sprite>("RenderSystem")
            .each([](flecs::entity e, Transform& t, Sprite& s) {
                // Here you would call graphics API to render sprite
                std::cout << "Rendering " << s.texture_path 
                          << " at (" << t.x << ", " << t.y << ")" << std::endl;
            });
    }

    void create_entities() {
        // Create player
        world_.entity("Player")
            .set<Transform>({0, 0, 0, 0})
            .set<Sprite>({"player.png", 32, 64})
            .set<PhysicsBody>({0, 0, 0, true})
            .set<PlayerController>({5.0f, 10.0f});

        // Create enemies
        world_.entity("Enemy1")
            .set<Transform>({10, 0, 0, 0})
            .set<Sprite>({"enemy.png", 32, 32})
            .set<PhysicsBody>({0, 0, 0, true})
            .set<EnemyAI>({EnemyAI::State::PATROL, 8.0f});

        world_.entity("Enemy2")
            .set<Transform>({20, 0, 0, 0})
            .set<Sprite>({"enemy.png", 32, 32})
            .set<PhysicsBody>({0, 0, 0, true})
            .set<EnemyAI>({EnemyAI::State::PATROL, 8.0f});
    }

    void update(float delta_time) {
        world_.set_target_fps(60);
        world_.progress();
    }

    void run() {
        // Simple game loop
        for (int i = 0; i < 100; ++i) {
            update(0.016f); // Assume 60fps
        }
    }
};

2. Relational ECS

#include <flecs.h>

void relationship_example() {
    flecs::world world;

    // Define relationship tags
    auto IsParentOf = world.entity("IsParentOf");
    auto IsChildOf = world.entity("IsChildOf");
    auto IsFriendOf = world.entity("IsFriendOf");

    // Create entities and establish relationships
    auto parent = world.entity("Parent");
    auto child1 = world.entity("Child1");
    auto child2 = world.entity("Child2");
    auto friend_entity = world.entity("Friend");

    // Add relationships
    parent.add(IsParentOf, child1);
    parent.add(IsParentOf, child2);
    child1.add(IsFriendOf, friend_entity);

    // Query relationships
    auto children_query = world.query_builder()
        .with(IsChildOf, flecs::Wildcard)  // Find all entities with IsChildOf relationship
        .build();

    std::cout << "Children found:" << std::endl;
    children_query.each([](flecs::entity e) {
        std::cout << " - " << e.name() << std::endl;
    });

    // Iterate over targets of specific relationship
    auto parent_of = world.target(parent, IsParentOf);
    std::cout << "Parent's children:" << std::endl;
    parent_of.each([](flecs::entity e) {
        std::cout << " - " << e.name() << std::endl;
    });
}

🔧 Advanced Features and Module System

1. Custom Modules

#include <flecs.h>

// Define game-specific module
struct GameModule {
    GameModule(flecs::world& world) {
        world.module<GameModule>();

        // Register components and systems in the module
        world.component<Transform>();
        world.component<Sprite>();

        setup_systems(world);
    }

    static void setup_systems(flecs::world& world) {
        world.system<Transform>("UpdateTransforms")
            .each([](flecs::entity e, Transform& t) {
                // Transform update logic
            });
    }
};

// Using the module
int main() {
    flecs::world world;

    // Import module
    world.import<GameModule>();

    // Now you can use components and systems defined in the module
    world.entity("TestEntity")
        .set<Transform>({0, 0, 0, 0});

    world.progress();
    return 0;
}

2. Serialization and Snapshots

#include <flecs.h>

void snapshot_example() {
    flecs::world world;

    world.component<Position>();
    world.component<Velocity>();

    // Create some test entities
    for (int i = 0; i < 3; ++i) {
        world.entity()
            .set<Position>({static_cast<float>(i), static_cast<float>(i)})
            .set<Velocity>({1.0f, 1.0f});
    }

    // Create world snapshot
    flecs::snapshot snapshot(world);
    snapshot.take();

    // Modify original world
    world.entity("NewEntity")
        .set<Position>({99, 99});

    // Restore snapshot
    snapshot.restore();

    std::cout << "World after restore:" << std::endl;
    world.each([](flecs::entity e) {
        std::cout << " - " << e.name() << std::endl;
    });
}

💡 Performance Optimization Tips

  1. Batch Processing:
    // Use each method for batch processing components
    world.system<Position, Velocity>("OptimizedSystem")
    .each([](flecs::entity e, Position& p, Velocity& v) {
        // This callback will be executed in batches, cache-friendly
        p.x += v.x;
        p.y += v.y;
    });

// Use iter method for finer control
world.system<Position, Velocity>(“AdvancedSystem”)
.iter([](flecs::iter& it, Position<em> p, Velocity<em> v) {
for (auto i : it) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
});

2. **Component Layout Optimization**:
```cpp
// Use SoA (Structure of Arrays) layout
struct OptimizedComponents {
    struct Positions {
        float x[1000];
        float y[1000];
    };

    struct Velocities {
        float x[1000];
        float y[1000];
    };
};

🔍 Suitable Scenarios

Flecs is particularly suitable for the following scenarios:

  • Game Development: 2D/3D game engines, entity management systems
  • Simulation Systems: Physics simulation, AI systems, economic simulations
  • Visualization Tools: Data visualization, editor tools
  • Real-time Systems: Applications requiring high performance and predictability

Flecs provides excellent framework support for building complex real-time applications through its powerful ECS implementation and rich feature set.

Leave a Comment