project(EntityComponentSystem)
set(EntityComponentSystem_HEADERS
- EC/Meta/TypeList.hpp
EC/Meta/Combine.hpp
EC/Meta/Contains.hpp
EC/Meta/ContainsAll.hpp
+ EC/Meta/ForEach.hpp
EC/Meta/IndexOf.hpp
+ EC/Meta/Matching.hpp
EC/Meta/Morph.hpp
+ EC/Meta/TypeList.hpp
+ EC/Meta/TypeListGet.hpp
EC/Meta/Meta.hpp
EC/Bitset.hpp
EC/Manager.hpp
if(GTEST_FOUND)
set(UnitTests_SOURCES
test/MetaTest.cpp
+ test/ECTest.cpp
test/Main.cpp)
add_executable(UnitTests ${UnitTests_SOURCES})
#include "Meta/TypeList.hpp"
#include "Meta/Combine.hpp"
#include "Meta/IndexOf.hpp"
+#include "Meta/ForEach.hpp"
namespace EC
{
template <typename Contents>
static constexpr Bitset<ComponentsList, TagsList> generateBitset()
{
- //TODO
Bitset<ComponentsList, TagsList> bitset;
-/*
- for(unsigned int i = 0; i < Contents::size; ++i)
- {
- if(EC::Meta::Contains<EC::Meta::TypeListGet<Contents, i>, Combined>::value)
+
+ EC::Meta::forEach<Contents>([&bitset] (auto t) {
+ if(EC::Meta::Contains<decltype(t), 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;
}
#ifndef 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 <tuple>
+#include <utility>
#include "Meta/Combine.hpp"
#include "Bitset.hpp"
using Combined = EC::Meta::Combine<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()
{
- entities.resize(EC_INIT_ENTITIES_SIZE);
+ resize(EC_INIT_ENTITIES_SIZE);
+ }
+
+ private:
+ void resize(std::size_t newCapacity)
+ {
+ if(currentCapacity >= newCapacity)
+ {
+ return;
+ }
+
+ EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
+ std::get<std::vector<decltype(t)> >(this->componentsStorage).resize(newCapacity);
+ });
- for(auto entity : entities)
+ entities.resize(newCapacity);
+ for(std::size_t i = currentCapacity; i < newCapacity; ++i)
{
- entity->first = false;
+ entities[i] = std::make_tuple(false, i, BitsetType{});
}
+
+ currentCapacity = newCapacity;
}
- template <typename EComponentsList>
+ public:
std::size_t addEntity()
{
- //TODO
- BitsetType newEntity;
- return 0;
+ 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:
- using ComponentsStorage = EC::Meta::Morph<ComponentsList, std::tuple<> >;
- using EntitiesType = std::tuple<bool, BitsetType>;
+ 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<> >;
- std::vector<EntitiesType> entities;
+ 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));
+ }
+ }
+ }
};
}
--- /dev/null
+
+#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
+
#include "IndexOf.hpp"
#include "Morph.hpp"
#include "ForEach.hpp"
+#include "Matching.hpp"
--- /dev/null
+
+#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);
+}
+
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)
{
int size = ListComponentsAll::size;
using MorphedComponents = EC::Meta::Morph<ListComponentsAll, std::tuple<> >;
bool isSame = std::is_same<MorphedComponents, TupleAll>::value;
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)
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);
+ }
+}
+