Fixed docs, ids changed to const references

This commit is contained in:
Stephen Seo 2016-09-20 20:31:56 +09:00
parent 7c49ab4f04
commit 2e115cd7a2
2 changed files with 248 additions and 181 deletions

View file

@ -21,6 +21,18 @@
namespace EC namespace EC
{ {
/*!
\brief Manages an EntityComponent system.
EC::Manager must be created with a list of all used Components and all used tags.
Note that all components must have a default constructor.
Example:
\code{.cpp}
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
\endcode
*/
template <typename ComponentsList, typename TagsList> template <typename ComponentsList, typename TagsList>
struct Manager struct Manager
{ {
@ -45,6 +57,12 @@ namespace EC
std::size_t currentSize = 0; std::size_t currentSize = 0;
public: public:
/*!
\brief Initializes the manager with a default capacity.
The default capacity is set with macro EC_INIT_ENTITIES_SIZE,
and will grow by amounts of EC_GROW_SIZE_AMOUNT when needed.
*/
Manager() Manager()
{ {
resize(EC_INIT_ENTITIES_SIZE); resize(EC_INIT_ENTITIES_SIZE);
@ -72,6 +90,13 @@ namespace EC
} }
public: public:
/*!
\brief Adds an entity to the system, returning the ID of the entity.
WARNING: The ID of an entity may change after calls to cleanup().
Usage of entity IDs should be safe during initialization.
Otherwise, only use the ID given during usage of forMatchingSignature().
*/
std::size_t addEntity() std::size_t addEntity()
{ {
if(currentSize == currentCapacity) if(currentSize == currentCapacity)
@ -84,44 +109,120 @@ namespace EC
return currentSize++; return currentSize++;
} }
void deleteEntity(std::size_t index) /*!
\brief Marks an entity for deletion.
A deleted Entity is not actually deleted until cleanup() is called.
While an Entity is "deleted" but still in the system, calls to forMatchingSignature()
will ignore the Entity.
*/
void deleteEntity(const std::size_t& index)
{ {
std::get<bool>(entities.at(index)) = false; std::get<bool>(entities.at(index)) = false;
} }
bool hasEntity(std::size_t index) const
/*!
\brief Checks if the Entity with the given ID is in the system.
Note that deleted Entities that haven't yet been cleaned up (via cleanup()) are
considered still in the system.
*/
bool hasEntity(const std::size_t& index) const
{ {
return index < currentSize; return index < currentSize;
} }
bool isAlive(std::size_t index) const
/*!
\brief Checks if the Entity is not marked as deleted.
Note that invalid Entities (Entities where calls to hasEntity() returns false)
will return false.
*/
bool isAlive(const std::size_t& index) const
{ {
return hasEntity(index) && std::get<bool>(entities.at(index)); return hasEntity(index) && std::get<bool>(entities.at(index));
} }
const EntitiesTupleType& getEntityInfo(std::size_t index) const /*!
\brief Returns a const reference to an Entity's info.
An Entity's info is a std::tuple with a bool, std::size_t, and a bitset.
\n The bool determines if the Entity is alive.
\n The std::size_t is the ID to this Entity's data in the system.
\n The bitset shows what Components and Tags belong to the Entity.
*/
const EntitiesTupleType& getEntityInfo(const std::size_t& index) const
{ {
return entities.at(index); return entities.at(index);
} }
/*!
\brief Returns a reference to a component belonging to the given Entity.
This function will return a reference to a Component regardless of whether or
not the Entity actually owns the reference. If the Entity doesn't own the Component,
changes to the Component will not affect any Entity. It is recommended to use
hasComponent() to determine if the Entity actually owns that Component.
*/
template <typename Component> template <typename Component>
Component& getEntityData(std::size_t index) Component& getEntityData(const std::size_t& index)
{ {
return std::get<std::vector<Component> >(componentsStorage).at(std::get<std::size_t>(entities.at(index))); return std::get<std::vector<Component> >(componentsStorage).at(std::get<std::size_t>(entities.at(index)));
} }
/*!
\brief Returns a reference to a component belonging to the given Entity.
Note that this function is the same as getEntityData().
This function will return a reference to a Component regardless of whether or
not the Entity actually owns the reference. If the Entity doesn't own the Component,
changes to the Component will not affect any Entity. It is recommended to use
hasComponent() to determine if the Entity actually owns that Component.
*/
template <typename Component> template <typename Component>
bool hasComponent(std::size_t index) const Component& getEntityComponent(const std::size_t& index)
{
return getEntityData<Component>(index);
}
/*!
\brief Checks whether or not the given Entity has the given Component.
Example:
\code{.cpp}
manager.hasComponent<C0>(entityID);
\endcode
*/
template <typename Component>
bool hasComponent(const std::size_t& index) const
{ {
return std::get<BitsetType>(entities.at(index)).template getComponentBit<Component>(); return std::get<BitsetType>(entities.at(index)).template getComponentBit<Component>();
} }
/*!
\brief Checks whether or not the given Entity has the given Tag.
Example:
\code{.cpp}
manager.hasTag<T0>(entityID);
\endcode
*/
template <typename Tag> template <typename Tag>
bool hasTag(std::size_t index) const bool hasTag(const std::size_t& index) const
{ {
return std::get<BitsetType>(entities.at(index)).template getTagBit<Tag>(); return std::get<BitsetType>(entities.at(index)).template getTagBit<Tag>();
} }
/*!
\brief Does garbage collection on Entities.
Does housekeeping on the vector containing Entities that will result in
entity IDs changing if some Entities were marked for deletion.
<b>This function should be called periodically to correctly handle deletion of entities.</b>
*/
void cleanup() void cleanup()
{ {
if(currentSize == 0) if(currentSize == 0)
@ -168,8 +269,33 @@ namespace EC
currentSize = rhs + 1; currentSize = rhs + 1;
} }
/*!
\brief Adds a component to the given Entity.
Additional parameters given to this function will construct the Component with those
parameters.
Note that if the Entity already has the same component, then it will be overwritten
by the newly created Component with the given arguments.
Example:
\code{.cpp}
struct C0
{
// constructor is compatible as a default constructor
C0(int a = 0, char b = 'b') :
a(a), b(b)
{}
int a;
char b;
}
manager.addComponent<C0>(entityID, 10, 'd');
\endcode
*/
template <typename Component, typename... Args> template <typename Component, typename... Args>
void addComponent(std::size_t entityID, Args&&... args) void addComponent(const std::size_t& entityID, Args&&... args)
{ {
if(!hasEntity(entityID) || !isAlive(entityID)) if(!hasEntity(entityID) || !isAlive(entityID))
{ {
@ -182,8 +308,18 @@ namespace EC
std::get<std::vector<Component> >(componentsStorage)[std::get<std::size_t>(entities[entityID])] = std::move(component); std::get<std::vector<Component> >(componentsStorage)[std::get<std::size_t>(entities[entityID])] = std::move(component);
} }
/*!
\brief Removes the given Component from the given Entity.
If the Entity does not have the Component given, nothing will change.
Example:
\code{.cpp}
manager.removeComponent<C0>(entityID);
\endcode
*/
template <typename Component> template <typename Component>
void removeComponent(std::size_t entityID) void removeComponent(const std::size_t& entityID)
{ {
if(!hasEntity(entityID) || !isAlive(entityID)) if(!hasEntity(entityID) || !isAlive(entityID))
{ {
@ -193,8 +329,16 @@ namespace EC
std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = false; std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = false;
} }
/*!
\brief Adds the given Tag to the given Entity.
Example:
\code{.cpp}
manager.addTag<T0>(entityID);
\endcode
*/
template <typename Tag> template <typename Tag>
void addTag(std::size_t entityID) void addTag(const std::size_t& entityID)
{ {
if(!hasEntity(entityID) || !isAlive(entityID)) if(!hasEntity(entityID) || !isAlive(entityID))
{ {
@ -204,8 +348,18 @@ namespace EC
std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = true; std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = true;
} }
/*!
\brief Removes the given Tag from the given Entity.
If the Entity does not have the Tag given, nothing will change.
Example:
\code{.cpp}
manager.removeTag<T0>(entityID);
\endcode
*/
template <typename Tag> template <typename Tag>
void removeTag(std::size_t entityID) void removeTag(const std::size_t& entityID)
{ {
if(!hasEntity(entityID) || !isAlive(entityID)) if(!hasEntity(entityID) || !isAlive(entityID))
{ {
@ -220,7 +374,7 @@ namespace EC
struct ForMatchingSignatureHelper struct ForMatchingSignatureHelper
{ {
template <typename CType, typename Function> template <typename CType, typename Function>
static void call(std::size_t entityID, CType& ctype, Function&& function) static void call(const std::size_t& entityID, CType& ctype, Function&& function)
{ {
function( function(
entityID, entityID,
@ -229,13 +383,28 @@ namespace EC
} }
template <typename CType, typename Function> template <typename CType, typename Function>
void callInstance(std::size_t entityID, CType& ctype, Function&& function) const void callInstance(const std::size_t& entityID, CType& ctype, Function&& function) const
{ {
ForMatchingSignatureHelper<Types...>::call(entityID, ctype, std::forward<Function>(function)); ForMatchingSignatureHelper<Types...>::call(entityID, ctype, std::forward<Function>(function));
} }
}; };
public: public:
/*!
\brief Calls the given function on all Entities matching the given Signature.
The function object given to this function must accept std::size_t as its first
parameter and Component references for the rest of the parameters. Tags specified in the
Signature are only used as filters and will not be given as a parameter to the function.
Example:
\code{.cpp}
manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here
});
\endcode
Note, the ID given to the function is not permanent. An entity's ID may change when cleanup() is called.
*/
template <typename Signature, typename Function> template <typename Signature, typename Function>
void forMatchingSignature(Function&& function) void forMatchingSignature(Function&& function)
{ {
@ -261,6 +430,27 @@ namespace EC
std::vector<std::function<void()> > forMatchingFunctions; std::vector<std::function<void()> > forMatchingFunctions;
public: public:
/*!
\brief Stores a function in the manager to be called later.
As an alternative to calling functions directly with
forMatchingSignature(), functions can be stored in the manager to
be called later with callForMatchingFunctions() and removed with
clearForMatchingFunctions().
The syntax for the Function is the same as with forMatchingSignature().
Example:
\code{.cpp}
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here
});
manager.callForMatchingFunctions(); // call all stored functions
manager.clearForMatchingFunctions(); // remove all stored functions
\endcode
*/
template <typename Signature, typename Function> template <typename Signature, typename Function>
void addForMatchingFunction(Function&& function) void addForMatchingFunction(Function&& function)
{ {
@ -285,6 +475,20 @@ namespace EC
}); });
} }
/*!
\brief Call all stored functions.
Example:
\code{.cpp}
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here
});
manager.callForMatchingFunctions(); // call all stored functions
manager.clearForMatchingFunctions(); // remove all stored functions
\endcode
*/
void callForMatchingFunctions() void callForMatchingFunctions()
{ {
for(auto functionIter = forMatchingFunctions.begin(); functionIter != forMatchingFunctions.end(); ++functionIter) for(auto functionIter = forMatchingFunctions.begin(); functionIter != forMatchingFunctions.end(); ++functionIter)
@ -293,6 +497,20 @@ namespace EC
} }
} }
/*!
\brief Remove all stored functions.
Example:
\code{.cpp}
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here
});
manager.callForMatchingFunctions(); // call all stored functions
manager.clearForMatchingFunctions(); // remove all stored functions
\endcode
*/
void clearForMatchingFunctions() void clearForMatchingFunctions()
{ {
forMatchingFunctions.clear(); forMatchingFunctions.clear();
@ -303,170 +521,3 @@ namespace EC
#endif #endif
/*! \class EC::Manager
\brief Manages an EntityComponent system.
EC::Manager must be created with a list of all used Components and all used tags.
Note that all components must have a default constructor.
Example:
\code{.cpp}
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
\endcode
*/
/*! \fn EC::Manager::Manager()
\brief Initializes the manager with a default capacity.
The default capacity is set with macro EC_INIT_ENTITIES_SIZE,
and will grow by amounts of EC_GROW_SIZE_AMOUNT when needed.
*/
/*! \fn std::size_t EC::Manager::addEntity()
\brief Adds an entity to the system, returning the ID of the entity.
WARNING: The ID of an entity may change after calls to cleanup().
Usage of entity IDs should be safe during initialization.
Otherwise, only use the ID given during usage of forMatchingSignature().
*/
/*! \fn void EC::Manager::deleteEntity(std::size_t index)
\brief Marks an entity for deletion.
A deleted Entity is not actually deleted until cleanup() is called.
While an Entity is "deleted" but still in the system, calls to forMatchingSignature()
will ignore the Entity.
*/
/*! \fn bool EC::Manager::hasEntity(std::size_t index) const
\brief Checks if the Entity with the given ID is in the system.
Note that deleted Entities that haven't yet been cleaned up (via cleanup()) are
considered still in the system.
*/
/*! \fn bool EC::Manager::isAlive(std::size_t index) const
\brief Checks if the Entity is not marked as deleted.
Note that invalid Entities (Entities where calls to hasEntity() returns false)
will return false.
*/
/*! \fn const EntitiesTupleType& EC::Manager::getEntityInfo(std::size_t index) const
\brief Returns a const reference to an Entity's info.
An Entity's info is a std::tuple with a bool, std::size_t, and a bitset.
\n The bool determines if the Entity is alive.
\n The std::size_t is the ID to this Entity's data in the system.
\n The bitset shows what Components and Tags belong to the Entity.
*/
/*! \fn Component& EC::Manager::getEntityData(std::size_t index)
\brief Returns a reference to a component belonging to the given Entity.
This function will return a reference to a Component regardless of whether or
not the Entity actually owns the reference. If the Entity doesn't own the Component,
changes to the Component will not affect any Entity. It is recommended to use
hasComponent() to determine if the Entity actually owns that Component.
*/
/*! \fn bool EC::Manager::hasComponent(std::size_t index) const
\brief Checks whether or not the given Entity has the given Component.
Example:
\code{.cpp}
manager.hasComponent<C0>(entityID);
\endcode
*/
/*! \fn bool EC::Manager::hasTag(std::size_t index) const
\brief Checks whether or not the given Entity has the given Tag.
Example:
\code{.cpp}
manager.hasTag<T0>(entityID);
\endcode
*/
/*! \fn void EC::Manager::cleanup()
\brief Does garbage collection on Entities.
Does housekeeping on the vector containing Entities that will result in
entity IDs changing if some Entities were marked for deletion.
<b>This function should be called periodically to correctly handle deletion of entities.</b>
*/
/*! \fn void EC::Manager::addComponent(std::size_t entityID, Args&&... args)
\brief Adds a component to the given Entity.
Additional parameters given to this function will construct the Component with those
parameters.
Note that if the Entity already has the same component, then it will be overwritten
by the newly created Component with the given arguments.
Example:
\code{.cpp}
struct C0
{
// constructor is compatible as a default constructor
C0(int a = 0, char b = 'b') :
a(a), b(b)
{}
int a;
char b;
}
manager.addComponent<C0>(entityID, 10, 'd');
\endcode
*/
/*! \fn void EC::Manager::removeComponent(std::size_t entityID)
\brief Removes the given Component from the given Entity.
If the Entity does not have the Component given, nothing will change.
Example:
\code{.cpp}
manager.removeComponent<C0>(entityID);
\endcode
*/
/*! \fn void EC::Manager::addTag(std::size_t entityID)
\brief Adds the given Tag to the given Entity.
Example:
\code{.cpp}
manager.addTag<T0>(entityID);
\endcode
*/
/*! \fn void EC::Manager::removeTag(std::size_t entityID)
\brief Removes the given Tag from the given Entity.
If the Entity does not have the Tag given, nothing will change.
Example:
\code{.cpp}
manager.removeTag<T0>(entityID);
\endcode
*/
/*! \fn void EC::Manager::forMatchingSignature(Function&& function)
\brief Calls the given function on all Entities matching the given Signature.
The function object given to this function must accept std::size_t as its first
parameter and Component references for the rest of the parameters. Tags specified in the
Signature are only used as filters and will not be given as a parameter to the function.
Example:
\code{.cpp}
manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here.
});
\endcode
Note, the ID given to the function is not permanent. An entity's ID may change when cleanup() is called.
*/

View file

@ -247,12 +247,18 @@ TEST(EC, FunctionStorage)
EC::Manager<ListComponentsAll, ListTagsAll> manager; EC::Manager<ListComponentsAll, ListTagsAll> manager;
auto eid = manager.addEntity(); auto eid = manager.addEntity();
manager.addComponent<C0>(eid); manager.addComponent<C0>(eid);
manager.addComponent<C1>(eid);
manager.addForMatchingFunction<EC::Meta::TypeList<C0> >( [] (std::size_t eid, C0& c0) { manager.addForMatchingFunction<EC::Meta::TypeList<C0>>( [] (std::size_t eid, C0& c0) {
c0.x = 1; c0.x = 1;
c0.y = 2; c0.y = 2;
}); });
manager.addForMatchingFunction<EC::Meta::TypeList<C0, C1>>( [] (std::size_t eid, C0& c0, C1& c1) {
c1.vx = c0.x + 10;
c1.vy = c1.vx + c0.y + 10;
});
manager.callForMatchingFunctions(); manager.callForMatchingFunctions();
{ {
@ -260,6 +266,11 @@ TEST(EC, FunctionStorage)
EXPECT_EQ(c0.x, 1); EXPECT_EQ(c0.x, 1);
EXPECT_EQ(c0.y, 2); EXPECT_EQ(c0.y, 2);
auto c1 = manager.getEntityData<C1>(eid);
EXPECT_EQ(c1.vx, 11);
EXPECT_EQ(c1.vy, 23);
} }
manager.clearForMatchingFunctions(); manager.clearForMatchingFunctions();
@ -271,6 +282,11 @@ TEST(EC, FunctionStorage)
EXPECT_EQ(c0.x, 1); EXPECT_EQ(c0.x, 1);
EXPECT_EQ(c0.y, 2); EXPECT_EQ(c0.y, 2);
auto c1 = manager.getEntityData<C1>(eid);
EXPECT_EQ(c1.vx, 11);
EXPECT_EQ(c1.vy, 23);
} }
} }