Przeglądaj źródła

review: actor + tests

Michele Caini 8 lat temu
rodzic
commit
338eb75bab
5 zmienionych plików z 172 dodań i 144 usunięć
  1. 1 1
      TODO
  2. 81 24
      src/entt/entity/actor.hpp
  3. 28 76
      test/CMakeLists.txt
  4. 52 33
      test/entt/entity/actor.cpp
  5. 10 10
      test/entt/entity/registry.cpp

+ 1 - 1
TODO

@@ -4,8 +4,8 @@
 * debugging tools (#60): the issue online already contains interesting tips on this, look at it
 * define a macro for the noexcept policy, so as to provide users with an easy way to disable exception handling
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
+* define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
-* remove Actor::update (it's application dependent), allow tag instead
 * "singleton mode" for tags (see #66)
 * AOB

+ 81 - 24
src/entt/entity/actor.hpp

@@ -2,8 +2,10 @@
 #define ENTT_ENTITY_ACTOR_HPP
 
 
+#include <cassert>
 #include <utility>
 #include "registry.hpp"
+#include "utility.hpp"
 
 
 namespace entt {
@@ -16,28 +18,25 @@ namespace entt {
  * with entity-component systems and prefer to iterate objects directly.
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Delta Type to use to provide elapsed time.
  */
-template<typename Entity, typename Delta>
+template<typename Entity>
 struct Actor {
     /*! @brief Type of registry used internally. */
     using registry_type = Registry<Entity>;
     /*! @brief Underlying entity identifier. */
     using entity_type = Entity;
-    /*! @brief Type used to provide elapsed time. */
-    using delta_type = Delta;
 
     /**
      * @brief Constructs an actor by using the given registry.
      * @param reg An entity-component system properly initialized.
      */
     Actor(Registry<Entity> &reg)
-        : reg{reg}, entity{reg.create()}
+        : reg{reg}, entt{reg.create()}
     {}
 
     /*! @brief Default destructor. */
     virtual ~Actor() {
-        reg.destroy(entity);
+        reg.destroy(entt);
     }
 
     /*! @brief Default copy constructor. */
@@ -50,6 +49,24 @@ struct Actor {
     /*! @brief Default move assignment operator. @return This actor. */
     Actor & operator=(Actor &&) = default;
 
+    /**
+     * @brief Assigns the given tag to an actor.
+     *
+     * A new instance of the given tag is created and initialized with the
+     * arguments provided (the tag must have a proper constructor or be of
+     * aggregate type). Then the tag is removed from its previous owner (if any)
+     * and assigned to the actor.
+     *
+     * @tparam Tag Type of the tag to create.
+     * @tparam Args Types of arguments to use to construct the tag.
+     * @param args Parameters to use to initialize the tag.
+     * @return A reference to the newly created tag.
+     */
+    template<typename Tag, typename... Args>
+    Tag & assign(tag_type_t, Args &&... args) {
+        return (reg.template remove<Tag>(), reg.template assign<Tag>(tag_type_t{}, entt, std::forward<Args>(args)...));
+    }
+
     /**
      * @brief Assigns the given component to an actor.
      *
@@ -65,8 +82,18 @@ struct Actor {
      * @return A reference to the newly created component.
      */
     template<typename Component, typename... Args>
-    Component & set(Args &&... args) {
-        return reg.template accommodate<Component>(entity, std::forward<Args>(args)...);
+    Component & assign(Args &&... args) {
+        return reg.template accommodate<Component>(entt, std::forward<Args>(args)...);
+    }
+
+    /**
+     * @brief Removes the given tag from an actor.
+     * @tparam Tag Type of the tag to remove.
+     */
+    template<typename Tag>
+    void remove(tag_type_t) {
+        assert(has<Tag>(tag_type_t{}));
+        reg.template remove<Tag>();
     }
 
     /**
@@ -74,8 +101,18 @@ struct Actor {
      * @tparam Component Type of the component to remove.
      */
     template<typename Component>
-    void unset() {
-        reg.template remove<Component>(entity);
+    void remove() {
+        reg.template remove<Component>(entt);
+    }
+
+    /**
+     * @brief Checks if an actor owns the given tag.
+     * @tparam Tag Type of the tag for which to perform the check.
+     * @return True if the actor owns the tag, false otherwise.
+     */
+    template<typename Tag>
+    bool has(tag_type_t) const noexcept {
+        return (reg.template has<Tag>() && (reg.template attachee<Tag>() == entt));
     }
 
     /**
@@ -85,23 +122,44 @@ struct Actor {
      */
     template<typename Component>
     bool has() const noexcept {
-        return reg.template has<Component>(entity);
+        return reg.template has<Component>(entt);
+    }
+
+    /**
+     * @brief Returns a reference to the given tag for an actor.
+     * @tparam Tag Type of the tag to get.
+     * @return A reference to the instance of the tag owned by the actor.
+     */
+    template<typename Tag>
+    const Tag & get(tag_type_t) const noexcept {
+        assert(has<Tag>(tag_type_t{}));
+        return reg.template get<Tag>();
+    }
+
+    /**
+     * @brief Returns a reference to the given tag for an actor.
+     * @tparam Tag Type of the tag to get.
+     * @return A reference to the instance of the tag owned by the actor.
+     */
+    template<typename Tag>
+    Tag & get(tag_type_t) noexcept {
+        return const_cast<Tag &>(const_cast<const Actor *>(this)->get<Tag>(tag_type_t{}));
     }
 
     /**
      * @brief Returns a reference to the given component for an actor.
      * @tparam Component Type of the component to get.
-     * @return A reference to the instance of the component owned by the entity.
+     * @return A reference to the instance of the component owned by the actor.
      */
     template<typename Component>
     const Component & get() const noexcept {
-        return reg.template get<Component>(entity);
+        return reg.template get<Component>(entt);
     }
 
     /**
      * @brief Returns a reference to the given component for an actor.
      * @tparam Component Type of the component to get.
-     * @return A reference to the instance of the component owned by the entity.
+     * @return A reference to the instance of the component owned by the actor.
      */
     template<typename Component>
     Component & get() noexcept {
@@ -110,7 +168,7 @@ struct Actor {
 
     /**
      * @brief Returns a reference to the underlying registry.
-     * @return A reference to the underlying registry
+     * @return A reference to the underlying registry.
      */
     const registry_type & registry() const noexcept {
         return reg;
@@ -118,21 +176,23 @@ struct Actor {
 
     /**
      * @brief Returns a reference to the underlying registry.
-     * @return A reference to the underlying registry
+     * @return A reference to the underlying registry.
      */
     registry_type & registry() noexcept {
         return const_cast<registry_type &>(const_cast<const Actor *>(this)->registry());
     }
 
     /**
-     * @brief Updates an actor, whatever it means to update it.
-     * @param delta Elapsed time.
+     * @brief Returns the entity associated with an actor.
+     * @return The entity associated with the actor.
      */
-    virtual void update(delta_type delta) = 0;
+    entity_type entity() const noexcept {
+        return entt;
+    }
 
 private:
     registry_type &reg;
-    Entity entity;
+    Entity entt;
 };
 
 
@@ -141,11 +201,8 @@ private:
  *
  * The default actor is the best choice for almost all the applications.<br/>
  * Users should have a really good reason to choose something different.
- *
- * @tparam Delta Type to use to provide elapsed time.
  */
-template<typename Delta>
-using DefaultActor = Actor<DefaultRegistry::entity_type, Delta>;
+using DefaultActor = Actor<DefaultRegistry::entity_type>;
 
 
 }

+ 28 - 76
test/CMakeLists.txt

@@ -4,16 +4,16 @@
 
 add_library(odr OBJECT odr.cpp)
 
+macro(ADD_ENTT_TEST TEST_NAME TEST_SOURCE)
+    add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
+    target_link_libraries(${TEST_NAME} PRIVATE gtest_main Threads::Threads)
+    add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
+endmacro()
+
 # Test benchmark
 
 if(BUILD_BENCHMARK)
-    add_executable(
-        benchmark
-        $<TARGET_OBJECTS:odr>
-        benchmark/benchmark.cpp
-    )
-    target_link_libraries(benchmark PRIVATE gtest_main Threads::Threads)
-    add_test(NAME benchmark COMMAND benchmark)
+    ADD_ENTT_TEST(benchmark benchmark/benchmark.cpp)
 endif()
 
 # Test mod
@@ -25,15 +25,9 @@ if(BUILD_MOD)
     execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
     set(DUKTAPE_SRC_DIR ${DUKTAPE_DEPS_DIR}/src/src)
 
-    add_executable(
-        mod
-        $<TARGET_OBJECTS:odr>
-        ${DUKTAPE_SRC_DIR}/duktape.c
-        mod/mod.cpp
-    )
+    set(MOD_TEST_SOURCE ${DUKTAPE_SRC_DIR}/duktape.c mod/mod.cpp)
+    ADD_ENTT_TEST(mod ${MOD_TEST_SOURCE})
     target_include_directories(mod PRIVATE ${DUKTAPE_SRC_DIR})
-    target_link_libraries(mod PRIVATE gtest_main Threads::Threads m)
-    add_test(NAME mod COMMAND mod)
 endif()
 
 # Test snapshot
@@ -45,83 +39,41 @@ if(BUILD_SNAPSHOT)
     execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
     set(CEREAL_SRC_DIR ${CEREAL_DEPS_DIR}/src/include)
 
-    add_executable(
-        snapshot
-        $<TARGET_OBJECTS:odr>
-        snapshot/snapshot.cpp
-    )
-    target_include_directories(snapshot PRIVATE ${CEREAL_SRC_DIR})
-    target_link_libraries(snapshot PRIVATE gtest_main Threads::Threads)
-    add_test(NAME snapshot COMMAND snapshot)
+    ADD_ENTT_TEST(cereal snapshot/snapshot.cpp)
+    target_include_directories(cereal PRIVATE ${CEREAL_SRC_DIR})
 endif()
 
 # Test core
 
-add_executable(
-    core
-    $<TARGET_OBJECTS:odr>
-    entt/core/family.cpp
-    entt/core/hashed_string.cpp
-    entt/core/ident.cpp
-)
-target_link_libraries(core PRIVATE gtest_main Threads::Threads)
-add_test(NAME core COMMAND core)
+ADD_ENTT_TEST(family entt/core/family.cpp)
+ADD_ENTT_TEST(hashed_string entt/core/hashed_string.cpp)
+ADD_ENTT_TEST(ident entt/core/ident.cpp)
 
 # Test entity
 
-add_executable(
-    entity
-    $<TARGET_OBJECTS:odr>
-    entt/entity/actor.cpp
-    entt/entity/helper.cpp
-    entt/entity/registry.cpp
-    entt/entity/snapshot.cpp
-    entt/entity/sparse_set.cpp
-    entt/entity/view.cpp
-)
-target_link_libraries(entity PRIVATE gtest_main Threads::Threads)
-add_test(NAME entity COMMAND entity)
+ADD_ENTT_TEST(actor entt/entity/actor.cpp)
+ADD_ENTT_TEST(helper entt/entity/helper.cpp)
+ADD_ENTT_TEST(registry entt/entity/registry.cpp)
+ADD_ENTT_TEST(snapshot entt/entity/snapshot.cpp)
+ADD_ENTT_TEST(sparse_set entt/entity/sparse_set.cpp)
+ADD_ENTT_TEST(view entt/entity/view.cpp)
 
 # Test locator
 
-add_executable(
-    locator
-    $<TARGET_OBJECTS:odr>
-    entt/locator/locator.cpp
-)
-target_link_libraries(locator PRIVATE gtest_main Threads::Threads)
-add_test(NAME locator COMMAND locator)
+ADD_ENTT_TEST(locator entt/locator/locator.cpp)
 
 # Test process
 
-add_executable(
-    process
-    $<TARGET_OBJECTS:odr>
-    entt/process/process.cpp
-    entt/process/scheduler.cpp
-)
-target_link_libraries(process PRIVATE gtest_main Threads::Threads)
-add_test(NAME process COMMAND process)
+ADD_ENTT_TEST(process entt/process/process.cpp)
+ADD_ENTT_TEST(scheduler entt/process/scheduler.cpp)
 
 # Test resource
 
-add_executable(
-    resource
-    $<TARGET_OBJECTS:odr>
-    entt/resource/resource.cpp
-)
-target_link_libraries(resource PRIVATE gtest_main Threads::Threads)
-add_test(NAME resource COMMAND resource)
+ADD_ENTT_TEST(resource entt/resource/resource.cpp)
 
 # Test signal
 
-add_executable(
-    signal
-    $<TARGET_OBJECTS:odr>
-    entt/signal/delegate.cpp
-    entt/signal/dispatcher.cpp
-    entt/signal/emitter.cpp
-    entt/signal/sigh.cpp
-)
-target_link_libraries(signal PRIVATE gtest_main Threads::Threads)
-add_test(NAME signal COMMAND signal)
+ADD_ENTT_TEST(delegate entt/signal/delegate.cpp)
+ADD_ENTT_TEST(dispatcher entt/signal/dispatcher.cpp)
+ADD_ENTT_TEST(emitter entt/signal/emitter.cpp)
+ADD_ENTT_TEST(sigh entt/signal/sigh.cpp)

+ 52 - 33
test/entt/entity/actor.cpp

@@ -3,55 +3,74 @@
 #include <entt/entity/actor.hpp>
 #include <entt/entity/registry.hpp>
 
-struct TestActor: entt::DefaultActor<unsigned int> {
-    using entt::DefaultActor<unsigned int>::DefaultActor;
-    void update(unsigned int) {}
-};
+struct ActorComponent final {};
+struct ActorTag final {};
 
-struct ActorPosition final {};
-struct ActorVelocity final {};
-
-TEST(Actor, Functionalities) {
+TEST(Actor, Component) {
     entt::DefaultRegistry registry;
-    TestActor *actor = new TestActor{registry};
-    const auto &cactor = *actor;
+    entt::DefaultActor actor{registry};
+    const auto &cactor = actor;
 
-    ASSERT_EQ(&registry, &actor->registry());
+    ASSERT_EQ(&registry, &actor.registry());
     ASSERT_EQ(&registry, &cactor.registry());
-    ASSERT_TRUE(registry.empty<ActorPosition>());
-    ASSERT_TRUE(registry.empty<ActorVelocity>());
+    ASSERT_TRUE(registry.empty<ActorComponent>());
     ASSERT_FALSE(registry.empty());
-    ASSERT_FALSE(actor->has<ActorPosition>());
-    ASSERT_FALSE(actor->has<ActorVelocity>());
+    ASSERT_FALSE(actor.has<ActorComponent>());
 
-    const auto &position = actor->set<ActorPosition>();
+    const auto &component = actor.assign<ActorComponent>();
 
-    ASSERT_EQ(&position, &actor->get<ActorPosition>());
-    ASSERT_EQ(&position, &cactor.get<ActorPosition>());
-    ASSERT_FALSE(registry.empty<ActorPosition>());
-    ASSERT_TRUE(registry.empty<ActorVelocity>());
+    ASSERT_EQ(&component, &actor.get<ActorComponent>());
+    ASSERT_EQ(&component, &cactor.get<ActorComponent>());
+    ASSERT_FALSE(registry.empty<ActorComponent>());
     ASSERT_FALSE(registry.empty());
-    ASSERT_TRUE(actor->has<ActorPosition>());
-    ASSERT_FALSE(actor->has<ActorVelocity>());
+    ASSERT_TRUE(actor.has<ActorComponent>());
 
-    actor->unset<ActorPosition>();
+    actor.remove<ActorComponent>();
 
-    ASSERT_TRUE(registry.empty<ActorPosition>());
-    ASSERT_TRUE(registry.empty<ActorVelocity>());
+    ASSERT_TRUE(registry.empty<ActorComponent>());
     ASSERT_FALSE(registry.empty());
-    ASSERT_FALSE(actor->has<ActorPosition>());
-    ASSERT_FALSE(actor->has<ActorVelocity>());
+    ASSERT_FALSE(actor.has<ActorComponent>());
+}
+
+TEST(Actor, Tag) {
+    entt::DefaultRegistry registry;
+    entt::DefaultActor actor{registry};
+    const auto &cactor = actor;
 
-    actor->set<ActorPosition>();
-    actor->set<ActorVelocity>();
+    ASSERT_EQ(&registry, &actor.registry());
+    ASSERT_EQ(&registry, &cactor.registry());
+    ASSERT_FALSE(registry.has<ActorTag>());
+    ASSERT_FALSE(actor.has<ActorTag>(entt::tag_type_t{}));
+
+    const auto &tag = actor.assign<ActorTag>(entt::tag_type_t{});
 
+    ASSERT_EQ(&tag, &actor.get<ActorTag>(entt::tag_type_t{}));
+    ASSERT_EQ(&tag, &cactor.get<ActorTag>(entt::tag_type_t{}));
+    ASSERT_TRUE(registry.has<ActorTag>());
     ASSERT_FALSE(registry.empty());
-    ASSERT_FALSE(registry.empty<ActorPosition>());
-    ASSERT_FALSE(registry.empty<ActorVelocity>());
+    ASSERT_TRUE(actor.has<ActorTag>(entt::tag_type_t{}));
+
+    actor.remove<ActorTag>(entt::tag_type_t{});
+
+    ASSERT_FALSE(registry.has<ActorTag>());
+    ASSERT_FALSE(registry.empty());
+    ASSERT_FALSE(actor.has<ActorTag>(entt::tag_type_t{}));
+}
+
+TEST(Actor, EntityLifetime) {
+    entt::DefaultRegistry registry;
+    auto *actor = new entt::DefaultActor{registry};
+    actor->assign<ActorComponent>();
+
+    ASSERT_FALSE(registry.empty<ActorComponent>());
+    ASSERT_FALSE(registry.empty());
+
+    registry.each([actor](const auto entity) {
+        ASSERT_EQ(actor->entity(), entity);
+    });
 
     delete actor;
 
+    ASSERT_TRUE(registry.empty<ActorComponent>());
     ASSERT_TRUE(registry.empty());
-    ASSERT_TRUE(registry.empty<ActorPosition>());
-    ASSERT_TRUE(registry.empty<ActorVelocity>());
 }

+ 10 - 10
test/entt/entity/registry.cpp

@@ -21,6 +21,16 @@ struct ComponentListener {
     int counter{0};
 };
 
+TEST(DefaultRegistry, Types) {
+    entt::DefaultRegistry registry;
+
+    ASSERT_EQ(registry.type<int>(entt::tag_type_t{}), registry.type<int>(entt::tag_type_t{}));
+    ASSERT_EQ(registry.type<int>(), registry.type<int>());
+
+    ASSERT_NE(registry.type<int>(entt::tag_type_t{}), registry.type<double>(entt::tag_type_t{}));
+    ASSERT_NE(registry.type<int>(), registry.type<double>(entt::tag_type_t{}));
+}
+
 TEST(DefaultRegistry, Functionalities) {
     entt::DefaultRegistry registry;
 
@@ -269,16 +279,6 @@ TEST(DefaultRegistry, Orphans) {
     ASSERT_EQ(tot, 0u);
 }
 
-TEST(DefaultRegistry, Types) {
-    entt::DefaultRegistry registry;
-
-    ASSERT_EQ(registry.type<int>(entt::tag_type_t{}), registry.type<int>(entt::tag_type_t{}));
-    ASSERT_EQ(registry.type<int>(), registry.type<int>());
-
-    ASSERT_NE(registry.type<int>(entt::tag_type_t{}), registry.type<double>(entt::tag_type_t{}));
-    ASSERT_NE(registry.type<int>(), registry.type<double>(entt::tag_type_t{}));
-}
-
 TEST(DefaultRegistry, CreateDestroyEntities) {
     entt::DefaultRegistry registry;
     entt::DefaultRegistry::entity_type pre{}, post{};