Skip to content
Alan Jefferson edited this page Jan 22, 2020 · 23 revisions

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.


Members


create

#include <entt/entity/registry.hpp>

Create a new entity.

entt::entity entity = registry.create();
  • Entities are of type std::uint32_t per default, they don't store any data. Instead, they associate data; see assign.

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());

destroy

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

assign

#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 assign with an already existing type throws an exception (in Debug)
  • Use replace to replace a component
  • Or remove followed by replace to re-trigger the on_construct handler
  • replace triggers the on_replace handler, not on_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 constructed

remove

The 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

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 constructed

assign_or_replace

Just 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..
}

reset

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);
      }
  });
}

get

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);
}

view

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();
}

each

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();
});

less

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?


has

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";
}

on_construct

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) {}

observer

#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