Better ecs::entity_container documentation
This commit is contained in:
parent
3701df15f4
commit
6bab38a545
2 changed files with 183 additions and 90 deletions
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/detail/table.hpp>
|
||||
#include <psemek/util/assert.hpp>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
||||
struct entity_accessor
|
||||
{
|
||||
entity_accessor(detail::table * table, std::uint32_t row)
|
||||
: table_(table)
|
||||
, row_(row)
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
Component * get_if()
|
||||
{
|
||||
util::uuid const uuid = Component::uuid();
|
||||
|
||||
auto const component_uuids = table_->get_component_uuids();
|
||||
|
||||
for (std::size_t i = 0; i < component_uuids.size(); ++i)
|
||||
if (uuid == component_uuids[i])
|
||||
return reinterpret_cast<Component *>(table_->get_component_pointers()[i].data + detail::stride<Component>() * row_);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
Component & get()
|
||||
{
|
||||
if (auto ptr = get_if<Component>())
|
||||
return *ptr;
|
||||
assert(false);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
bool contains() const
|
||||
{
|
||||
return get_if<Component>() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::table * table_;
|
||||
std::uint32_t row_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -6,79 +6,183 @@
|
|||
#include <psemek/ecs/detail/query_cache_container.hpp>
|
||||
#include <psemek/ecs/detail/apply_helper.hpp>
|
||||
#include <psemek/ecs/detail/all_different_types.hpp>
|
||||
#include <psemek/ecs/entity_accessor.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/range.hpp>
|
||||
#include <psemek/util/exception.hpp>
|
||||
#include <psemek/util/type_name.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
||||
using query_cache = std::shared_ptr<detail::query_cache>;
|
||||
|
||||
struct component_exception
|
||||
: util::exception
|
||||
{
|
||||
component_exception(std::type_info const & type, entity_handle const & handle, boost::stacktrace::stacktrace stacktrace = {})
|
||||
: util::exception(util::to_string("Component ", util::type_name(type), " not found for entity ", handle), std::move(stacktrace))
|
||||
, type_(type)
|
||||
, handle_(handle)
|
||||
{}
|
||||
|
||||
std::type_info const & type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
entity_handle const & handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::type_info const & type_;
|
||||
entity_handle handle_;
|
||||
};
|
||||
|
||||
struct entity_accessor
|
||||
{
|
||||
entity_accessor() = default;
|
||||
entity_accessor(entity_accessor const &) = default;
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
/** Obtain a pointer to the specified component type
|
||||
* of the accessed entity, or a null pointer if
|
||||
* the entity doesn't contain this component type.
|
||||
*/
|
||||
template <typename Component>
|
||||
Component * get_if();
|
||||
|
||||
/** Obtain a reference to the specified component type
|
||||
* of the accessed entity.
|
||||
* If the entity doesn't contain this component type,
|
||||
* an exception of type `component_exception` is thrown.
|
||||
*/
|
||||
template <typename Component>
|
||||
Component & get();
|
||||
|
||||
/** Check if the entity contains this component type.
|
||||
*/
|
||||
template <typename Component>
|
||||
bool contains() const;
|
||||
|
||||
private:
|
||||
detail::table * table_ = nullptr;
|
||||
std::uint32_t row_ = 0;
|
||||
|
||||
friend struct entity_container;
|
||||
|
||||
entity_accessor(detail::table * table, std::uint32_t row);
|
||||
};
|
||||
|
||||
struct entity_container
|
||||
{
|
||||
// Create an entity with the specified components
|
||||
// NB: equivalent to create() followed by a
|
||||
// series of attach() calls, but faster
|
||||
// All components must be unique
|
||||
/** Create an entity with the specified components.
|
||||
* It is faster to create an entity with all the components at once, than to
|
||||
* create it with no components and `attach()` them one-by-one.
|
||||
* If any two of the passed component types are equal, the call fails with
|
||||
* a compilation error.
|
||||
* After the function returns, the new entity is considered alive (`alive(handle)` returns true).
|
||||
* Creating a new entity invalidates all previously created entity accessors.
|
||||
* If the entity is created during iteration (inside an `apply()` call), it is unspecified
|
||||
* whether the created entity will be visited by iteration or not.
|
||||
*/
|
||||
template <typename ... Components>
|
||||
entity_handle create(Components && ... components);
|
||||
|
||||
// Check if an entity handle refers to an active entity
|
||||
// UB if the handle wasn't obtained by a create() call
|
||||
/** Check if an entity handle refers to an alive entity (i.e. one that wasn't
|
||||
* destroyed yet.
|
||||
* Destroying an entity invalidates all previously created entity accessors.
|
||||
* If the handle wasn't previously obtained by a `create()` call, the
|
||||
* behavior is undefined.
|
||||
*/
|
||||
bool alive(entity_handle const & entity) const;
|
||||
|
||||
// Destroy an entity
|
||||
// UB if the handle wasn't obtained by a create() call
|
||||
// or if the entity was already destroyed
|
||||
/** Destroy an entity specified by a handle. After the call, `alive()`
|
||||
* returns false for this handle.
|
||||
* If the entity is destroyed during iteration (inside an `apply()` call), the iteration
|
||||
* is guaranteed not to visit the destroyed entity (unless it did so before the entity
|
||||
* was destroyed).
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
*/
|
||||
void destroy(entity_handle const & entity);
|
||||
|
||||
// Get an entity accessor for an entity
|
||||
// UB if the handle wasn't obtained by a create() call
|
||||
// or if the entity isn't active
|
||||
/** Get an entity accessor for an entity, which provides access to
|
||||
* the entitie's components.
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
* Creating or destroying entities invalidates all previously created
|
||||
* entity accessors.
|
||||
*/
|
||||
entity_accessor get(entity_handle const & entity);
|
||||
|
||||
// Attach new components to an existing entity
|
||||
// If the entity already has a component, its value
|
||||
// is replaced with a new one
|
||||
/** Attach new components to an existing entity, or update existing
|
||||
* components with new values.
|
||||
* If any two of the passed component types are equal, the call fails with
|
||||
* a compilation error.
|
||||
* Attaching components invalidates all previously created entity accessors.
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
*/
|
||||
// TODO: implement
|
||||
template <typename ... Components>
|
||||
void attach(entity_handle const & entity, Components && ... components);
|
||||
|
||||
// Remove components from an existing entity
|
||||
/** Detach (remove) components from an existing entity.
|
||||
* Detaching components invalidates all previously created entity accessors.
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
*/
|
||||
// TODO: implement
|
||||
template <typename ... Components>
|
||||
void detach(entity_handle const & entity);
|
||||
|
||||
// Create a query cache that can be used for the
|
||||
// apply() call to speed it up
|
||||
/** Create a query cache that can be used for speeding up
|
||||
* the `apply()` calls.
|
||||
*/
|
||||
template <typename ... Components>
|
||||
query_cache cache();
|
||||
|
||||
// Apply a function to all entities having the specified
|
||||
// components. The function signature is one of
|
||||
// void(components...)
|
||||
// void(entity_handle, components...)
|
||||
// void(entity_container, components...)
|
||||
// void(entity_container, entity_handle, components...)
|
||||
// The function can create or destroy any entities.
|
||||
// An optional query cache can be supplied to speed up the call
|
||||
// UB if the cache wasn't created with the exact same component sequence
|
||||
// UB if accessing the entitie's component after destroying it
|
||||
/** Apply a function to all entities having the specified components,
|
||||
* in unspecified order.
|
||||
* The function must have one of the following signatures:
|
||||
* void(components...)
|
||||
* void(entity_handle, components...)
|
||||
* void(entity_container, components...)
|
||||
* void(entity_container, entity_handle, components...)
|
||||
* The function can freely create or destroy entities. It is unspecified
|
||||
* whether the function will or will not visit newly created entities
|
||||
* during this `apply()` call. The function is guaranteed not to visit
|
||||
* destroyed entities (unless it did so before the entity was destroyed).
|
||||
* An optional query cache can be supplied to speed up iteration.
|
||||
* If the query cache wasn't created with the exact sequence of component
|
||||
* types, the behavior is undefined.
|
||||
* If the function accesses passed components after destroying the
|
||||
* currently visited entity, the behavior is undefined.
|
||||
* If the function recursively calls `apply()`, the behavior is undefined.
|
||||
*/
|
||||
template <typename ... Components, typename Function>
|
||||
void apply(Function && function, query_cache cache = {});
|
||||
|
||||
// Apply a function to all entities having the specified
|
||||
// components. Instead of applying the function to each entity,
|
||||
// the function is applied in "batch" mode, i.e. to array views
|
||||
// of components. The function signature is one of
|
||||
// void(span<components>...)
|
||||
// void(span<entity_handle const>, span<components>...)
|
||||
// void(entity_container, span<components>...)
|
||||
// void(entity_container, span<entity_handle const>, span<components>...)
|
||||
// An optional query cache can be supplied to speed up the call
|
||||
// UB if the cache wasn't created with the exact same component sequence
|
||||
// UB if the function tries to create or destroy entities
|
||||
/** Apply a function to all entities having the specified components,
|
||||
* in unspecified order. Instead of applying the function to each
|
||||
* entity separately, it is applied in "batch" mode, i.e. to array
|
||||
* views of respective components.
|
||||
* The function must have one of the following signatures:
|
||||
* void(span<components>...)
|
||||
* void(span<entity_handle const>, span<components>...)
|
||||
* void(entity_container, span<components>...)
|
||||
* void(entity_container, span<entity_handle const>, span<components>...)
|
||||
* An optional query cache can be supplied to speed up iteration.
|
||||
* If the query cache wasn't created with the exact sequence of component
|
||||
* types, the behavior is undefined.
|
||||
* If the function creates or destroyes entities, the behavior is undefined.
|
||||
*/
|
||||
template <typename ... Components, typename Function>
|
||||
void batch_apply(Function && function, query_cache cache = {});
|
||||
|
||||
|
|
@ -195,4 +299,43 @@ namespace psemek::ecs
|
|||
}
|
||||
}
|
||||
|
||||
inline entity_accessor::operator bool() const
|
||||
{
|
||||
return table_ != nullptr;
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
Component * entity_accessor::get_if()
|
||||
{
|
||||
util::uuid const uuid = Component::uuid();
|
||||
|
||||
auto const component_uuids = table_->get_component_uuids();
|
||||
|
||||
for (std::size_t i = 0; i < component_uuids.size(); ++i)
|
||||
if (uuid == component_uuids[i])
|
||||
return reinterpret_cast<Component *>(table_->get_component_pointers()[i].data + detail::stride<Component>() * row_);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
Component & entity_accessor::get()
|
||||
{
|
||||
if (auto ptr = get_if<Component>())
|
||||
return *ptr;
|
||||
assert(false);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
bool entity_accessor::contains() const
|
||||
{
|
||||
return get_if<Component>() != nullptr;
|
||||
}
|
||||
|
||||
inline entity_accessor::entity_accessor(detail::table * table, std::uint32_t row)
|
||||
: table_(table)
|
||||
, row_(row)
|
||||
{}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue