Flecs v3.2
A fast entity component system (ECS) for C & C++
Loading...
Searching...
No Matches
Flecs Quickstart

This document provides a quick overview of the different features and concepts in Flecs with short examples. This is a good resource if you're just getting started or just want to get a better idea of what kind of features are available in Flecs!

Building Flecs

To use Flecs, copy the flecs.c and flecs.h files from the repository root to your project's source folder. When building, make sure your build system is setup to do the following:

  • If it is a C++ project, make sure to compile flecs.c as C code, for example by using gcc/clang instead of g++/clang++.
  • If you are building on Windows and you're not using the Microsoft Visual Studio compiler, make sure to add -lWs2_32 to the end(!) of the linker command. The socket API is used for connecting to Flecs explorer.
  • When compiling Flecs with gcc/clang, add -std=gnu99 to the compiler command. This ensures that addons that rely on time & socket functions are compiled correctly.
  • C++ files that use Flecs must be compiled with -std=c++0x (C++11) or higher.

Dynamic linking

To build Flecs as a dynamic library, remove this line from the top of the flecs.h file:

#define flecs_STATIC

When compiling flecs.c, make sure to define flecs_EXPORTS, for example by adding -Dflecs_EXPORTS to the compiler command.

Alternatively Flecs can also be built as a dynamic library with the cmake, meson, bazel or bake build files provided in the repository. These use the files from src to build as opposed to the amalgamated files, which is better suited for development.

Building with CMake

Locate flecs on your system (either by cloning or as a submodule) and use add_subdirectory or use FetchContent to download the source code from the master branch of the flecs repository. After that, add the following to your CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} flecs::flecs_static)

Building with Bake

Download or git clone the flecs repository and run bake from inside the directory. After that, add the following to your project.json file's value property:

"use": ["flecs"]

Running tests (bake)

First make sure you have bake installed (see the bake repository for instructions).

Run the following commands to run all tests (use -j to specify the number of threads):

# Core test suite
bake run test/api -- -j 4
# Addon tests
bake run test/addons -- -j 4
# Reflection tests
bake run test/meta -- -j 4
# C++ tests
bake run test/cpp_api -- -j 4

To run tests with asan enabled, add --cfg sanitize to the command:

bake run --cfg sanitize test/api -- -j 4

Running tests (cmake, experimental)

First make sure to clone bake.

Run the following commands to run all the tests:

# Generate make files for Flecs and tests
cmake -DFLECS_TESTS=ON -DBAKE_DIRECTORY="path to cloned bake repository"
# Build flecs and test suites
cmake --build . -j 4
# Run the tests
ctest -C Debug --verbose

Emscripten

When building for emscripten, add the following command line options to the emcc link command:

-s ALLOW_MEMORY_GROWTH=1
-s STACK_SIZE=1mb
-s EXPORTED_RUNTIME_METHODS=cwrap
-s MODULARIZE=1
-s EXPORT_NAME="my_app"

Addons

Flecs has a modular architecture that makes it easy to only build the features you really need. By default all addons are built. To customize a build, first define FLECS_CUSTOM_BUILD, then add defines for the addons you need. For example:

#define FLECS_CUSTOM_BUILD // Don't build all addons
#define FLECS_SYSTEM // Build FLECS_SYSTEM

Additionally, you can also specify addons to exclude from a build by adding NO to the define:

#define FLECS_NO_LOG

The following addons can be configured:

Addon Description Define
Cpp C++11 API FLECS_CPP
Module Organize game logic into reusable modules FLECS_MODULE
System Create & run systems FLECS_SYSTEM
Pipeline Automatically schedule & multithread systems FLECS_PIPELINE
Timer Run systems at time intervals or at a rate FLECS_TIMER
Meta Flecs reflection system FLECS_META
Meta_C (C) Utilities for auto-inserting reflection data FLECS_META_C
Units Builtin unit types FLECS_UNITS
Expr String format optimized for ECS data FLECS_EXPR
JSON JSON format FLECS_JSON
Doc Add documentation to components, systems & more FLECS_DOC
Http Tiny HTTP server for processing simple requests FLECS_HTTP
Rest REST API for showing entities in the browser FLECS_REST
Parser Create entities & queries from strings FLECS_PARSER
Plecs Small utility language for asset/scene loading FLECS_PLECS
Rules Powerful prolog-like query language FLECS_RULES
Snapshot Take snapshots of the world & restore them FLECS_SNAPSHOT
Stats Functions for collecting statistics FLECS_STATS
Monitor Periodically collect & store flecs statistics FLECS_MONITOR
Metrics Create metrics from user-defined components FLECS_METRICS
Alerts Create alerts from user-defined queries FLECS_ALERTS
Log Extended tracing and error logging FLECS_LOG
Journal Journaling of API functions FLECS_JOURNAL
App Flecs application framework FLECS_APP
OS API Impl Default OS API implementation for Posix/Win32 FLECS_OS_API_IMPL

Concepts

This section contains an overview of all the different concepts in Flecs and how they wire together. The sections in the quickstart go over them in more detail and with code examples.

Flecs Overview

World

The world is the container for all ECS data. It stores the entities and their components, does queries and runs systems. Typically there is only a single world, but there is no limit on the number of worlds an application can create.

Entity

An entity is a unique thing in the world, and is represented by a 64 bit id. Entities can be created and deleted. If an entity is deleted it is no longer considered "alive". A world can contain up to 4 billion(!) alive entities. Entity identifiers contain a few bits that make it possible to check whether an entity is alive or not.

Entities can have names which makes it easier to identify them in an application. In C++ the name can be passed to the constructor. If a name is provided during entity creation time and an entity with that name already exists, the existing entity will be returned.

  • C

    In C a name can be assigned with the ecs_entity_init function or ecs_entity macro.

    ecs_entity_t e = ecs_entity(world, { .name = "Bob" });
    printf("Entity name: %s\n", ecs_get_name(world, e));
    #define ecs_entity(world,...)
    Shorthand for creating an entity with ecs_entity_init().
    Definition flecs_c.h:200
    const char * ecs_get_name(const ecs_world_t *world, ecs_entity_t entity)
    Get the name of an entity.
  • C++

    auto e = world.entity("Bob");
    std::cout << "Entity name: " << e.name() << std::endl;
    flecs::string_view name() const
    Return the entity name.
  • C#

    Entity e = world.Entity("Bob");
    Console.WriteLine($"Entity name: {e.Name()}");

Entities can be looked up by name with the lookup function:

  • C

    ecs_entity_t e = ecs_lookup(world, "Bob");
    ecs_entity_t ecs_lookup(const ecs_world_t *world, const char *path)
    Lookup an entity by it's path.
  • C++

    auto e = world.lookup("Bob");
    flecs::entity lookup(const char *name, bool search_path=true) const
    Lookup entity by name.
    Definition world.hpp:78
  • C#

    Entity e = world.Lookup("Bob");

Id

An id is a 64 bit number that can encode anything that can be added to an entity. In flecs this can be either a component, tag or a pair. A component is data that can be added to an entity. A tag is an "empty" component. A pair is a combination of two component/tag ids which is used to encode entity relationships. All entity/component/tag identifiers are valid ids, but not all ids are valid entity identifier.

The following sections describe components, tags and pairs in more detail.

Component

A component is a type of which instances can be added and removed to entities. Each component can be added only once to an entity (though not really, see Pair). In C applications components must be registered before use. By default in C++ this happens automatically.

  • C

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);
    // Add a component. This creates the component in the ECS storage, but does not
    // assign it with a value.
    ecs_add(world, e, Velocity);
    // Set the value for the Position & Velocity components. A component will be
    // added if the entity doesn't have it yet.
    ecs_set(world, e, Position, {10, 20});
    ecs_set(world, e, Velocity, {1, 2});
    // Get a component
    const Position *p = ecs_get(world, e, Position);
    // Remove component
    ecs_remove(world, e, Position);
    #define ECS_COMPONENT(world, id)
    Declare & define a component.
    Definition flecs_c.h:145
  • C++

    auto e = world.entity();
    // Add a component. This creates the component in the ECS storage, but does not
    // assign it with a value.
    e.add<Velocity>();
    // Set the value for the Position & Velocity components. A component will be
    // added if the entity doesn't have it yet.
    e.set<Position>({10, 20})
    .set<Velocity>({1, 2});
    // Get a component
    const Position *p = e.get<Position>();
    // Remove component
    e.remove<Position>();
    Self & add()
    Add a component to an entity.
    Definition builder.hpp:25
    const T * get() const
    Get component value.
  • C#

    Entity e = world.Entity();
    // Add a component. This creates the component in the ECS storage, but does not
    // assign it with a value.
    e.Add<Velocity>();
    // Set the value for the Position & Velocity components. A component will be
    // added if the entity doesn't have it yet.
    e.Set<Position>(new(10, 20))
    .Set<Velocity>(new(1, 2));
    // Get a component
    ref readonly Position p = ref e.Get<Position>();
    // Remove component
    e.Remove<Position>();

Each component is associated by a unique entity identifier by Flecs. This makes it possible to inspect component data, or attach your own data to components.

  • C

    C applications can use the ecs_id macro to get the entity id for a component.

    ECS_COMPONENT(world, Position);
    ecs_entity_t pos_e = ecs_id(Position);
    printf("Name: %s\n", ecs_get_name(world, pos_e)); // outputs 'Name: Position'
    // It's possible to add components like you would for any entity
    ecs_add(world, pos_e, Serializable);
  • C++

    C++ applications can use the world::entity function.

    flecs::entity pos_e = world.entity<Position>();
    std::cout << "Name: " << pos_e.name() << std::endl; // outputs 'Name: Position'
    // It's possible to add components like you would for any entity
    pos_e.add<Serializable>();
    Entity.
    Definition entity.hpp:30
  • C#

    C# applications can use the World.Entity() function.

    Entity posE = world.Entity<Position>();
    Console.WriteLine($"Name: {posE.Name()}"); // outputs 'Name: Position'
    // It's possible to add components like you would for any entity
    posE.Add<Serializable>();

The thing that makes an ordinary entity a component is the EcsComponent (or flecs::Component, in C++) component. This is a builtin component that tells Flecs how much space is needed to store a component, and can be inspected by applications:

Because components are stored as regular entities, they can in theory also be deleted. To prevent unexpected accidents however, by default components are registered with a tag that prevents them from being deleted. If this tag were to be removed, deleting a component would cause it to be removed from all entities. For more information on these policies, see Relationship cleanup properties.

Tag

A tag is a component that does not have any data. In Flecs tags can be either empty types (in C++) or regular entities (C & C++) that do not have the EcsComponent component (or have an EcsComponent component with size 0). Tags can be added & removed using the same APIs as adding & removing components, but because tags have no data, they cannot be assigned a value. Because tags (like components) are regular entities, they can be created & deleted at runtime.

  • C

    // Create Enemy tag
    ecs_entity_t Enemy = ecs_new_id(world);
    // Create entity, add Enemy tag
    ecs_add_id(world, e, Enemy);
    ecs_has_id(world, e, Enemy); // true!
    ecs_remove_id(world, e, Enemy);
    ecs_has_id(world, e, Enemy); // false!
    void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
    Add a (component) id to an entity.
    void ecs_remove_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
    Remove a (component) id from an entity.
    bool ecs_has_id(const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
    Test if an entity has an id.
  • C++

    // Option 1: create Tag as empty struct
    struct Enemy { };
    // Create entity, add Enemy tag
    auto e = world.entity().add<Enemy>();
    e.has<Enemy>(); // true!
    e.remove<Enemy>();
    e.has<Enemy>(); // false!
    // Option 2: create Tag as entity
    auto Enemy = world.entity();
    // Create entity, add Enemy tag
    auto e = world.entity().add(Enemy);
    e.has(Enemy); // true!
    e.remove(Enemy);
    e.has(Enemy); // false!
    bool has(flecs::id_t e) const
    Check if entity has the provided entity.
  • C#

    // Option 1: create Tag as empty struct
    public struct Enemy { }
    // Create entity, add Enemy tag
    Entity e = world.Entity().Add<Enemy>();
    e.Has<Enemy>(); // true!
    e.Remove<Enemy>();
    e.Has<Enemy>(); // false!
    // Option 2: create Tag as entity
    Entity Enemy = world.Entity();
    // Create entity, add Enemy tag
    Entity e = world.Entity().Add(Enemy);
    e.Has(Enemy); // true!
    e.Remove(Enemy);
    e.Has(Enemy); // false!

Note that both options in the C++ example achieve the same effect. The only difference is that in option 1 the tag is fixed at compile time, whereas in option 2 the tag can be created dynamically at runtime.

When a tag is deleted, the same rules apply as for components (see Relationship cleanup properties).

Pair

A pair is a combination of two entity ids. Pairs can be used to store entity relationships, where the first id represents the relationship kind and the second id represents the relationship target (called "object"). This is best explained by an example:

  • C

    // Create Likes relationship
    ecs_entity_t Likes = ecs_new_id(world);
    // Create a small graph with two entities that like each other
    ecs_entity_t Bob = ecs_new_id(world);
    ecs_entity_t Alice = ecs_new_id(world);
    ecs_add_pair(world, Bob, Likes, Alice); // Bob likes Alice
    ecs_add_pair(world, Alice, Likes, Bob); // Alice likes Bob
    ecs_has_pair(world, Bob, Likes, Alice); // true!
    ecs_remove_pair(world, Bob, Likes, Alice);
    ecs_has_pair(world, Bob, Likes, Alice); // false!
  • C++

    // Create Likes relationship as empty type (tag)
    struct Likes { };
    // Create a small graph with two entities that like each other
    auto Bob = world.entity();
    auto Alice = world.entity();
    Bob.add<Likes>(Alice); // Bob likes Alice
    Alice.add<Likes>(Bob); // Alice likes Bob
    Bob.has<Likes>(Alice); // true!
    Bob.remove<Likes>(Alice);
    Bob.has<Likes>(Alice); // false!
  • C#

    // Create Likes relationship as empty type (tag)
    public struct Likes { }
    // Create a small graph with two entities that like each other
    Entity Bob = world.Entity();
    Entity Alice = world.Entity();
    Bob.Add<Likes>(Alice); // Bob likes Alice
    Alice.Add<Likes>(Bob); // Alice likes Bob
    Bob.Has<Likes>(Alice); // true!
    Bob.Remove<Likes>(Alice);
    Bob.Has<Likes>(Alice); // false!

A pair can be encoded in a single 64 bit identifier by using the ecs_pair macro in C, or the world.pair function in C++:

The following examples show how to get back the elements from a pair:

  • C

    if (ecs_id_is_pair(id)) {
    ecs_entity_t relationship = ecs_pair_first(world, id);
    ecs_entity_t target = ecs_pair_second(world, id);
    }
    bool ecs_id_is_pair(ecs_id_t id)
    Utility to check if id is a pair.
  • C++

    flecs::id id = ...;
    if (id.is_pair()) {
    auto relationship = id.first();
    auto target = id.second();
    }
  • C#

    Id id = ...;
    if (id.IsPair())
    {
    Entity relationship = id.First();
    Entity target = id.Second();
    }

A component or tag can be added multiple times to the same entity as long as it is part of a pair, and the pair itself is unique:

  • C

    ecs_add_pair(world, Bob, Eats, Apples);
    ecs_add_pair(world, Bob, Eats, Pears);
    ecs_add_pair(world, Bob, Grows, Pears);
    ecs_has_pair(world, Bob, Eats, Apples); // true!
    ecs_has_pair(world, Bob, Eats, Pears); // true!
    ecs_has_pair(world, Bob, Grows, Pears); // true!
  • C++

    flecs::entity bob = ...;
    bob.add(Eats, Apples);
    bob.add(Eats, Pears);
    bob.add(Grows, Pears);
    bob.has(Eats, Apples); // true!
    bob.has(Eats, Pears); // true!
    bob.has(Grows, Pears); // true!
  • C#

    Entity Bob = ...;
    Bob.Add(Eats, Apples);
    Bob.Add(Eats, Pears);
    Bob.Add(Grows, Pears);
    Bob.Has(Eats, Apples); // true!
    Bob.Has(Eats, Pears); // true!
    Bob.Has(Grows, Pears); // true!

The target function can be used in C and C++ to get the object for a relationship:

  • C

    ecs_entity_t o = ecs_get_target(world, Alice, Likes, 0); // Returns Bob
    ecs_entity_t ecs_get_target(const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t rel, int32_t index)
    Get the target of a relationship.
  • C++

    flecs::entity alice = ...;
    auto o = alice.target<Likes>(); // Returns Bob
  • C#

    Entity Alice = ...;
    Entity o = Alice.Target<Likes>(); // Returns Bob

Entity relationships enable lots of interesting patterns and possibilities. Make sure to check out the Relationships manual.

Hierarchies

Flecs has builtin support for hierarchies with the builtin EcsChildOf (or flecs::ChildOf, in C++) relationship. A hierarchy can be created with the regular relationship API, or with the child_of shortcut in C++:

When entities have names, they can be used together with hierarchies to generate path names or do relative lookups:

  • C

    ecs_entity_t parent = ecs_entity(world, {
    .name = "parent"
    });
    ecs_entity_t child = ecs_entity(world, {
    .name = "child"
    });
    ecs_add_pair(world, child, EcsChildOf, parent);
    char *path = ecs_get_fullpath(world, child);
    printf("%s\n", path); // output: 'parent.child'
    ecs_os_free(path);
    ecs_lookup_path(world, 0, "parent.child"); // returns child
    ecs_lookup_path(world, parent, "child"); // returns child
  • C++

    auto parent = world.entity("parent");
    auto child = world.entity("child").child_of(parent);
    std::cout << child.path() << std::endl; // output: 'parent::child'
    world.lookup("parent::child"); // returns child
    parent.lookup("child"); // returns child
    flecs::string path(const char *sep="::", const char *init_sep="::") const
    Return the entity path.
    flecs::entity lookup(const char *path, bool search_path=false) const
    Lookup an entity by name.
    Definition impl.hpp:184
  • C#

    Entity parent = world.Entity("parent");
    Entity child = world.Entity("child").ChildOf(parent);
    Console.WriteLine(child.Path()); // output: 'parent.child'
    world.Lookup("parent.child"); // returns child
    parent.Lookup("child"); // returns child

Queries (see below) can use hierarchies to order data breadth-first, which can come in handy when you're implementing a transform system:

  • C

    .filter.terms = {
    { ecs_id(Position) },
    { ecs_id(Position), .src = {
    .flags = EcsCascade, // Breadth-first order
    .trav = EcsChildOf // Use ChildOf relationship for traversal
    }}
    }
    });
    ecs_iter_t it = ecs_query_iter(world, q);
    while (ecs_query_next(&it)) {
    Position *p = ecs_field(&it, Position, 1);
    Position *p_parent = ecs_field(&it, Position, 2);
    for (int i = 0; i < it.count; i++) {
    // Do the thing
    }
    }
    struct ecs_query_t ecs_query_t
    A query that caches its results.
    Definition flecs.h:394
    ecs_query_t * ecs_query_init(ecs_world_t *world, const ecs_query_desc_t *desc)
    Create a query.
    ecs_iter_t ecs_query_iter(const ecs_world_t *world, ecs_query_t *query)
    Return a query iterator.
    bool ecs_query_next(ecs_iter_t *iter)
    Progress the query iterator.
    #define EcsCascade
    Sort results breadth first.
    Definition flecs.h:711
    Used with ecs_query_init().
    Definition flecs.h:1035
  • C++

    auto q = world.query_builder<Position, Position>()
    .term_at(2).parent().cascade()
    .build();
    q.each([](Position& p, Position& p_parent) {
    // Do the thing
    });
    flecs::query_builder< Comps... > query_builder(Args &&... args) const
    Create a query builder.
  • C#

    Query q = world.QueryBuilder<Position, Position>()
    .TermAt(2).Parent().Cascade()
    .Build();
    q.Each((ref Position p, ref Position pParent) =>
    {
    // Do the thing
    });

Instancing

Flecs has builtin support for instancing (sharing a single component with multiple entities) through the builtin EcsIsA relationship (flecs::IsA in C++). An entity with an IsA relationship to a base entity "inherits" all entities from that base:

  • C

    // Shortcut to create entity & set a component
    ecs_entity_t base = ecs_set(world, 0, Triangle, {{0, 0}, {1, 1}, {-1, -1}});
    // Create entity that shares components with base
    ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base);
    const Triangle *t = ecs_get(world, e, Triangle); // gets Triangle from base
    const ecs_entity_t EcsIsA
    Used to express inheritance relationships.
  • C++

    auto base = world.entity().set<Triangle>({{0, 0}, {1, 1}, {-1, -1}});
    // Create entity that shares components with base
    auto e = world.entity().is_a(base);
    const Triangle *t = e.get<Triangle>(); // gets Triangle from base
    Self & is_a(entity_t second)
    Shortcut for add(IsA, entity).
    Definition builder.hpp:213
  • C#

    Entity base = world.Entity().Set<Triangle>(new Triangle(new(0, 0), new(1, 1), new(-1, -1)));
    // Create entity that shares components with base
    Entity e = world.Entity().IsA(base);
    ref readonly Triangle t = ref e.Get<Triangle>(); // gets Triangle from base

Entities can override components from their base:

  • C

    // Add private instance of Triangle to e, copy value from base
    ecs_add(world, e, Triangle);
  • C++

    // Add private instance of Triangle to e, copy value from base
    e.add<Triangle>();
  • C#

    // Add private instance of Triangle to e, copy value from base
    e.Add<Triangle>();

Instancing can be used to build modular prefab hierarchies, as the foundation of a batched renderer with instancing support, or just to reduce memory footprint by sharing common data across entities.

Type

The type (often referred to as "archetype") is the list of ids an entity has. Types can be used for introspection which is useful when debugging, or when for example building an entity editor. The most common thing to do with a type is to convert it to text and print it:

  • C

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);
    ecs_add(world, e, Position);
    ecs_add(world, e, Velocity);
    const ecs_type_t *type = ecs_get_type(world, e);
    char *type_str = ecs_type_str(world, type);
    printf("Type: %s\n", type_str); // output: 'Position,Velocity'
    ecs_os_free(type_str);
    const ecs_type_t * ecs_get_type(const ecs_world_t *world, ecs_entity_t entity)
    Get the type of an entity.
    char * ecs_type_str(const ecs_world_t *world, const ecs_type_t *type)
    Convert type to string.
    A type is a list of (component) ids.
    Definition flecs.h:335
  • C++

    auto e = ecs.entity()
    .add<Position>()
    .add<Velocity>();
    std::cout << e.type().str() << std::endl; // output: 'Position,Velocity'
  • C#

    Entity e = ecs.Entity()
    .Add<Position>()
    .Add<Velocity>();
    Console.WriteLine(e.Type().Str()); // output: 'Position,Velocity'

A type can also be iterated by an application:

  • C

    const ecs_type_t *type = ecs_get_type(world, e);
    for (int i = 0; i < type->count; i++) {
    if (type->array[i] == ecs_id(Position)) {
    // Found Position component!
    }
    }
  • C++

    e.each([&](flecs::id id) {
    if (id == world.id<Position>()) {
    // Found Position component!
    }
    });
    flecs::id id(E value) const
    Convert enum constant to entity.
  • C#

    e.Each((Id id) =>
    {
    if (id == world.Id<Position>())
    {
    // Found Position component!
    }
    });

Singleton

A singleton is a single instance of a component that can be retrieved without an entity. The functions for singletons are very similar to the regular API:

  • C

    // Set singleton component
    ecs_singleton_set(world, Gravity, { 9.81 });
    // Get singleton component
    const Gravity *g = ecs_singleton_get(world, Gravity);
  • C++

    // Set singleton component
    world.set<Gravity>({ 9.81 });
    // Get singleton component
    const Gravity *g = world.get<Gravity>();
    const T * get() const
    Get singleton component.
    Definition world.hpp:116
    void set(const T &value) const
    Set singleton component.
    Definition world.hpp:548
  • C#

    // Set singleton component
    world.Set<Gravity>(new(9.81));
    // Get singleton component
    ref readonly Gravity g = ref world.Get<Gravity>();

Singleton components are created by adding the component to its own entity id. The above code examples are shortcuts for these regular API calls:

  • C

    ecs_set(world, ecs_id(Gravity), Gravity, {10, 20});
    const Gravity *g = ecs_get(world, ecs_id(Gravity), Gravity);
  • C++

    flecs::entity grav_e = world.entity<Gravity>();
    grav_e.set<Gravity>({10, 20});
    const Gravity *g = grav_e.get<Gravity>();
  • C#

    Entity gravE = world.Entity<Gravity>();
    gravE.Set<Gravity>(new(10, 20));
    ref readonly Gravity g = ref gravE.Get<Gravity>();

The following examples show how to query for a singleton component:

  • C

    // Create query that matches Gravity as singleton
    .filter.terms = {
    // Regular component
    { .id = ecs_id(Velocity) },
    // A singleton is a component matched on itself
    { .id = ecs_id(Gravity), .src.id = ecs_id(Gravity) }
    }
    });
    // Create a system using the query DSL with a singleton:
    ECS_SYSTEM(world, ApplyGravity, EcsOnUpdate, Velocity, Gravity($));
    #define ECS_SYSTEM(world, id, phase,...)
    Declare & define a system.
    Definition system.h:137
    #define ecs_query(world,...)
    Shorthand for creating a query with ecs_query_init().
    Definition flecs_c.h:259
  • C++

    world.query_builder<Velocity, Gravity>()
    .term_at(2).singleton()
    .build();
  • C#

    world.QueryBuilder<Velocity, Gravity>()
    .TermAt(2).Singleton()
    .Build();

Filter

Filters are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter:

  • C

    // Initialize a filter with 2 terms on the stack
    .terms = {
    { ecs_id(Position) },
    { ecs_pair(EcsChildOf, parent) }
    }
    });
    // Iterate the filter results. Because entities are grouped by their type there
    // are two loops: an outer loop for the type, and an inner loop for the entities
    // for that type.
    ecs_iter_t it = ecs_filter_iter(world, f);
    while (ecs_filter_next(&it)) {
    // Each type has its own set of component arrays
    Position *p = ecs_field(&it, Position, 1);
    // Iterate all entities for the type
    for (int i = 0; i < it.count; i++) {
    printf("%s: {%f, %f}\n", ecs_get_name(world, it.entities[i]),
    p[i].x, p[i].y);
    }
    }
    ecs_filter_t * ecs_filter_init(ecs_world_t *world, const ecs_filter_desc_t *desc)
    Initialize filter A filter is a lightweight object that can be used to query for entities in a world.
    bool ecs_filter_next(ecs_iter_t *it)
    Iterate tables matched by filter.
    ecs_iter_t ecs_filter_iter(const ecs_world_t *world, const ecs_filter_t *filter)
    Return a filter iterator.
    void ecs_filter_fini(ecs_filter_t *filter)
    Deinitialize filter.
    Used with ecs_filter_init().
    Definition flecs.h:998
    Filters allow for ad-hoc quick filtering of entity tables.
    Definition flecs.h:786
  • C++

    // For simple queries the each function can be used
    world.each([](Position& p, Velocity& v) { // flecs::entity argument is optional
    p.x += v.x;
    p.y += v.y;
    });
    // More complex filters can first be created, then iterated
    auto f = world.filter_builder<Position>()
    .term(flecs::ChildOf, parent)
    .build();
    // Option 1: each() function that iterates each entity
    f.each([](flecs::entity e, Position& p) {
    std::cout << e.name() << ": {" << p.x << ", " << p.y << "}" << std::endl;
    });
    // Option 2: iter() function that iterates each archetype
    f.iter([](flecs::iter& it, Position *p) {
    for (auto i : it) {
    std::cout << it.entity(i).name()
    << ": {" << p[i].x << ", " << p[i].y << "}" << std::endl;
    }
    });
    void each(Func &&func) const
    Iterate over all entities with components in argument list of function.
    flecs::filter_builder< Comps... > filter_builder(Args &&... args) const
    Create a filter builder.
    Class for iterating over query results.
    Definition iter.hpp:68
    flecs::entity entity(size_t row) const
    Obtain mutable handle to entity being iterated over.
    Definition iter.hpp:27
  • C#

    // For simple queries the each function can be used
    world.Each((ref Position p, ref Velocity v) => // Entity argument is optional
    {
    p.X += v.X;
    p.Y += v.Y;
    });
    // More complex filters can first be created, then iterated
    using Filter f = world.FilterBuilder<Position>()
    .Term(Ecs.ChildOf, parent)
    .Build();
    // Option 1: Each() function that iterates each entity
    f.Each((Entity e, ref Position p) =>
    {
    Console.WriteLine($"{e.Name()}: ({p.X}, {p.Y})")
    });
    // Option 2: Iter() function that iterates each archetype
    f.Iter((Iter it, Field<Position> p) =>
    {
    foreach (int i in it)
    Console.WriteLine($"{it.Entity(i).Name()}: ({p[i].X}, {p[i].Y})")
    });

Filters can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs.

The following example shows a filter that matches all entities with a parent that do not have Position:

  • C

    .terms = {
    { ecs_pair(EcsChildOf, EcsWildcard) }
    { ecs_id(Position), .oper = EcsNot },
    }
    });
    // Iteration code is the same
    const ecs_entity_t EcsWildcard
    Wildcard entity ("*").
    @ EcsNot
    The term must not match.
    Definition flecs.h:699
  • C++

    auto f = world.filter_builder<>()
    .term(flecs::ChildOf, flecs::Wildcard)
    .term<Position>().oper(flecs::Not)
    .build();
    // Iteration code is the same
  • C#

    using Filter f = world.FilterBuilder()
    .Term(Ecs.ChildOf, Ecs.Wildcard)
    .Term<Position>().Oper(Ecs.Not)
    .Build();
    // Iteration code is the same

Query

Queries are cached versions of filters. They are slower to create than filters, but much faster to iterate since this just means iterating their cache.

The API for queries is similar to filters:

  • C

    // Create a query with 2 terms
    .filter.terms = {
    { ecs_id(Position) },
    { ecs_pair(EcsChildOf, EcsWildcard) }
    }
    });
    ecs_iter_t it = ecs_query_iter(world, q);
    while (ecs_query_next(&it)) {
    // Same as for filters
    }
  • C++

    // Create a query with two terms
    auto q = world.query_builder<Position>()
    .term(flecs::ChildOf, flecs::Wildcard)
    .build();
    // Iteration is the same as filters
  • C#

    // Create a query with two terms
    Query q = world.QueryBuilder<Position>()
    .Term(Ecs.ChildOf, Ecs.Wildcard)
    .Build();
    // Iteration is the same as filters

When using queries, make sure to reuse a query object instead of creating a new one each time you need it. Query creation is expensive, and many of the performance benefits of queries are lost when they are created in loops.

See the query manual for more details.

System

A system is a query combined with a callback. Systems can be either ran manually or ran as part of an ECS-managed main loop (see Pipeline). The system API looks similar to queries:

  • C

    // Option 1, use the ECS_SYSTEM convenience macro
    ECS_SYSTEM(world, Move, 0, Position, Velocity);
    ecs_run(world, Move, delta_time, NULL); // Run system
    // Option 2, use the ecs_system_init function/ecs_system macro
    ecs_entity_t move_sys = ecs_system(world, {
    .query.filter.terms = {
    {ecs_id(Position)},
    {ecs_id(Velocity)},
    },
    .callback = Move
    });
    ecs_run(world, move_sys, delta_time, NULL); // Run system
    // The callback code (same for both options)
    void Move(ecs_iter_t *it) {
    Position *p = ecs_field(it, Position, 1);
    Velocity *v = ecs_field(it, Velocity, 2);
    for (int i = 0; i < it->count; i++) {
    p[i].x += v[i].x * it->delta_time;
    p[i].y += v[i].y * it->delta_time;
    }
    }
    #define ecs_system(world,...)
    Shorthand for creating a system with ecs_system_init().
    Definition system.h:161
    FLECS_API ecs_entity_t ecs_run(ecs_world_t *world, ecs_entity_t system, ecs_ftime_t delta_time, void *param)
    Run a specific system manually.
  • C++

    // Use each() function that iterates each individual entity
    auto move_sys = world.system<Position, Velocity>()
    .iter([](flecs::iter it, Position *p, Velocity *v) {
    for (auto i : it) {
    p[i].x += v[i].x * it.delta_time();
    p[i].y += v[i].y * it.delta_time();
    }
    });
    // Just like with filters & queries, systems have both the iter() and
    // each() methods to iterate entities.
    move_sys.run();
    flecs::system system(flecs::entity e) const
    Upcast entity to a system.
  • C#

    // Use Each() function that iterates each individual entity
    Routine moveSys = world.Routine<Position, Velocity>()
    .Iter((Iter it, Field<Position> p, Field<Velocity> v) =>
    {
    foreach (int i in it)
    {
    p[i].X += v[i].X * it.DeltaTime();
    p[i].Y += v[i].Y * it.DeltaTime();
    }
    });
    // Just like with filters & queries, systems have both the Iter() and
    // Each() methods to iterate entities.
    moveSys.Run();

Systems are stored as entities with an EcsSystem component (flecs::System in C++), similar to components. That means that an application can use a system as a regular entity:

  • C

    printf("System: %s\n", ecs_get_name(world, move_sys));
    ecs_add(world, move_sys, EcsOnUpdate);
    ecs_delete(world, move_sys);
  • C++

    std::cout << "System: " << move_sys.name() << std::endl;
    move_sys.add(flecs::OnUpdate);
    move_sys.destruct();
  • C#

    Console.WriteLine($"System: {moveSys.Name()}");
    moveSys.Entity.Add(Ecs.OnUpdate);
    moveSys.Entity.Destruct();

Pipeline

A pipeline is a list of tags that when matched, produces a list of systems to run. These tags are also referred to as a system "phase". Flecs comes with a default pipeline that has the following phases:

  • C

    EcsOnLoad
    EcsPostLoad
    EcsPreUpdate
    EcsOnUpdate
    EcsOnValidate
    EcsPostUpdate
    EcsPreStore
    EcsOnStore
  • C++

    flecs::OnLoad
    flecs::PostLoad
    flecs::PreUpdate
    flecs::OnUpdate
    flecs::OnValidate
    flecs::PostUpdate
    flecs::PreStore
    flecs::OnStore
  • C#

    Ecs.OnLoad
    Ecs.PostLoad
    Ecs.PreUpdate
    Ecs.OnUpdate
    Ecs.OnValidate
    Ecs.PostUpdate
    Ecs.PreStore
    Ecs.OnStore

When a pipeline is executed, systems are ran in the order of the phases. This makes pipelines and phases the primary mechanism for defining ordering between systems. The following code shows how to assign systems to a pipeline, and how to run the pipeline with the progress() function:

  • C

    ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity);
    ECS_SYSTEM(world, Transform, EcsPostUpdate, Position, Transform);
    ECS_SYSTEM(world, Render, EcsOnStore, Transform, Mesh);
    ecs_progress(world, 0); // run systems in default pipeline
    FLECS_API bool ecs_progress(ecs_world_t *world, ecs_ftime_t delta_time)
    Progress a world.
  • C++

    world.system<Position, Velocity>("Move").kind(flecs::OnUpdate).each( ... );
    world.system<Position, Transform>("Transform").kind(flecs::PostUpdate).each( ... );
    world.system<Transform, Mesh>("Render").kind(flecs::OnStore).each( ... );
    world.progress();
    bool progress(ecs_ftime_t delta_time=0.0) const
    Progress world one tick.
    void each(const Func &func) const
    Iterate (component) ids of an entity.
    Definition impl.hpp:117
  • C#

    world.Routine<Position, Velocity>("Move").Kind(Ecs.OnUpdate).Each( ... );
    world.Routine<Position, Transform>("Transform").Kind(Ecs.PostUpdate).Each( ... );
    world.Routine<Transform, Mesh>("Render").Kind(Ecs.OnStore).Each( ... );
    world.Progress();

Because phases are just tags that are added to systems, applications can use the regular API to add/remove systems to a phase:

  • C

    ecs_remove_id(world, Move, EcsOnUpdate);
    ecs_add_id(world, Move, EcsPostUpdate);
  • C++

    move_sys.add(flecs::OnUpdate);
    move_sys.remove(flecs::PostUpdate);
  • C#

    moveSys.Add(Ecs.OnUpdate);
    moveSys.Remove(Ecs.PostUpdate);

Inside a phase, systems are guaranteed to be ran in their declaration order.

Observer

Observers are callbacks that are invoked when one or more events matches the query of an observer. Events can be either user defined or builtin. Examples of builtin events are OnAdd, OnRemove and OnSet.

When an observer has a query with more than one component, the observer will not be invoked until the entity for which the event is emitted satisfies the entire query.

An example of an observer with two components:

  • C

    ecs_observer(world, {
    .filter.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }},
    .event = EcsOnSet,
    .callback = OnSetPosition
    });
    // Callback code is same as system
    ecs_entity_t e = ecs_new_id(world); // Doesn't invoke the observer
    ecs_set(world, e, Position, {10, 20}); // Doesn't invoke the observer
    ecs_set(world, e, Velocity, {1, 2}); // Invokes the observer
    ecs_set(world, e, Position, {20, 40}); // Invokes the observer
    const ecs_entity_t EcsOnSet
    Event that triggers when a component is set for an entity.
    #define ecs_observer(world,...)
    Shorthand for creating an observer with ecs_observer_init().
    Definition flecs_c.h:274
  • C++

    world.observer<Position, Velocity>("OnSetPosition").event(flecs::OnSet).each( ... );
    auto e = ecs.entity(); // Doesn't invoke the observer
    e.set<Position>({10, 20}); // Doesn't invoke the observer
    e.set<Velocity>({1, 2}); // Invokes the observer
    e.set<Position>({20, 30}); // Invokes the observer
    flecs::observer observer(flecs::entity e) const
    Observer builder.
  • C#

    world.Observer<Position, Velocity>("OnSetPosition").Event(Ecs.OnSet).Each( ... );
    Entity e = ecs.Entity(); // Doesn't invoke the observer
    e.Set<Position>(new(10, 20)); // Doesn't invoke the observer
    e.Set<Velocity>(new(1, 2)); // Invokes the observer
    e.Set<Position>(new(20, 30)); // Invokes the observer

Module

A module is a function that imports and organizes components, systems, triggers, observers, prefabs into the world as reusable units of code. A well designed module has no code that directly relies on code of another module, except for components definitions. All module contents are stored as child entities inside the module scope with the ChildOf relationship. The following examples show how to define a module in C and C++:

  • C

    // Module header (e.g. MyModule.h)
    typedef struct {
    float x;
    float y;
    } Position;
    extern ECS_COMPONENT_DECLARE(Position);
    // The import function name has to follow the convention: <ModuleName>Import
    void MyModuleImport(ecs_world_t *world);
    // Module source (e.g. MyModule.c)
    void MyModuleImport(ecs_world_t *world) {
    ECS_MODULE(world, MyModule);
    ECS_COMPONENT_DEFINE(world, Position);
    }
    // Import code
    ECS_IMPORT(world, MyModule);
    #define ECS_IMPORT(world, id)
    Wrapper around ecs_import().
    Definition module.h:118
    #define ECS_COMPONENT_DEFINE(world, id_)
    Define a forward declared component.
    Definition flecs_c.h:122
    #define ECS_COMPONENT_DECLARE(id)
    Forward declare a component.
    Definition flecs_c.h:112
  • C++

    struct my_module {
    my_module(flecs::world& world) {
    world.module<my_module>();
    // Define components, systems, triggers, ... as usual. They will be
    // automatically created inside the scope of the module.
    }
    };
    // Import code
    world.import<my_module>();
    flecs::entity import()
    Import a module.
    flecs::entity module(const char *name=nullptr) const
    Define a module.
  • C#

    public struct MyModule : IFlecsModule
    {
    public void InitModule(ref World world)
    {
    world.Module<MyModule>();
    // Define components, systems, triggers, ... as usual. They will be
    // automatically created inside the scope of the module.
    }
    };
    // Import code
    world.Import<MyModule>();