]> git.seodisparate.com - EntityComponentMetaSystem/commitdiff
Added Meta/Matching, progress on Bitset/Manager
authorStephen Seo <seo.disparate@gmail.com>
Sun, 13 Mar 2016 09:07:49 +0000 (18:07 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Sun, 13 Mar 2016 09:07:49 +0000 (18:07 +0900)
src/CMakeLists.txt
src/EC/Bitset.hpp
src/EC/Manager.hpp
src/EC/Meta/Matching.hpp [new file with mode: 0644]
src/EC/Meta/Meta.hpp
src/test/ECTest.cpp [new file with mode: 0644]
src/test/MetaTest.cpp

index 44f3270fa12849aa70890614801be40c28df5714..f1fff02e11faac11d43c80b7aaa5b5e8078f415f 100644 (file)
@@ -2,12 +2,15 @@ cmake_minimum_required(VERSION 3.0)
 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
@@ -53,6 +56,7 @@ find_package(GTest)
 if(GTEST_FOUND)
     set(UnitTests_SOURCES
         test/MetaTest.cpp
+        test/ECTest.cpp
         test/Main.cpp)
 
     add_executable(UnitTests ${UnitTests_SOURCES})
index 6e92e3233b46dc361f39a6f0dfa4e8ea6fd99875..1288cee0b6aa4cd15e4cda8e90f8ec54b1cc5f57 100644 (file)
@@ -6,6 +6,7 @@
 #include "Meta/TypeList.hpp"
 #include "Meta/Combine.hpp"
 #include "Meta/IndexOf.hpp"
+#include "Meta/ForEach.hpp"
 
 namespace EC
 {
@@ -30,17 +31,14 @@ 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;
         }
index 530c6c6f861d75863c865a0cf764b7aeb87b5841..534429e288a3e59711d6d25352eeff4d9d3ed855 100644 (file)
@@ -2,10 +2,12 @@
 #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"
@@ -19,29 +21,189 @@ namespace EC
         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));
+                }
+            }
+        }
 
     };
 }
diff --git a/src/EC/Meta/Matching.hpp b/src/EC/Meta/Matching.hpp
new file mode 100644 (file)
index 0000000..e2e5a78
--- /dev/null
@@ -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
+
index 8463a6fb3e4b1fbacda93ea5f5e5bbe9529e5eb5..5fac08226b4c10dc3c485ef584b297c30414f6df 100644 (file)
@@ -7,4 +7,5 @@
 #include "IndexOf.hpp"
 #include "Morph.hpp"
 #include "ForEach.hpp"
+#include "Matching.hpp"
 
diff --git a/src/test/ECTest.cpp b/src/test/ECTest.cpp
new file mode 100644 (file)
index 0000000..aa546b3
--- /dev/null
@@ -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);
+}
+
index b2c12e93033f073d142fb85c66ec26180eea8e50..0f34fe12eff2f5db79e2278c193815f4c99ecff2 100644 (file)
@@ -20,6 +20,14 @@ using ListTagsAll = EC::Meta::TypeList<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)
 {
     int size = ListComponentsAll::size;
@@ -157,6 +165,13 @@ TEST(Meta, Morph)
     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)
@@ -199,3 +214,18 @@ TEST(Meta, ForEach)
     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);
+    }
+}
+