Skip to content

Commit

Permalink
Changed: world::derive renamed to world::as
Browse files Browse the repository at this point in the history
Changed: IsA renamed to As
Tweaked: early exit for world::is
  • Loading branch information
richardbiely committed Jan 7, 2024
1 parent d1ed2a9 commit 52b79b9
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 45 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ w.add(e, weak);
```

### Entity inheritance
Entities can inherit from other entities by using the (IsA, target) relationship. This is a powerful feature that helps you identify an entire group of entities using a single entity.
Entities can inherit from other entities by using the (As, target) relationship. This is a powerful feature that helps you identify an entire group of entities using a single entity.

```cpp
ecs::World w;
Expand All @@ -893,9 +893,14 @@ ecs::Entity herbivore = w.add();
ecs::Entity rabbit = w.add();
ecs::Entity hare = w.add();

w.add(herbivore, ecs::Pair(ecs::IsA, animal)); // w.derive(herbivore, animal)
w.add(rabbit, ecs::Pair(ecs::IsA, herbivore)); // w.derive(rabbit, herbivore)
w.add(hare, ecs::Pair(ecs::IsA, herbivore)); // w.derive(hare, herbivore)
w.add(herbivore, ecs::Pair(ecs::As, animal)); // w.as(herbivore, animal)
w.add(rabbit, ecs::Pair(ecs::As, herbivore)); // w.as(rabbit, herbivore)
w.add(hare, ecs::Pair(ecs::As, herbivore)); // w.as(hare, herbivore)

bool herbibore_is_animal = w.is(herbivore, animal); // true
bool rabbit_is_herbivore = w.is(rabbit, herbivore); // true
bool rabbit_is_animal = w.is(rabbit, animal); // true
bool animal_is_rabbit = w.is(animal, rabbit); // false

ecs::Query q = w.query().all(animal);
q.each([](ecs::Entity entity) {
Expand Down
22 changes: 18 additions & 4 deletions include/gaia/ecs/archetype.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,18 @@ namespace gaia {
//! Number of ticks before empty chunks are removed
static constexpr uint16_t MAX_ARCHETYPE_LIFESPAN = (1 << ARCHETYPE_LIFESPAN_BITS) - 1;

//! Remaining lifespan of the archetype
uint32_t m_lifespanCountdown: ARCHETYPE_LIFESPAN_BITS;
//! If set the archetype is to be deleted
uint32_t m_dead : 1;
//! Number of relationship pairs on the archetype
uint32_t m_pairCnt: Chunk::MAX_COMPONENTS_BITS;
//! Number of As relationship pairs on the archetype
uint32_t m_pairCnt_as: Chunk::MAX_COMPONENTS_BITS;

// Constructor is hidden. Create archetypes via Create
Archetype(const ComponentCache& cc, uint32_t& worldVersion):
m_cc(cc), m_worldVersion(worldVersion), m_lifespanCountdown(0), m_dead(0), m_pairCnt(0) {}
m_cc(cc), m_worldVersion(worldVersion), m_lifespanCountdown(0), m_dead(0), m_pairCnt(0), m_pairCnt_as(0) {}

//! Calulcates offsets in memory at which important chunk data is going to be stored.
//! These offsets are use to setup the chunk data area layout.
Expand Down Expand Up @@ -296,9 +301,14 @@ namespace gaia {

// Calculate the number of pairs
GAIA_EACH(ids) {
if (ids[i].pair()) {
++newArch->m_pairCnt;
}
if (!ids[i].pair())
continue;

++newArch->m_pairCnt;

// If it is a As relationship, count it separately as well
if (ids[i].id() == As.id())
++newArch->m_pairCnt_as;
}

// Find the index of the last generic component in both arrays
Expand Down Expand Up @@ -604,6 +614,10 @@ namespace gaia {
return m_pairCnt;
}

GAIA_NODISCARD uint32_t pairs_as() const {
return m_pairCnt_as;
}

/*!
Checks if an entity is a part of the archetype.
\param entity Entity
Expand Down
4 changes: 2 additions & 2 deletions include/gaia/ecs/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,10 @@ namespace gaia {
//! Entity representing a physical hierarchy
inline Entity ChildOf = Entity(12, 0, false, false, EntityKind::EK_Gen);
//! Alias for a base entity
inline Entity IsA = Entity(13, 0, false, false, EntityKind::EK_Gen);
inline Entity As = Entity(13, 0, false, false, EntityKind::EK_Gen);

// Always has to match the last internal entity
inline Entity GAIA_ID(LastCoreComponent) = IsA;
inline Entity GAIA_ID(LastCoreComponent) = As;

//----------------------------------------------------------------------
// Helper functions
Expand Down
29 changes: 17 additions & 12 deletions include/gaia/ecs/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,15 @@ namespace gaia {
return *this;
}

//! Shortcut for add(Pair(IsA, entityBase)).
//! Shortcut for add(Pair(As, entityBase)).
//! Effectively makes an entity inherit from \param entityBase
EntityBuilder& derive(Entity entityBase) {
return add(Pair(IsA, entityBase));
EntityBuilder& as(Entity entityBase) {
return add(Pair(As, entityBase));
}

//! Check if \param entity inherits from \param entityBase
//! \return True if entity inherits from entityBase
GAIA_NODISCARD bool derive(Entity entity, Entity entityBase) const {
GAIA_NODISCARD bool as(Entity entity, Entity entityBase) const {
return static_cast<const World&>(m_world).is(entity, entityBase);
}

Expand Down Expand Up @@ -290,7 +290,7 @@ namespace gaia {
}

void try_set_AliasOf(Entity entity, bool enable) {
if (!entity.pair() || entity.id() != IsA.id())
if (!entity.pair() || entity.id() != As.id())
return;

updateFlag(m_entity, EntityContainerFlags::HasAliasOf, enable);
Expand Down Expand Up @@ -1573,7 +1573,7 @@ namespace gaia {

// Base entity is.
{
const auto& id = IsA;
const auto& id = As;
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<AliasOf_>(id);
GAIA_ASSERT(desc.entity == id);
Expand Down Expand Up @@ -1623,7 +1623,7 @@ namespace gaia {
.add(Acyclic)
.add(Pair(OnDelete, Error))
.add(Pair(OnDeleteTarget, Delete));
EntityBuilder(*this, IsA) //
EntityBuilder(*this, As) //
.add(Core)
.add(Acyclic)
.add(Pair(OnDelete, Error));
Expand Down Expand Up @@ -1979,21 +1979,26 @@ namespace gaia {

//----------------------------------------------------------------------

//! Shortcut for add(entity, Pair(IsA, entityBase)
void derive(Entity entity, Entity entityBase) {
add(entity, Pair(IsA, entityBase));
//! Shortcut for add(entity, Pair(As, entityBase)
void as(Entity entity, Entity entityBase) {
add(entity, Pair(As, entityBase));
}

//! Checks if \param entity inherits from \param entityBase.
//! True if entity is an is for entityBase. False otherwise.
GAIA_NODISCARD bool is(Entity entity, Entity entityBase) const {
const auto& ec = fetch(entity);
const auto& ids = ec.pArchetype->ids();
const auto* pArchetype = ec.pArchetype;

// Early exit if there are no As relationships on the archetype
if (pArchetype->pairs_as() == 0)
return false;

const auto& ids = ec.pArchetype->ids();
for (auto e: ids) {
if (!e.pair())
continue;
if (e.id() != IsA.id())
if (e.id() != As.id())
continue;

const auto& ecTarget = m_recs.entities[e.gen()];
Expand Down
55 changes: 37 additions & 18 deletions single_include/gaia.h
Original file line number Diff line number Diff line change
Expand Up @@ -14317,10 +14317,10 @@ namespace gaia {
//! Entity representing a physical hierarchy
inline Entity ChildOf = Entity(12, 0, false, false, EntityKind::EK_Gen);
//! Alias for a base entity
inline Entity IsA = Entity(13, 0, false, false, EntityKind::EK_Gen);
inline Entity As = Entity(13, 0, false, false, EntityKind::EK_Gen);

// Always has to match the last internal entity
inline Entity GAIA_ID(LastCoreComponent) = IsA;
inline Entity GAIA_ID(LastCoreComponent) = As;

//----------------------------------------------------------------------
// Helper functions
Expand Down Expand Up @@ -17298,13 +17298,18 @@ namespace gaia {
//! Number of ticks before empty chunks are removed
static constexpr uint16_t MAX_ARCHETYPE_LIFESPAN = (1 << ARCHETYPE_LIFESPAN_BITS) - 1;

//! Remaining lifespan of the archetype
uint32_t m_lifespanCountdown: ARCHETYPE_LIFESPAN_BITS;
//! If set the archetype is to be deleted
uint32_t m_dead : 1;
//! Number of relationship pairs on the archetype
uint32_t m_pairCnt: Chunk::MAX_COMPONENTS_BITS;
//! Number of As relationship pairs on the archetype
uint32_t m_pairCnt_as: Chunk::MAX_COMPONENTS_BITS;

// Constructor is hidden. Create archetypes via Create
Archetype(const ComponentCache& cc, uint32_t& worldVersion):
m_cc(cc), m_worldVersion(worldVersion), m_lifespanCountdown(0), m_dead(0), m_pairCnt(0) {}
m_cc(cc), m_worldVersion(worldVersion), m_lifespanCountdown(0), m_dead(0), m_pairCnt(0), m_pairCnt_as(0) {}

//! Calulcates offsets in memory at which important chunk data is going to be stored.
//! These offsets are use to setup the chunk data area layout.
Expand Down Expand Up @@ -17477,9 +17482,14 @@ namespace gaia {

// Calculate the number of pairs
GAIA_EACH(ids) {
if (ids[i].pair()) {
++newArch->m_pairCnt;
}
if (!ids[i].pair())
continue;

++newArch->m_pairCnt;

// If it is a As relationship, count it separately as well
if (ids[i].id() == As.id())
++newArch->m_pairCnt_as;
}

// Find the index of the last generic component in both arrays
Expand Down Expand Up @@ -17785,6 +17795,10 @@ namespace gaia {
return m_pairCnt;
}

GAIA_NODISCARD uint32_t pairs_as() const {
return m_pairCnt_as;
}

/*!
Checks if an entity is a part of the archetype.
\param entity Entity
Expand Down Expand Up @@ -20272,15 +20286,15 @@ namespace gaia {
return *this;
}

//! Shortcut for add(Pair(IsA, entityBase)).
//! Shortcut for add(Pair(As, entityBase)).
//! Effectively makes an entity inherit from \param entityBase
EntityBuilder& derive(Entity entityBase) {
return add(Pair(IsA, entityBase));
EntityBuilder& as(Entity entityBase) {
return add(Pair(As, entityBase));
}

//! Check if \param entity inherits from \param entityBase
//! \return True if entity inherits from entityBase
GAIA_NODISCARD bool derive(Entity entity, Entity entityBase) const {
GAIA_NODISCARD bool as(Entity entity, Entity entityBase) const {
return static_cast<const World&>(m_world).is(entity, entityBase);
}

Expand Down Expand Up @@ -20401,7 +20415,7 @@ namespace gaia {
}

void try_set_AliasOf(Entity entity, bool enable) {
if (!entity.pair() || entity.id() != IsA.id())
if (!entity.pair() || entity.id() != As.id())
return;

updateFlag(m_entity, EntityContainerFlags::HasAliasOf, enable);
Expand Down Expand Up @@ -21684,7 +21698,7 @@ namespace gaia {

// Base entity is.
{
const auto& id = IsA;
const auto& id = As;
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<AliasOf_>(id);
GAIA_ASSERT(desc.entity == id);
Expand Down Expand Up @@ -21734,7 +21748,7 @@ namespace gaia {
.add(Acyclic)
.add(Pair(OnDelete, Error))
.add(Pair(OnDeleteTarget, Delete));
EntityBuilder(*this, IsA) //
EntityBuilder(*this, As) //
.add(Core)
.add(Acyclic)
.add(Pair(OnDelete, Error));
Expand Down Expand Up @@ -22090,21 +22104,26 @@ namespace gaia {

//----------------------------------------------------------------------

//! Shortcut for add(entity, Pair(IsA, entityBase)
void derive(Entity entity, Entity entityBase) {
add(entity, Pair(IsA, entityBase));
//! Shortcut for add(entity, Pair(As, entityBase)
void as(Entity entity, Entity entityBase) {
add(entity, Pair(As, entityBase));
}

//! Checks if \param entity inherits from \param entityBase.
//! True if entity is an is for entityBase. False otherwise.
GAIA_NODISCARD bool is(Entity entity, Entity entityBase) const {
const auto& ec = fetch(entity);
const auto& ids = ec.pArchetype->ids();
const auto* pArchetype = ec.pArchetype;

// Early exit if there are no As relationships on the archetype
if (pArchetype->pairs_as() == 0)
return false;

const auto& ids = ec.pArchetype->ids();
for (auto e: ids) {
if (!e.pair())
continue;
if (e.id() != IsA.id())
if (e.id() != As.id())
continue;

const auto& ecTarget = m_recs.entities[e.gen()];
Expand Down
12 changes: 7 additions & 5 deletions src/test/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1882,22 +1882,24 @@ TEST_CASE("DependsOn") {
REQUIRE(!w.has(rabbit, carrot));
}

TEST_CASE("Inheritance (IsA)") {
TEST_CASE("Inheritance (As)") {
ecs::World w;
ecs::Entity animal = w.add();
ecs::Entity herbivore = w.add();
ecs::Entity rabbit = w.add();
ecs::Entity hare = w.add();
ecs::Entity wolf = w.add();

w.derive(herbivore, animal); // w.add(herbivore, ecs::Pair(ecs::IsA, animal));
w.derive(rabbit, herbivore); // w.add(rabbit, ecs::Pair(ecs::IsA, herbivore));
w.derive(hare, herbivore); // w.add(hare, ecs::Pair(ecs::IsA, herbivore));
w.derive(wolf, animal); // w.add(wolf, ecs::Pair(ecs::IsA, animal))
w.as(herbivore, animal); // w.add(herbivore, ecs::Pair(ecs::As, animal));
w.as(rabbit, herbivore); // w.add(rabbit, ecs::Pair(ecs::As, herbivore));
w.as(hare, herbivore); // w.add(hare, ecs::Pair(ecs::As, herbivore));
w.as(wolf, animal); // w.add(wolf, ecs::Pair(ecs::As, animal))

REQUIRE(w.is(herbivore, animal));
REQUIRE(w.is(rabbit, herbivore));
REQUIRE(w.is(hare, herbivore));
REQUIRE(w.is(rabbit, animal));
REQUIRE(w.is(hare, animal));
REQUIRE(w.is(wolf, animal));

REQUIRE_FALSE(w.is(animal, herbivore));
Expand Down

0 comments on commit 52b79b9

Please sign in to comment.