Link to the Flecs Discord. Link to the repository.

After having been in development for almost a year, Flecs 2.0, an Entity Component System for C and C++ is finally out! This release is almost a complete revamp of the internal data structures, and comes with many new features that make it easier than ever to build games with an Entity Component System.

Here are the some of the highlights of v2:

Building C and C++ code can sometimes be a chore, especially if a library doesn’t use the same build system as your own project. For this reason Flecs v2 is now packaged in just two files, flecs.c and flecs.h. All you need to do to get started is copy these files to your C or C++ project!

This feature is completely invisible for the user, but represents a big change under the hood. Whenever components are added or removed from an entity, the entity is moved to a new “table” (sometimes called archetype). This table needs to be looked up, which takes time. In v2, table lookups are graph-based, and this results in a massive performance improvement over v1. If you’d like to read more about the graph design, see https://medium.com/@ajmmertens/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9

Similar to the table graph, this change is invisible for the API, but is yet another big performance improvement. Previously the entity index was based on a map, but in v2 most entity lookups use a sparse set. In practice this means that it is much faster to find an entity based on its id, which improves performance of almost any entity-based operation.

In Flecs v1 it was possible to iterate over entities using systems, which were functions that Flecs automatically ran each frame. In Flecs v2 systems are still available, but applications can now also iterate over entities in free functions, by using queries. This is what a typical query looks like in the C++ API:

flecs::query<Position, Velocity> q(world);q.each([](flecs::entity e, Position& p, Velocity& v) {
p.x += v.x;
p.y += v.y;
});

Read more about queries in the Quickstart.

The query feature comes with a useful new capability, which is sorting. Applications can specify that a query needs to be sorted, which causes Flecs to automatically keep entities that match a query sorted. This makes it easy to, for example, build a render system that renders entities back to front. This is an example of how sorting is setup in the C API:

ecs_query_t *q = ecs_query_new(world, "Position");ecs_query_order_by(
world, q, ecs_entity(Position), compare_position);

Once setup, entities will be automatically resorted every time the application iterates over the query, if the data was changed since the last iteration. For the full example code, go to this example. For more information about sorting, see this section of the manual.

Hierarchies were already supported in v1, but in v2 they are much more useful and usable. It would be too much to list all of the improvements here, but one of them is the ability to easily iterate over the children of an entity. This is an example of how this is done in the C++ API:

for (auto children : entity.children()) {
for (auto i : children) {
std::cout << children.entity(i).name() << std::endl;
}
}

Additionally, there have been many improvements to creating hierarchies using string-based paths. The following code creates a simple hierarchy of entities, and looks up entities using a relative lookup function:

auto foo = flecs::entity(world, "foo");
auto bar = flecs::entity(world, "foo::bar");
// Returns "foo::bar"
auto result = foo.lookup("bar");

Something that is notoriously difficult in many ECS implementations is implementing generic behavior that is matched with many components. Because systems are matched to a well-defined set of components, it is typically not possible to create one system that works on many components. To address this problem, Flecs introduces component traits. Here’s an example of traits in the C API:

ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, ExpiryTimer);
ECS_SYSTEM(world, Expire, EcsOnUpdate, TRAIT | ExpiryTimer);ecs_entity_t e = ecs_new(world, 0);
ecs_add(world, e, Position);
ecs_add(world, e, Velocity);
ecs_set_trait(world, e, Position, ExpiryTimer, {.expiry_time = 3});
ecs_set_trait(world, e, Velocity, ExpiryTimer, {.expiry_time = 2});

This small example shows how a trait (“ExpiryTimer”) can be added to the same entity twice, once for component Position, and once for component Velocity. This trait is designed to remove the component it is applied to after “expiry_time” seconds. Click here for the full example, or see the QuickStart for more information on traits.

A big improvement over Flecs v1 is that you can now specify component lifecycle callbacks, which are invoked when components are constructed, destructed, copied or moved. This makes it possible to create components that store complex data structures without having to worry about memory leaks or uninitialized values. What’s better, the feature is 100% integrated with C++, so you can write C++ constructors, destructors, copy & move assignment operators as usual, and Flecs will invoke them at the right times! Read more about component lifecycle in the Quickstart.

As the feature set is growing, it is possible that a project doesn’t need all of the features that Flecs offers. Flecs v2 makes it easy to remove the features that you don’t need, which means that if all you’re looking for is a minimal ECS framework, you simply remove everything that is optional with a custom build. Customizing your build is really easy, see the README on how to set this up.

A much requested feature in Flecs v1 was to allow applications to specify their own system phases. A phase in Flecs allows applications to sequence systems in a frame, for example, EcsOnUpdate comes after EcsPreUpdate. In Flecs v1 these phases were fixed and hardcoded in the library. In v2 applications can now specify their own phases, and define their own system pipelines. To read more about pipelines, see this section in the Quickstart.

Monitors are a new kind of system that are executed once when a condition becomes true, where a condition is specified as a component query. This is different from a regular system, which is executed as long as a condition is true. Monitors make it easy to respond to events in your game that depend on multiple components being added/removed to your entities. To read more about monitors, see this section in the Quickstart.

This is just the tip of the iceberg, for a full list of things that have changed between v1 and v2, check the Migration Guide!

If you’re thinking about becoming a Flecs user, or if you’ve already been using it for your projects, feel free to join the Flecs Discord!

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