diff options
| -rw-r--r-- | src/crepe/api/Script.h | 19 | ||||
| -rw-r--r-- | src/crepe/api/Script.hpp | 26 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.cpp | 11 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.h | 73 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.hpp | 81 | ||||
| -rw-r--r-- | src/test/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/test/ECSTest.cpp | 79 | ||||
| -rw-r--r-- | src/test/ScriptECSTest.cpp | 41 | ||||
| -rw-r--r-- | src/test/ScriptEventTest.cpp | 2 | ||||
| -rw-r--r-- | src/test/ScriptSceneTest.cpp | 2 | ||||
| -rw-r--r-- | src/test/ScriptTest.cpp | 2 | ||||
| -rw-r--r-- | src/test/ScriptTest.h | 2 | 
12 files changed, 307 insertions, 32 deletions
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 3417bb4..668e5d1 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -86,6 +86,25 @@ protected:  	RefVector<T> get_components() const;  	/** +	 * \copydoc ComponentManager::get_components_by_id +	 * \see ComponentManager::get_components_by_id +	 */ +	template <typename T> +	RefVector<T> get_components_by_id(game_object_id_t id) const; +	/** +	 * \copydoc ComponentManager::get_components_by_name +	 * \see ComponentManager::get_components_by_name +	 */ +	template <typename T> +	RefVector<T> get_components_by_name(const std::string & name) const; +	/** +	 * \copydoc ComponentManager::get_components_by_tag +	 * \see ComponentManager::get_components_by_tag +	 */ +	template <typename T> +	RefVector<T> get_components_by_tag(const std::string & tag) const; + +	/**  	 * \brief Log a message using Log::logf  	 *  	 * \tparam Args Log::logf parameters diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 23d69d9..225a51c 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,9 +20,7 @@ T & Script::get_component() const {  template <typename T>  RefVector<T> Script::get_components() const { -	ComponentManager & mgr = this->mediator->component_manager; - -	return mgr.get_components_by_id<T>(this->game_object_id); +	return this->get_components_by_id<T>(this->game_object_id);  }  template <typename... Args> @@ -54,4 +52,26 @@ void Script::subscribe(const EventHandler<EventType> & callback) {  	this->subscribe_internal(callback, EventManager::CHANNEL_ALL);  } +template <typename T> +RefVector<T> Script::get_components_by_id(game_object_id_t id) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_id<T>(id); +} +template <typename T> +RefVector<T> Script::get_components_by_name(const std::string & name) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_name<T>(name); +} +template <typename T> +RefVector<T> Script::get_components_by_tag(const std::string & tag) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_tag<T>(tag); +} +  } // namespace crepe diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 80cf8b4..df30d27 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,4 +1,5 @@  #include "../api/GameObject.h" +#include "../api/Metadata.h"  #include "../types.h"  #include "../util/Log.h" @@ -61,3 +62,13 @@ GameObject ComponentManager::new_object(const string & name, const string & tag,  void ComponentManager::set_persistent(game_object_id_t id, bool persistent) {  	this->persistent[id] = persistent;  } + +set<game_object_id_t> ComponentManager::get_objects_by_name(const string & name) const { +	return this->get_objects_by_predicate<Metadata>( +		[name](const Metadata & data) { return data.name == name; }); +} + +set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) const { +	return this->get_objects_by_predicate<Metadata>( +		[tag](const Metadata & data) { return data.tag == tag; }); +} diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 44429d9..19a8e81 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -1,6 +1,7 @@  #pragma once  #include <memory> +#include <set>  #include <typeindex>  #include <unordered_map>  #include <vector> @@ -134,8 +135,77 @@ public:  	 */  	template <typename T>  	RefVector<T> get_components_by_type() const; +	/** +	 * \brief Get all components of a specific type on a GameObject with name \c name +	 *  +	 * \tparam T The type of the component +	 * \param name Metadata::name for the same game_object_id as the returned components +	 * \return Components matching criteria +	 */ +	template <typename T> +	RefVector<T> get_components_by_name(const std::string & name) const; +	/** +	 * \brief Get all components of a specific type on a GameObject with tag \c tag +	 *  +	 * \tparam T The type of the component +	 * \param name Metadata::tag for the same game_object_id as the returned components +	 * \return Components matching criteria +	 */ +	template <typename T> +	RefVector<T> get_components_by_tag(const std::string & tag) const; + +private: +	/** +	 * \brief Get object IDs by predicate function +	 * +	 * This function calls the predicate function \c pred for all components matching type \c T, +	 * and adds their parent game_object_id to a \c std::set if the predicate returns true. +	 * +	 * \tparam T The type of the component to check the predicate against +	 * \param pred Predicate function +	 * +	 * \note The predicate function may be called for multiple components with the same \c +	 * game_object_id. In this case, the ID is added if *any* call returns \c true. +	 * +	 * \returns game_object_id for all components where the predicate returned true +	 */ +	template <typename T> +	std::set<game_object_id_t> +	get_objects_by_predicate(const std::function<bool(const T &)> & pred) const; + +	/** +	 * \brief Get components of type \c T for multiple game object IDs +	 * +	 * \tparam T The type of the components to return +	 * \param ids The object IDs +	 * +	 * \return All components matching type \c T and one of the IDs in \c ids +	 */ +	template <typename T> +	RefVector<T> get_components_by_ids(const std::set<game_object_id_t> & ids) const; + +	/** +	 * \brief Get object IDs for objects with name \c name +	 * +	 * \param name Object name to match +	 * \returns Object IDs where Metadata::name is equal to \c name +	 */ +	std::set<game_object_id_t> get_objects_by_name(const std::string & name) const; +	/** +	 * \brief Get object IDs for objects with tag \c tag +	 * +	 * \param tag Object tag to match +	 * \returns Object IDs where Metadata::tag is equal to \c tag +	 */ +	std::set<game_object_id_t> get_objects_by_tag(const std::string & tag) const;  private: +	//! By Component \c std::type_index (readability helper type) +	template <typename T> +	using by_type = std::unordered_map<std::type_index, T>; +	//! By \c game_object_id index (readability helper type) +	template <typename T> +	using by_id_index = std::vector<T>;  	/**  	 * \brief The components  	 * @@ -146,8 +216,7 @@ private:  	 * The first vector is for the ids of the GameObjects and the second vector is for the  	 * components (because a GameObject might have multiple components).  	 */ -	std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> -		components; +	by_type<by_id_index<std::vector<std::unique_ptr<Component>>>> components;  	//! Persistent flag for each GameObject  	std::unordered_map<game_object_id_t, bool> persistent; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index ffb38ec..9e70865 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -95,32 +95,25 @@ template <typename T>  RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {  	using namespace std; -	// Determine the type of T (this is used as the key of the unordered_map<>) -	type_index type = typeid(T); - -	// Create an empty vector<> -	RefVector<T> component_vector; - -	if (this->components.find(type) == this->components.end()) return component_vector; - -	// Get the correct vector<> -	const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type); - -	// Make sure that the id (that we are looking for) is within the boundaries of the vector<> -	if (id >= component_array.size()) return component_vector; - -	// Loop trough the whole vector<> -	for (const unique_ptr<Component> & component_ptr : component_array[id]) { -		// Cast the unique_ptr to a raw pointer -		T * casted_component = static_cast<T *>(component_ptr.get()); - -		if (casted_component == nullptr) continue; +	static_assert(is_base_of<Component, T>::value, +				  "get_components_by_id must recieve a derivative class of Component"); -		// Add the dereferenced raw pointer to the vector<> -		component_vector.push_back(*casted_component); +	type_index type = typeid(T); +	if (!this->components.contains(type)) return {}; + +	const by_id_index<vector<unique_ptr<Component>>> & components_by_id +		= this->components.at(type); +	if (id >= components_by_id.size()) return {}; + +	RefVector<T> out = {}; +	const vector<unique_ptr<Component>> & components = components_by_id.at(id); +	for (auto & component_ptr : components) { +		if (component_ptr == nullptr) continue; +		Component & component = *component_ptr.get(); +		out.push_back(static_cast<T &>(component));  	} -	return component_vector; +	return out;  }  template <typename T> @@ -158,4 +151,46 @@ RefVector<T> ComponentManager::get_components_by_type() const {  	return component_vector;  } +template <typename T> +std::set<game_object_id_t> +ComponentManager::get_objects_by_predicate(const std::function<bool(const T &)> & pred) const { +	using namespace std; + +	set<game_object_id_t> objects = {}; +	RefVector<T> components = this->get_components_by_type<T>(); + +	for (const T & component : components) { +		game_object_id_t id = dynamic_cast<const Component &>(component).game_object_id; +		if (objects.contains(id)) continue; +		if (!pred(component)) continue; +		objects.insert(id); +	} + +	return objects; +} + +template <typename T> +RefVector<T> +ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids) const { +	using namespace std; + +	RefVector<T> out = {}; +	for (game_object_id_t id : ids) { +		RefVector<T> components = get_components_by_id<T>(id); +		out.insert(out.end(), components.begin(), components.end()); +	} + +	return out; +} + +template <typename T> +RefVector<T> ComponentManager::get_components_by_name(const std::string & name) const { +	return this->get_components_by_ids<T>(this->get_objects_by_name(name)); +} + +template <typename T> +RefVector<T> ComponentManager::get_components_by_tag(const std::string & tag) const { +	return this->get_components_by_ids<T>(this->get_objects_by_tag(tag)); +} +  } // namespace crepe diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index f426a24..7196404 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -21,4 +21,5 @@ target_sources(test_main PUBLIC  	Profiling.cpp  	SaveManagerTest.cpp  	ScriptSaveManagerTest.cpp +	ScriptECSTest.cpp  ) diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index 3e6c61c..af2b7b0 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -1,6 +1,7 @@  #include <gtest/gtest.h>  #define protected public +#define private public  #include <crepe/api/GameObject.h>  #include <crepe/api/Metadata.h> @@ -16,6 +17,10 @@ class ECSTest : public ::testing::Test {  public:  	ComponentManager mgr{m}; + +	class TestComponent : public Component { +		using Component::Component; +	};  };  TEST_F(ECSTest, createGameObject) { @@ -387,3 +392,77 @@ TEST_F(ECSTest, resetPersistent) {  	EXPECT_EQ(metadata.size(), 0);  	EXPECT_EQ(transform.size(), 0);  } + +TEST_F(ECSTest, IDByName) { +	GameObject foo = mgr.new_object("foo"); +	GameObject bar = mgr.new_object("bar"); + +	{ +		auto objects = mgr.get_objects_by_name(""); +		EXPECT_EQ(objects.size(), 0); +	} + +	{ +		auto objects = mgr.get_objects_by_name("foo"); +		EXPECT_EQ(objects.size(), 1); +		EXPECT_TRUE(objects.contains(foo.id)); +	} +} + +TEST_F(ECSTest, IDByTag) { +	GameObject foo = mgr.new_object("foo", "common tag"); +	GameObject bar = mgr.new_object("bar", "common tag"); + +	{ +		auto objects = mgr.get_objects_by_tag(""); +		EXPECT_EQ(objects.size(), 0); +	} + +	{ +		auto objects = mgr.get_objects_by_tag("common tag"); +		EXPECT_EQ(objects.size(), 2); +		EXPECT_TRUE(objects.contains(foo.id)); +		EXPECT_TRUE(objects.contains(bar.id)); +	} +} + +TEST_F(ECSTest, ComponentsByName) { +	GameObject foo = mgr.new_object("foo"); +	foo.add_component<TestComponent>(); +	GameObject bar = mgr.new_object("bar"); +	bar.add_component<TestComponent>(); +	bar.add_component<TestComponent>(); + +	{ +		auto objects = mgr.get_components_by_name<TestComponent>(""); +		EXPECT_EQ(objects.size(), 0); +	} + +	{ +		auto objects = mgr.get_components_by_name<TestComponent>("foo"); +		EXPECT_EQ(objects.size(), 1); +	} + +	{ +		auto objects = mgr.get_components_by_name<TestComponent>("bar"); +		EXPECT_EQ(objects.size(), 2); +	} +} + +TEST_F(ECSTest, ComponentsByTag) { +	GameObject foo = mgr.new_object("foo", "common tag"); +	foo.add_component<TestComponent>(); +	GameObject bar = mgr.new_object("bar", "common tag"); +	bar.add_component<TestComponent>(); +	bar.add_component<TestComponent>(); + +	{ +		auto objects = mgr.get_components_by_tag<TestComponent>(""); +		EXPECT_EQ(objects.size(), 0); +	} + +	{ +		auto objects = mgr.get_components_by_tag<TestComponent>("common tag"); +		EXPECT_EQ(objects.size(), 3); +	} +} diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp new file mode 100644 index 0000000..1ec33ba --- /dev/null +++ b/src/test/ScriptECSTest.cpp @@ -0,0 +1,41 @@ +#include <gtest/gtest.h> + +#define protected public + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Metadata.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/system/ScriptSystem.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptECSTest : public ScriptTest { +public: +	class TestComponent : public Component { +		using Component::Component; +	}; +}; + +TEST_F(ScriptECSTest, GetOwnComponent) { +	MyScript & script = this->script; +	Metadata & metadata = script.get_component<Metadata>(); + +	EXPECT_EQ(metadata.name, OBJ_NAME); +} + +TEST_F(ScriptECSTest, GetOwnComponents) { +	const unsigned COUNT = 4; + +	for (unsigned i = 0; i < COUNT; i++) entity.add_component<TestComponent>(); + +	MyScript & script = this->script; +	RefVector<TestComponent> components = script.get_components<TestComponent>(); + +	EXPECT_EQ(components.size(), COUNT); +} diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index 5da31e7..c1b4028 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -26,7 +26,7 @@ public:  	class MyEvent : public Event {};  }; -TEST_F(ScriptEventTest, Inactive) { +TEST_F(ScriptEventTest, Default) {  	BehaviorScript & behaviorscript = this->behaviorscript;  	MyScript & script = this->script;  	EventManager & evmgr = this->event_manager; diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 9ee1e52..2568049 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -18,7 +18,7 @@ public:  	class MyScene : public Scene {};  }; -TEST_F(ScriptSceneTest, Inactive) { +TEST_F(ScriptSceneTest, Default) {  	BehaviorScript & behaviorscript = this->behaviorscript;  	MyScript & script = this->script; diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 1d2d6dd..acdae70 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -6,7 +6,6 @@  #define protected public  #include "ScriptTest.h" -#include <crepe/api/GameObject.h>  using namespace std;  using namespace crepe; @@ -14,7 +13,6 @@ using namespace testing;  void ScriptTest::SetUp() {  	auto & mgr = this->component_manager; -	GameObject entity = mgr.new_object("name");  	BehaviorScript & component = entity.add_component<BehaviorScript>();  	this->behaviorscript = component; diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 1bbfdd3..309e016 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -11,10 +11,12 @@  class ScriptTest : public testing::Test {  protected:  	crepe::Mediator mediator; +	static constexpr const char * OBJ_NAME = "foo";  public:  	crepe::ComponentManager component_manager{mediator};  	crepe::ScriptSystem system{mediator}; +	crepe::GameObject entity = component_manager.new_object(OBJ_NAME);  	class MyScript : public crepe::Script {  		// NOTE: explicitly stating `public:` is not required on actual scripts  |