-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This first page is a quick reference for when you're looking to jog your memory. See table of contents to the right for a more detailed look.
- create
- query
- events
- extras
#include <entt/entity/registry.hpp>
Create a new entity.
entt::entity entity = registry.create();- Entities are of type
std::uint32_tper default, they don't store any data. Instead, they associate data; seeassign.
Examples
// Typical usage
auto entity = registry.create();// Serialise entities via to_integer
auto entity = registry.create();
auto serialised = entt::to_integer(entity); // default is std::uint32_t// Create in bulk..
std::vector<entt::entity> entities(10);
registry.create(entities.begin(), entities.end());
// ..with pre-assigned component
std::vector<entt::entity> entities(10);
registry.create<Position, Orientation>(entities.begin(), entities.end());Delete an entity, and all of its components.
registry.destroy(entity);Examples
auto entity = registry.create();
registry.assign<Position>(entity);
registry.destroy(entity);
// entity and the assigned Position component was destroyed
#include <entt/entity/registry.hpp>
Create a new component.
registry.assign<Position>(entity);- Components are initialised on assignment
- Initialiser arguments are optional, but recommended
- Only one component type may exist per entity
- Calling
assignwith an already existing type throws an exception (in Debug) - Use
replaceto replace a component - Or
removefollowed byreplaceto re-trigger theon_constructhandler -
replacetriggers theon_replacehandler, noton_construct(despite also being constructed)
Examples
struct Position { float x, y, z; };
auto entity = registry.create();
registry.assign<Position>(entity, 1.0f, 2.0f, 4.0f);The first argument is the entitiy with which to associate this new component, the remaining arguments are passed to the constructor of the component. It is the equivalent of:
Position position { 1.0f, 2.0f, 4.0f };
registy.assign<Position>(entity, position);Passing no arguments default constructs the component.
registry.assign<Position>(entity); // Default constructedThe opposite of assign, remove a single component from an entity.
registry.remove<Position>(entity);Examples
auto entity = registry.create();
registry.assign<Position>(entity);
registry.remove<Position>(entity);Replace an existing component.
Trying to replace a component not already assigned is undefined
registry.replace<Position>(entity, 1.0f, 5.0f);Examples
auto entity = registry.create();
registry.assign<Position>(entity, 5.0f, 2.0f);
registry.replace<Position>(entity) // default constructedJust assign, no matter what.
registry.assign_or_replace<Position>(entity);Examples
If you're assigning in a loop, you may consider doing..
// somewhere..
registry.assign<Position>(entity);
// ..elsewhere
while (true) {
registry.replace<Position>(entity);
// other things..
}However is Position is removed for whatever reason during other things.. you run into trouble. And whilst you could..
while (true) {
if (!registry.has<Position>(entity)) {
registry.assign<Position>(entity);
} else {
registry.replace<Position>(entity);
}
// other things..
}A more readable option might be..
while (true) {
registry.assign_or_replace<Position>(entity);
// other things..
}Remove all components of any or all types.
registry.reset<Position>(); // all Position components
registry.reset(); // all components (and entities, because what good are they now?)Examples
Useful for managing selection.
registry.reset<Selected>();
if (some_condition) {
registry.assign<Selected>(entity);
}Or whether something is hovered.
void mouseMoveEvent(const MouseEvent& event) {
registry.reset<Hovered>();
registry.view<Position, Shape>().each([&](auto entity, const auto& pos, const auto& shape) {
if (contains(pos, shape, event.position())) {
registry.assign<Hovered>(entity);
}
});
}Components can be explicitly queried from an entity.
auto pos = registry.get<Position>(entity);Examples
auto entity = registry.create();
registry.assign<Position>(entity);
auto pos = registry.get<Position>(entity);You typically won't get components, instead you should use view to iterate on components in bulk.
void on_position_created(auto entity) {
auto ori = registry.get<Orientation>(entity);
}Iterate over a single or combination of components.
for (auto entity : registry.view<Position>()) {
// ...
}This is the most common method with which to interact with entities and components.
Examples
Iterate over all entities with one specific type of component assigned
auto entity = registry.create();
registry.assign<Position>(entity);
for (auto entity : registry.view<Position>()) {
auto& pos = registry.get<Position>(entity);
pos.x += 1.0f;
}Iterate over all entites that have a combination of components.
for (auto entity : registry.view<Name, Position, Color>()) {
const auto& name = registry.get<Name>(entity);
const auto& col = registry.get<Color>(entity);
auto& pos = registry.get<Position>(entity);
printf("Adding the red color to the x position of %s", name);
pos.x += col.r();
}For convenient, you can also return two or more components from a single .get<>.
for (auto entity : registry.view<Name, Position, Color>()) {
const auto& [name, col] = registry.get<Name, Color>(entity);
auto& pos = registry.get<Position>(entity);
printf("Adding the red color to the x position of %s", name);
pos.x += col.r();
}Combine view and get into a single query with each.
registry.view<Position>().each([](auto entity, auto& pos) {
pos.x += 1.0f;
});Examples
If you don't need to access the entity, then don't include it.
registry.view<Name, Position, Color>().each([](const auto& name, auto& pos, const auto& col) {
printf("Adding the red color to the x position of %s", name);
pos.x += col.r();
});A view returns entities that carry all of the specified components. Like a filter. Sometimes it may make sense to use components strictly for filtering.
struct IsAlive {};
// ...
registry.view<IsAlive, Position>().each([](const auto& isalive, auto& pos) {
printf("An entity at (%d, %d) is alive!\n", pos.x, pos.y);
});As an empty struct doesn't provide any information and serve no purpose inside of your loop, they would not be missed if not included. And that's what less is for.
registry.view<IsAlive, Position>().less([](auto& pos) {
printf("An entity at (%d, %d) is alive!\n", pos.x, pos.y);
});See how much more readable that is?
Check whether an entity has a component with registry::has
if (entt::has<Position>(entity)) {
// do work..
}Example
Iterating over components is both branchless and guaranteed to never hand you a nullptr. This is where view is more pleasant to work with than get, but in the few cases where you need get, how can you be sure the component you're asking for exists?
auto id = registry.try_get<Id>(entity);
if (id) {
id.name = "Alan";
}
// Equivalent to
if (registry.has<Id>(entity)) {
auto id = registry.get<Id>(entity);
id.name = "Alan";
}Call a function or method when a component has been constructed.
entt::on_construct<Position>(&onPositionConstructed);Example
In object-oriented programming, types have a constructor. Constructors come in handy when there is more to initialisation than just de-nullifying initial values. With on_construct you can achieve similar benefit, but in an ECS way.
struct Position {
float x, y, z;
};
void on_position_created(entt::entity ent, entt:registry& reg, Position& pos) {
pos.x = 0.5;
pos.y = pos.x / 2.0;
pos.z = pos.x + pos.z;
}
// Attach event handler
registry.on_construct<Position>().connect<&on_position_created>();
auto player = registry.create();
registry.assign<Position>(player); // <-- called here (after creation)Alternatives
// Equally valid argument signatures; less arguments is faster
void on_position_created_1(entt::entity ent, entt:registry& reg, Position& pos) {}
void on_position_created_2(entt::entity ent, entt:registry& reg) {}
void on_position_created_3(entt::entity ent) {}
#include <entt/entity/observer.hpp>
Where on_construct executes a callback on the exact moment of a component being constructed, an entt::observer can defer a callback until explicitly called.
entt::observer newPositions { registry, entt::collector.group<Position>() };
auto entity = registry.create();
registry.assign<Position>(entity);
for (const auto e : newPositions) {
assert(e == entity);
}| Member | Description |
|---|---|
entt::collector |
A.k.a. "rule", when to consider an event related to a given observer. Name inherited from the Entitas project |
entt::collector.group |
EnTT - Fast and Reliable ECS (Entity Component System)
Table of contents
Examples
Blog
- RAII
- Polymorphism
- Shared Components
- Intent System
- Input Handling
- Undo
- Operator Stack
- State
- Resources
- Interpolation
Resources
Extras