Frequently asked questions.
See the ECS FAQ
There are a lot of reasons, but the main ones are:
- Faster compile times, especially when compared with header-only C++ libraries
- C can be called from almost any programming language
- C is super portable and has a tiny runtime so you can run it on a toaster if you want
- The rules and limitations of the ECS aren't dictated by any type system
- You can create a zero-overhead C++ API on top of C, but not the other way around
You can! Even though the C++ API is C++11, you can use it with any revision at or above 11.
You can! Components can contain almost any C++ type.
Archetype-based refers to the way the ECS stores components. It means that all entities with the same components are stored together in an archetype. This provides efficient CPU cache utilization and allows for things like vectorization and fast querying.
Other examples of archetype implementations are Unity DOTS, Unreal Mass and Bevy ECS.
For more information, see this blog: https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9
Flecs and EnTT both are ECS libraries, but other than that they are different in almost every way, which can make comparing the two frameworks tricky. When you are comparing Flecs and EnTT, you can generally expect to see the following:
- Add/remove operations are faster in EnTT
- Single component queries are faster in EnTT
- Multi-component queries are faster in Flecs
- Bulk-creating entities are faster in Flecs
- Entity destruct are faster in Flecs (especially for worlds/registries with lots of components)
- Iterating a single entity's components are faster in Flecs
When doing a benchmark comparison don't rely on someone else's numbers, always test for your own use case!
Yes, Flecs is used commercially (see the README).
This is likely because queries (flecs::query
) are created repeatedly in a loop or system. Queries are the fastest way to iterate over entities, but are expensive to create, so make sure to create them in advance:
// GOOD
flecs::query<Position> q = world.query<Position>();
world.system()
.run([=](flecs::iter& it) {
q.each([&](Position& p) {
// ...
});
});
// BAD
world.system()
.run([](flecs::iter& it) {
flecs::query<Position> q = world.query<Position>();
q.each([&](Position& p) {
// ...
});
});
Likely because of the same reason as above. Queries are registered with the world, and unless they are deleted explicitly they will take up space. To delete a query, do:
q.destruct();
System functions are called for each matching archetype (see above for "What is an archetype"). In short, the reason for this is that it provides systems with direct access to component arrays.
If you have code that needs to run only once per frame or you want to take control over the entire iteration, take a look the custom_runner
examples, which show how to use the run
callback.
Yes it can! See the quickstart manual for more information.
This happens in C if the variable that holds the component id can't be found. This example shows how to fix it: https://github.com/SanderMertens/flecs/tree/master/examples/c/entities/fwd_declare_component
When you inspect the integer value of an entity you may see that this value is very large, usually around 4 billion. This is not a bug, and instead means that the entity id has been recycled. This example shows when recycling happens:
flecs::entity e1 = world.entity(); // small id
e1.destruct(); // id is made available for recycling
flecs::entity e2 = world.entity(); // recycles e1
e1.is_alive(); // false
e2.is_alive(); // true
std::cout << e2.id(); // large id, upper 32 bits contains liveliness count
An add
just adds a component without assigning a value to it. A set
assigns a value to the component. Both operations ensure that the entity will have the component afterwards.
An add
is used mostly for adding things that don't have a value, like empty types/tags and most relationships. If add
is used with a component that has a constructor, adding the component will invoke its constructor.
Additionally you can use emplace
to construct a component in place in the storage (similar to std::vector::emplace
).
Yes it can! See the reflection examples:
- https://github.com/SanderMertens/flecs/tree/master/examples/c/reflection
- https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection
If you look at the size of the distr/flecs.c and distr/flecs.h files you might wonder why they are so large. There are a few reasons:
- The files contain a lot of comments and in-code documentation!
- Flecs has a small core with a lot of addons. The distr/flecs.c and distr/flecs.h files are the full source code, including addons.
- The distr/flecs.h file contains the full C++ API.
- Flecs implements its own data structures like vectors and maps, vs. depending on something like the STL.
- C tends to be a bit more verbose than other languages.
Not all addons are useful in any project. You can customize a Flecs build to only build the things you need, which reduces executable size and improves build speed. See the quickstart on how to customize a build.
Make sure that:
- The REST API is enabled (see the Remote API manual)
- You can reach the REST API by testing http://localhost:27750/entity/flecs
- You call
ecs_progress
/world::progress
in your main loop
If that doesn't work, see the README of the explorer for potential issues with browser security settings and how to run a local instance of the explorer.
No! The https://flecs.dev/explorer page is a 100% client side application that does not talk to a backend. When you navigate to the page it will attempt to connect to http://localhost:27750 to find a running Flecs application. The data that the explorer fetches never leaves your machine.
The explorer can only display component values if the reflection data has been registered for the component. See the C reflection examples and C++ reflection examples for more information.
Flecs has builtin change detection. Additionally, you can use an OnSet
observer to get notified of changes to component values. See the query change detection and observer examples for more information.
No, relationships are a deeply integrated feature that is faster in many ways than a component with an entity handle. See the relationship manual for more information.
You can! Systems can be created from any function, except other systems.
You can! The flecs scheduler (implemented in the pipeline addon) is fully optional and is built on top of Flecs. You can create a custom pipeline, or a custom schedule implementation that does something else entirely than what Flecs provides.
You can! Systems are an optional addon that can be disabled. You can build applications that just use Flecs queries.
This is likely because the entity has a parent. A lookup by name requires you to provide the full path to an entity, like:
ecs_lookup(world, "parent.child");
or in C++:
world.lookup("parent::child");
You can! By default ECS operations are deferred when called from inside a system, which means that they are added to a queue and processed later when it's safe to do so. Flecs does not have a dedicated command API, if you call an operation from a system it will be automatically added to the command queue!
A LOCKED_STORAGE error means that you're trying to add or remove an entity to an archetype that is currently being accessed, usually during query iteration. An example:
q.each([](flecs::entity e) {
e.destruct(); // LOCKED_STORAGE error: removes entity from table
});
Most LOCKED_STORAGE errors can be resolved by putting defer_begin
and defer_end
around the iteration, which postpones the table-changing operations until after the iteration:
world.defer_begin();
q.each([](flecs::entity e) {
e.destruct();
});
world.defer_end(); // OK: entities are deleted here