Added Meta/Matching, progress on Bitset/Manager

This commit is contained in:
Stephen Seo 2016-03-13 18:07:49 +09:00
parent bee0c9d26d
commit f123f075eb
7 changed files with 382 additions and 27 deletions

View file

@ -2,12 +2,15 @@ cmake_minimum_required(VERSION 3.0)
project(EntityComponentSystem) project(EntityComponentSystem)
set(EntityComponentSystem_HEADERS set(EntityComponentSystem_HEADERS
EC/Meta/TypeList.hpp
EC/Meta/Combine.hpp EC/Meta/Combine.hpp
EC/Meta/Contains.hpp EC/Meta/Contains.hpp
EC/Meta/ContainsAll.hpp EC/Meta/ContainsAll.hpp
EC/Meta/ForEach.hpp
EC/Meta/IndexOf.hpp EC/Meta/IndexOf.hpp
EC/Meta/Matching.hpp
EC/Meta/Morph.hpp EC/Meta/Morph.hpp
EC/Meta/TypeList.hpp
EC/Meta/TypeListGet.hpp
EC/Meta/Meta.hpp EC/Meta/Meta.hpp
EC/Bitset.hpp EC/Bitset.hpp
EC/Manager.hpp EC/Manager.hpp
@ -53,6 +56,7 @@ find_package(GTest)
if(GTEST_FOUND) if(GTEST_FOUND)
set(UnitTests_SOURCES set(UnitTests_SOURCES
test/MetaTest.cpp test/MetaTest.cpp
test/ECTest.cpp
test/Main.cpp) test/Main.cpp)
add_executable(UnitTests ${UnitTests_SOURCES}) add_executable(UnitTests ${UnitTests_SOURCES})

View file

@ -6,6 +6,7 @@
#include "Meta/TypeList.hpp" #include "Meta/TypeList.hpp"
#include "Meta/Combine.hpp" #include "Meta/Combine.hpp"
#include "Meta/IndexOf.hpp" #include "Meta/IndexOf.hpp"
#include "Meta/ForEach.hpp"
namespace EC namespace EC
{ {
@ -30,17 +31,14 @@ namespace EC
template <typename Contents> template <typename Contents>
static constexpr Bitset<ComponentsList, TagsList> generateBitset() static constexpr Bitset<ComponentsList, TagsList> generateBitset()
{ {
//TODO
Bitset<ComponentsList, TagsList> bitset; Bitset<ComponentsList, TagsList> bitset;
/*
for(unsigned int i = 0; i < Contents::size; ++i) EC::Meta::forEach<Contents>([&bitset] (auto t) {
{ if(EC::Meta::Contains<decltype(t), Combined>::value)
if(EC::Meta::Contains<EC::Meta::TypeListGet<Contents, i>, Combined>::value)
{ {
bitset[EC::Meta::IndexOf<EC::Meta::TypeListGet<Contents, i>, Combined>::value] = true; bitset[EC::Meta::IndexOf<decltype(t), Combined>::value] = true;
} }
} });
*/
return bitset; return bitset;
} }

View file

@ -2,10 +2,12 @@
#ifndef EC_MANAGER_HPP #ifndef EC_MANAGER_HPP
#define EC_MANAGER_HPP #define EC_MANAGER_HPP
#define EC_INIT_ENTITIES_SIZE 1024 #define EC_INIT_ENTITIES_SIZE 256
#define EC_GROW_SIZE_AMOUNT 256
#include <cstddef> #include <cstddef>
#include <tuple> #include <tuple>
#include <utility>
#include "Meta/Combine.hpp" #include "Meta/Combine.hpp"
#include "Bitset.hpp" #include "Bitset.hpp"
@ -19,29 +21,189 @@ namespace EC
using Combined = EC::Meta::Combine<ComponentsList, TagsList>; using Combined = EC::Meta::Combine<ComponentsList, TagsList>;
using BitsetType = EC::Bitset<ComponentsList, TagsList>; using BitsetType = EC::Bitset<ComponentsList, TagsList>;
private:
template <typename... Types>
struct Storage
{
using type = std::tuple<std::vector<Types>... >;
};
using ComponentsStorage = typename EC::Meta::Morph<ComponentsList, Storage<> >::type;
// Entity: isAlive, dataIndex, ComponentsTags Info
using EntitiesTupleType = std::tuple<bool, std::size_t, BitsetType>;
using EntitiesType = std::vector<EntitiesTupleType>;
EntitiesType entities;
ComponentsStorage componentsStorage;
std::size_t currentCapacity = 0;
std::size_t currentSize = 0;
public:
Manager() Manager()
{ {
entities.resize(EC_INIT_ENTITIES_SIZE); resize(EC_INIT_ENTITIES_SIZE);
for(auto entity : entities)
{
entity->first = false;
}
}
template <typename EComponentsList>
std::size_t addEntity()
{
//TODO
BitsetType newEntity;
return 0;
} }
private: private:
using ComponentsStorage = EC::Meta::Morph<ComponentsList, std::tuple<> >; void resize(std::size_t newCapacity)
using EntitiesType = std::tuple<bool, BitsetType>; {
if(currentCapacity >= newCapacity)
{
return;
}
std::vector<EntitiesType> entities; EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
std::get<std::vector<decltype(t)> >(this->componentsStorage).resize(newCapacity);
});
entities.resize(newCapacity);
for(std::size_t i = currentCapacity; i < newCapacity; ++i)
{
entities[i] = std::make_tuple(false, i, BitsetType{});
}
currentCapacity = newCapacity;
}
public:
std::size_t addEntity()
{
if(currentSize == currentCapacity)
{
resize(currentCapacity + EC_GROW_SIZE_AMOUNT);
}
std::get<bool>(entities[currentSize]) = true;
return currentSize++;
}
void deleteEntity(std::size_t index)
{
std::get<bool>(entities.at(index)) = false;
}
bool hasEntity(std::size_t index) const
{
return index < currentSize;
}
const EntitiesTupleType& getEntityInfo(std::size_t index) const
{
return entities.at(index);
}
template <typename Component>
Component& getEntityData(std::size_t index)
{
return std::get<std::vector<Component> >(componentsStorage).at(std::get<std::size_t>(entities.at(index)));
}
void cleanup()
{
std::size_t rhs = currentSize - 1;
std::size_t lhs = 0;
while(lhs < rhs)
{
if(!std::get<bool>(entities[lhs]))
{
// lhs is marked for deletion
// swap lhs entity with rhs entity
std::swap(entities[lhs], entities[rhs]);
// inc/dec pointers
++lhs; --rhs;
}
else
{
++lhs;
}
}
currentSize = rhs + 1;
}
template <typename Component, typename... Args>
void addComponent(std::size_t entityID, Args&&... args)
{
if(!hasEntity(entityID))
{
return;
}
Component component(args...);
std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = true;
std::get<std::vector<Component> >(componentsStorage)[std::get<std::size_t>(entities[entityID])] = component;
}
template <typename Component>
void removeComponent(std::size_t entityID)
{
if(!hasEntity(entityID))
{
return;
}
std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = false;
}
template <typename Tag>
void addTag(std::size_t entityID)
{
if(!hasEntity(entityID))
{
return;
}
std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = true;
}
template <typename Tag>
void removeTag(std::size_t entityID)
{
if(!hasEntity(entityID))
{
return;
}
std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = false;
}
private:
template <typename... Types>
struct ForMatchingSignatureHelper
{
template <typename CType, typename Function>
static void call(std::size_t entityID, CType& ctype, Function&& function)
{
function(
entityID,
ctype.template getEntityData<Types>(entityID)...
);
}
};
public:
template <typename Signature, typename Function>
void forMatchingSignature(Function&& function)
{
using SignatureComponents = typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper = EC::Meta::Morph<SignatureComponents, ForMatchingSignatureHelper<> >;
BitsetType signatureBitset = BitsetType::template generateBitset<Signature>();
for(std::size_t i = 0; i < currentSize; ++i)
{
if(!std::get<bool>(entities[i]))
{
continue;
}
if((signatureBitset & std::get<BitsetType>(entities[i])) == signatureBitset)
{
Helper::call(i, *this, std::forward<Function>(function));
}
}
}
}; };
} }

54
src/EC/Meta/Matching.hpp Normal file
View file

@ -0,0 +1,54 @@
#ifndef EC_META_MATCHING_HPP
#define EC_META_MATCHING_HPP
#include "TypeList.hpp"
#include "Contains.hpp"
namespace EC
{
namespace Meta
{
template <typename TTypeListA, typename TTypeListB>
struct MatchingHelper
{
};
template <typename TTypeListA, typename TTypeListB, typename... Matching>
struct MatchingHelperHelper
{
};
template <typename TTypeListA, typename TTypeListB, typename... Matching>
struct MatchingHelperHelper<TTypeListA, TTypeListB, TypeList<Matching...> >
{
using type = TypeList<Matching...>;
};
template <template <typename...> class TTypeListA, typename TTypeListB, typename Type, typename... Types, typename... Matching>
struct MatchingHelperHelper<TTypeListA<Type, Types...>, TTypeListB, TypeList<Matching...> > :
std::conditional<
Contains<Type, TTypeListB>::value,
MatchingHelperHelper<TTypeListA<Types...>, TTypeListB, TypeList<Matching..., Type> >,
MatchingHelperHelper<TTypeListA<Types...>, TTypeListB, TypeList<Matching...> >
>::type
{
};
template <template <typename...> class TTypeListA, typename TTypeListB, typename Type, typename... Types>
struct MatchingHelper<TTypeListA<Type, Types...>, TTypeListB> :
std::conditional<
Contains<Type, TTypeListB>::value,
MatchingHelperHelper<TTypeListA<Types...>, TTypeListB, TypeList<Type> >,
MatchingHelper<TTypeListA<Types...>, TTypeListB>
>::type
{
};
template <typename TTypeListA, typename TTypeListB>
using Matching = MatchingHelper<TTypeListA, TTypeListB>;
}
}
#endif

View file

@ -7,4 +7,5 @@
#include "IndexOf.hpp" #include "IndexOf.hpp"
#include "Morph.hpp" #include "Morph.hpp"
#include "ForEach.hpp" #include "ForEach.hpp"
#include "Matching.hpp"

106
src/test/ECTest.cpp Normal file
View file

@ -0,0 +1,106 @@
#include <gtest/gtest.h>
#include <tuple>
#include <EC/Meta/Meta.hpp>
#include <EC/EC.hpp>
struct C0 {
int x, y;
};
struct C1 {
int vx, vy;
};
struct C2 {};
struct C3 {};
struct T0 {};
struct T1 {};
using ListComponentsAll = EC::Meta::TypeList<C0, C1, C2, C3>;
using ListComponentsSome = EC::Meta::TypeList<C1, C3>;
using ListTagsAll = EC::Meta::TypeList<T0, T1>;
using ListAll = EC::Meta::TypeList<C0, C1, C2, C3, T0, T1>;
using EmptyList = EC::Meta::TypeList<>;
using MixedList = EC::Meta::TypeList<C2, T1>;
TEST(EC, Bitset)
{
{
EC::Bitset<ListComponentsAll, EmptyList> bitset;
bitset[1] = true;
bitset[3] = true;
auto genBitset = EC::Bitset<ListComponentsAll, EmptyList>::generateBitset<ListComponentsSome>();
EXPECT_EQ(bitset, genBitset);
}
{
EC::Bitset<ListAll, EmptyList> bitset;
bitset[2] = true;
bitset[5] = true;
auto genBitset = EC::Bitset<ListAll, EmptyList>::generateBitset<MixedList>();
EXPECT_EQ(bitset, genBitset);
}
}
TEST(EC, Manager)
{
EC::Manager<ListComponentsAll, ListTagsAll> manager;
std::size_t e0 = manager.addEntity();
std::size_t e1 = manager.addEntity();
manager.addComponent<C0>(e0);
manager.addComponent<C1>(e0);
manager.addComponent<C0>(e1);
manager.addComponent<C1>(e1);
manager.addTag<T0>(e1);
{
auto& pos = manager.getEntityData<C0>(e0);
pos.x = 5;
pos.y = 5;
}
{
auto& vel = manager.getEntityData<C1>(e0);
vel.vx = 1;
vel.vy = 1;
}
auto posUpdate = [] (std::size_t id, C0& pos, C1& vel) {
pos.x += vel.vx;
pos.y += vel.vy;
};
auto updateTag = [] (std::size_t id, C0& pos, C1& vel) {
pos.x = pos.y = vel.vx = vel.vy = 0;
};
manager.forMatchingSignature<EC::Meta::TypeList<C0, C1> >(posUpdate);
manager.forMatchingSignature<EC::Meta::TypeList<C0, C1> >(posUpdate);
manager.forMatchingSignature<EC::Meta::TypeList<C0, C1, T0> >(updateTag);
{
auto& pos = manager.getEntityData<C0>(e0);
EXPECT_EQ(pos.x, 7);
EXPECT_EQ(pos.y, 7);
}
manager.deleteEntity(e0);
manager.cleanup();
std::size_t edata = std::get<std::size_t>(manager.getEntityInfo(0));
EXPECT_EQ(edata, 1);
}

View file

@ -20,6 +20,14 @@ using ListTagsAll = EC::Meta::TypeList<T0, T1>;
using ListAll = EC::Meta::TypeList<C0, C1, C2, C3, T0, T1>; using ListAll = EC::Meta::TypeList<C0, C1, C2, C3, T0, T1>;
using ListSome = EC::Meta::TypeList<C1, C3, T1>;
template <typename... STypes>
struct Storage
{
using type = std::tuple<std::vector<STypes>... >;
};
TEST(Meta, Contains) TEST(Meta, Contains)
{ {
int size = ListComponentsAll::size; int size = ListComponentsAll::size;
@ -157,6 +165,13 @@ TEST(Meta, Morph)
using MorphedComponents = EC::Meta::Morph<ListComponentsAll, std::tuple<> >; using MorphedComponents = EC::Meta::Morph<ListComponentsAll, std::tuple<> >;
bool isSame = std::is_same<MorphedComponents, TupleAll>::value; bool isSame = std::is_same<MorphedComponents, TupleAll>::value;
EXPECT_TRUE(isSame); EXPECT_TRUE(isSame);
using ComponentsStorage = EC::Meta::Morph<ListComponentsAll, Storage<> >;
isSame = std::is_same<ComponentsStorage::type,
std::tuple<std::vector<C0>, std::vector<C1>, std::vector<C2>, std::vector<C3> > >::value;
EXPECT_TRUE(isSame);
} }
TEST(Meta, TypeListGet) TEST(Meta, TypeListGet)
@ -199,3 +214,18 @@ TEST(Meta, ForEach)
EXPECT_FALSE(bitset[5]); EXPECT_FALSE(bitset[5]);
} }
TEST(Meta, Matching)
{
{
using Matched = EC::Meta::Matching<ListComponentsSome, ListComponentsAll>::type;
bool isSame = std::is_same<ListComponentsSome, Matched>::value;
EXPECT_TRUE(isSame);
}
{
using Matched = EC::Meta::Matching<ListSome, ListAll>::type;
bool isSame = std::is_same<ListSome, Matched>::value;
EXPECT_TRUE(isSame);
}
}