diff options
Diffstat (limited to 'src')
30 files changed, 622 insertions, 26 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e63c1f9..9c508a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,9 +3,7 @@ cmake_minimum_required(VERSION 3.28) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) - set(CMAKE_BUILD_TYPE Debug) -add_compile_definitions(DEBUG) add_subdirectory(../lib/soloud soloud) add_subdirectory(../lib/googletest googletest) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index c7f18cd..a02e991 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -8,6 +8,14 @@ target_sources(crepe PUBLIC ParticleEmitter.cpp ParticleSystem.cpp SDLApp.cpp + ComponentManager.cpp + Component.cpp + GameObject.cpp + Collider.cpp + Rigidbody.cpp + Sprite.cpp + ScriptSystem.cpp + Script.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -18,6 +26,16 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES ParticleSystem.hpp Particle.hpp SDLApp.hpp + ComponentManager.h + ComponentManager.hpp + Component.h + GameObject.h + GameObject.hpp + Collider.h + Rigidbody.h + Sprite.h + System.h + ScriptSystem.h ) add_subdirectory(api) diff --git a/src/crepe/Collider.cpp b/src/crepe/Collider.cpp new file mode 100644 index 0000000..3f12afd --- /dev/null +++ b/src/crepe/Collider.cpp @@ -0,0 +1,5 @@ +#include "Collider.h" + +using namespace crepe; + +Collider::Collider(int size) : size(size) {} diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h new file mode 100644 index 0000000..120da05 --- /dev/null +++ b/src/crepe/Collider.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Component.h" + +namespace crepe { + +class Collider : public Component { +public: + Collider(int size); + + int size; +}; + +} // namespace crepe diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp new file mode 100644 index 0000000..d14159c --- /dev/null +++ b/src/crepe/Component.cpp @@ -0,0 +1,5 @@ +#include "Component.h" + +using namespace crepe; + +Component::Component() : active(true) {} diff --git a/src/crepe/Component.h b/src/crepe/Component.h new file mode 100644 index 0000000..16a4ce5 --- /dev/null +++ b/src/crepe/Component.h @@ -0,0 +1,14 @@ +#pragma once + +namespace crepe { + +class Component { +public: + Component(); + // TODO: shouldn't this constructor be deleted because this class will never + // directly be instantiated? + + bool active; +}; + +} // namespace crepe diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp new file mode 100644 index 0000000..9a3fec7 --- /dev/null +++ b/src/crepe/ComponentManager.cpp @@ -0,0 +1,24 @@ +#include "ComponentManager.h" + +using namespace crepe; + +ComponentManager & ComponentManager::get_instance() { + static ComponentManager instance; + return instance; +} + +void ComponentManager::delete_all_components_of_id(uint32_t id) { + // Loop through all the types (in the unordered_map<>) + for (auto & [type, componentArray] : components) { + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < componentArray.size()) { + // Clear the components at this specific id + componentArray[id].clear(); + } + } +} + +void ComponentManager::delete_all_components() { + // Clear the whole unordered_map<> + components.clear(); +} diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h new file mode 100644 index 0000000..2ab9dc8 --- /dev/null +++ b/src/crepe/ComponentManager.h @@ -0,0 +1,62 @@ +#pragma once + +#include <cstdint> +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "Component.h" + +namespace crepe { + +class ComponentManager { +public: + // Singleton + static ComponentManager & get_instance(); + ComponentManager(const ComponentManager &) = delete; + ComponentManager(ComponentManager &&) = delete; + ComponentManager & operator=(const ComponentManager &) = delete; + ComponentManager & operator=(ComponentManager &&) = delete; + +public: + //! Add a component of a specific type + template <typename T, typename... Args> + void add_component(uint32_t id, Args &&... args); + //! Deletes all components of a specific type and id + template <typename T> + void delete_components_by_id(uint32_t id); + //! Deletes all components of a specific type + template <typename T> + void delete_components(); + //! Deletes all components of a specific id + void delete_all_components_of_id(uint32_t id); + //! Deletes all components + void delete_all_components(); + + //! Get a vector<> of all components at specific type and id + template <typename T> + std::vector<std::reference_wrapper<T>> + get_components_by_id(uint32_t id) const; + //! Get a vector<> of all components of a specific type + template <typename T> + std::vector<std::reference_wrapper<T>> get_components_by_type() const; + +private: + ComponentManager() = default; + + /* + * The std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> below might seem a bit strange, let me explain this structure: + * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. + * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. + * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). + */ + std::unordered_map<std::type_index, + std::vector<std::vector<std::unique_ptr<Component>>>> + components; +}; + +} // namespace crepe + +#include "ComponentManager.hpp" diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp new file mode 100644 index 0000000..084cd33 --- /dev/null +++ b/src/crepe/ComponentManager.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include <type_traits> + +#include "ComponentManager.h" + +namespace crepe { + +template <class T, typename... Args> +void ComponentManager::add_component(uint32_t id, Args &&... args) { + using namespace std; + + static_assert(is_base_of<Component, T>::value, "add_component must recieve a derivative class of Component"); + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // Check if this component type is already in the unordered_map<> + if (components.find(type) == components.end()) { + //If not, create a new (empty) vector<> of vector<unique_ptr<Component>> + components[type] = vector<vector<unique_ptr<Component>>>(); + } + + // Resize the vector<> if the id is greater than the current size + if (id >= components[type].size()) { + // Initialize new slots to nullptr (resize does automatically init to nullptr) + components[type].resize(id + 1); + } + + // Create a new component of type T (arguments directly forwarded). The + // constructor must be called by ComponentManager. + T * instance = new T(forward<Args>(args)...); + // store its unique_ptr in the vector<> + components[type][id].push_back(unique_ptr<T>(instance)); +} + +template <typename T> +void ComponentManager::delete_components_by_id(uint32_t id) { + using namespace std; + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // Find the type (in the unordered_map<>) + if (components.find(type) != components.end()) { + // Get the correct vector<> + vector<vector<unique_ptr<Component>>> & component_array + = components[type]; + + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < component_array.size()) { + // Clear the whole vector<> of this specific type and id + component_array[id].clear(); + } + } +} + +template <typename T> +void ComponentManager::delete_components() { + // Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); + + // Find the type (in the unordered_map<>) + if (components.find(type) != components.end()) { + // Clear the whole vector<> of this specific type + components[type].clear(); + } +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_id(uint32_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<> + vector<reference_wrapper<T>> component_vector; + + // Find the type (in the unordered_map<>) + if (components.find(type) != components.end()) { + // Get the correct vector<> + const vector<vector<unique_ptr<Component>>> & component_array + = 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()) { + // 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()); + + // Ensure that the cast was successful + if (casted_component) { + // Add the dereferenced raw pointer to the vector<> + component_vector.push_back(*casted_component); + } + } + } + } + + // Return the vector<> + return component_vector; +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_type() 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<> + vector<reference_wrapper<T>> component_vector; + // Set the id to 0 (the id will also be stored in the returned vector<>) + // uint32_t id = 0; + + // Find the type (in the unordered_map<>) + if (components.find(type) != components.end()) { + + // Get the correct vector<> + const vector<vector<unique_ptr<Component>>> & component_array + = components.at(type); + + // Loop through the whole vector<> + for (const vector<unique_ptr<Component>> & component : + component_array) { + // Loop trough the whole vector<> + for (const unique_ptr<Component> & component_ptr : component) { + // Cast the unique_ptr to a raw pointer + T * casted_component = static_cast<T *>(component_ptr.get()); + + // Ensure that the cast was successful + if (casted_component) { + // Pair the dereferenced raw pointer and the id and add it to the vector<> + component_vector.emplace_back(ref(*casted_component)); + } + } + + // Increase the id (the id will also be stored in the returned vector<>) + //++id; + } + } + + // Return the vector<> + return component_vector; +} + +} // namespace crepe diff --git a/src/crepe/GameObject.cpp b/src/crepe/GameObject.cpp new file mode 100644 index 0000000..de3beb6 --- /dev/null +++ b/src/crepe/GameObject.cpp @@ -0,0 +1,7 @@ +#include "GameObject.h" + +using namespace crepe; + +GameObject::GameObject(uint32_t id, std::string name, std::string tag, + int layer) + : id(id), name(name), tag(tag), active(true), layer(layer) {} diff --git a/src/crepe/GameObject.h b/src/crepe/GameObject.h new file mode 100644 index 0000000..3588d9a --- /dev/null +++ b/src/crepe/GameObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include <cstdint> +#include <string> + +namespace crepe { + +class GameObject { +public: + GameObject(uint32_t id, std::string name, std::string tag, int layer); + + template <typename T, typename... Args> + void add_component(Args &&... args); + + uint32_t id; + std::string name; + std::string tag; + bool active; + int layer; +}; + +} // namespace crepe + +#include "GameObject.hpp" diff --git a/src/crepe/GameObject.hpp b/src/crepe/GameObject.hpp new file mode 100644 index 0000000..5966fbf --- /dev/null +++ b/src/crepe/GameObject.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "GameObject.h" + +#include "ComponentManager.h" + +namespace crepe { + +template <typename T, typename... Args> +void GameObject::add_component(Args &&... args) { + auto & mgr = ComponentManager::get_instance(); + mgr.add_component<T>(id, std::forward<Args>(args)...); +} + +} // namespace crepe diff --git a/src/crepe/Rigidbody.cpp b/src/crepe/Rigidbody.cpp new file mode 100644 index 0000000..495d908 --- /dev/null +++ b/src/crepe/Rigidbody.cpp @@ -0,0 +1,6 @@ +#include "Rigidbody.h" + +using namespace crepe; + +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) + : mass(mass), gravity_scale(gravityScale), body_type(bodyType) {} diff --git a/src/crepe/Rigidbody.h b/src/crepe/Rigidbody.h new file mode 100644 index 0000000..63a8877 --- /dev/null +++ b/src/crepe/Rigidbody.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Component.h" + +namespace crepe { + +class Rigidbody : public Component { +public: + Rigidbody(int mass, int gravityScale, int bodyType); + + int mass; + int gravity_scale; + int body_type; +}; + +} // namespace crepe diff --git a/src/crepe/Script.cpp b/src/crepe/Script.cpp new file mode 100644 index 0000000..42e3666 --- /dev/null +++ b/src/crepe/Script.cpp @@ -0,0 +1,7 @@ +#include "Script.h" + +using namespace crepe; + +void Script::init() { } +void Script::update() { } + diff --git a/src/crepe/Script.h b/src/crepe/Script.h new file mode 100644 index 0000000..ba4073a --- /dev/null +++ b/src/crepe/Script.h @@ -0,0 +1,16 @@ +#pragma once + +namespace crepe { + +class Script { +protected: + virtual void init(); + virtual void update(); + // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be + // implemented as member methods in derivative user script classes and + // registered in init(), otherwise this class will balloon in size with each + // added event. +}; + +} + diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp new file mode 100644 index 0000000..e301c71 --- /dev/null +++ b/src/crepe/ScriptSystem.cpp @@ -0,0 +1,22 @@ +#include "util/log.h" + +#include "ScriptSystem.h" + +using namespace crepe; + +ScriptSystem::ScriptSystem() { + dbg_trace(); +} +ScriptSystem::~ScriptSystem() { + dbg_trace(); +} + +ScriptSystem & ScriptSystem::get_instance() { + static ScriptSystem instance; + return instance; +} + +void ScriptSystem::update() { + dbg_trace(); +} + diff --git a/src/crepe/ScriptSystem.h b/src/crepe/ScriptSystem.h new file mode 100644 index 0000000..e1ed290 --- /dev/null +++ b/src/crepe/ScriptSystem.h @@ -0,0 +1,18 @@ +#pragma once + +#include "System.h" + +namespace crepe { + +class ScriptSystem : public System { +public: + static ScriptSystem & get_instance(); + virtual void update(); + +private: + ScriptSystem(); + ~ScriptSystem(); +}; + +} + diff --git a/src/crepe/Sprite.cpp b/src/crepe/Sprite.cpp new file mode 100644 index 0000000..a5a5e68 --- /dev/null +++ b/src/crepe/Sprite.cpp @@ -0,0 +1,8 @@ +#include <string> + +#include "Sprite.h" + +using namespace crepe; +using namespace std; + +Sprite::Sprite(string path) : path(path) {} diff --git a/src/crepe/Sprite.h b/src/crepe/Sprite.h new file mode 100644 index 0000000..143e702 --- /dev/null +++ b/src/crepe/Sprite.h @@ -0,0 +1,16 @@ +#pragma once + +#include <string> + +#include "Component.h" + +namespace crepe { + +class Sprite : public Component { +public: + Sprite(std::string path); + + std::string path; +}; + +} // namespace crepe diff --git a/src/crepe/System.h b/src/crepe/System.h new file mode 100644 index 0000000..3fe3d66 --- /dev/null +++ b/src/crepe/System.h @@ -0,0 +1,23 @@ +#pragma once + +namespace crepe { + +class System { +public: + static System & get_instance(); + virtual void update() = 0; + +protected: + System() { }; + virtual ~System() { }; + +private: + // singleton + System(const System &) = delete; + System(System &&) = delete; + System & operator=(const System &) = delete; + System & operator=(System &&) = delete; +}; + +} + diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp new file mode 100644 index 0000000..2dd933e --- /dev/null +++ b/src/crepe/api/BehaviorScript.cpp @@ -0,0 +1,10 @@ +#include "../util/log.h" + +#include "BehaviorScript.h" + +using namespace crepe::api; + +BehaviorScript::BehaviorScript() { + dbg_trace(); +} + diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h new file mode 100644 index 0000000..e9542c1 --- /dev/null +++ b/src/crepe/api/BehaviorScript.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../Script.h" +#include "../Component.h" + +namespace crepe::api { + +class BehaviorScript : public Script, public Component { + // only allow ComponentManager to instantiate scripts + friend class ComponentManager; + +protected: + BehaviorScript(); +}; + +} + diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 9548594..86623de 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,9 +1,10 @@ target_sources(crepe PUBLIC # AudioSource.cpp + BehaviorScript.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - AudioSource.h - Component.h + # AudioSource.h + BehaviorScript.h ) diff --git a/src/crepe/api/Component.h b/src/crepe/api/Component.h deleted file mode 100644 index d5e0499..0000000 --- a/src/crepe/api/Component.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace crepe::api { - -class Component { -public: - bool active; -}; - -} // namespace crepe::api diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp index 6829ec3..f91d52c 100644 --- a/src/crepe/util/log.cpp +++ b/src/crepe/util/log.cpp @@ -8,10 +8,10 @@ using namespace crepe::util; static const char * const LOG_PREFIX[] = { - [log_level::debug] = "[DBG] ", - [log_level::info] = "[INFO] ", - [log_level::warning] = "[WARN] ", - [log_level::error] = "[ERR] ", + [log_level::DEBUG] = "[DBG] ", + [log_level::INFO] = "[INFO] ", + [log_level::WARNING] = "[WARN] ", + [log_level::ERROR] = "[ERR] ", }; static void va_logf(enum log_level level, va_list args, const std::string fmt) { @@ -38,7 +38,7 @@ static void va_logf(enum log_level level, va_list args, const std::string fmt) { void crepe::util::logf(const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(crepe::util::log_level::debug, args, fmt); + va_logf(crepe::util::log_level::DEBUG, args, fmt); va_end(args); } diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index bfe7291..2b0fbe1 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -7,7 +7,7 @@ // utility macros #define _crepe_logf_here(fmt, ...) \ - crepe::util::logf(util::log_level::debug, "%s%s (%s:%d)" fmt "\n", \ + crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)" fmt "\n", \ crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ __VA_ARGS__) @@ -24,10 +24,10 @@ namespace crepe::util { enum log_level { - debug, - info, - warning, - error, + DEBUG, + INFO, + WARNING, + ERROR, }; void logf(const char * fmt, ...); diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 17e995b..f66e08a 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -1,5 +1,20 @@ -add_executable(audio_internal EXCLUDE_FROM_ALL audio_internal.cpp) -target_link_libraries(audio_internal PUBLIC crepe) +# add_example(target_name [SOURCES...]) +function(add_example target_name) + # if SOURCES is not specified + if(NOT ARGV1) + # A .cpp file with target_name exists, and should be used + set(sources ${target_name}.cpp) + else() + set(sources ${ARGV}) + endif() + + add_executable(${target_name} EXCLUDE_FROM_ALL ${sources}) + target_link_libraries(${target_name} PUBLIC crepe) +endfunction() + +add_example(audio_internal) +add_example(components_internal) +add_example(script) add_executable(particel EXCLUDE_FROM_ALL particel.cpp) target_link_libraries(particel PUBLIC crepe) diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp new file mode 100644 index 0000000..54ce295 --- /dev/null +++ b/src/example/components_internal.cpp @@ -0,0 +1,60 @@ +/** \file + * + * Standalone example for usage of the internal ECS + */ + +#include <cassert> +#include <chrono> + +#include <crepe/Collider.h> +#include <crepe/Component.h> +#include <crepe/ComponentManager.h> +#include <crepe/GameObject.h> +#include <crepe/Rigidbody.h> +#include <crepe/Sprite.h> +#include <crepe/util/log.h> + +using namespace crepe; +using namespace std; + +#define OBJ_COUNT 100000 + +int main() { + dbg_trace(); + + auto & mgr = ComponentManager::get_instance(); + + auto start_adding = chrono::high_resolution_clock::now(); + + GameObject * game_object[OBJ_COUNT]; + + for (int i = 0; i < OBJ_COUNT; ++i) { + game_object[i] = new GameObject(i, "Name", "Tag", 0); + + game_object[i]->add_component<Sprite>("test"); + game_object[i]->add_component<Rigidbody>(0, 0, i); + game_object[i]->add_component<Collider>(i); + } + + auto stop_adding = chrono::high_resolution_clock::now(); + + auto sprites = mgr.get_components_by_type<Sprite>(); + for (auto sprite : sprites) { + assert(sprite.get().path == "test"); + } + + auto stop_looping = chrono::high_resolution_clock::now(); + + for (int i = 0; i < OBJ_COUNT; ++i) { + delete game_object[i]; + } + + auto add_time = chrono::duration_cast<chrono::microseconds>(stop_adding + - start_adding); + auto loop_time = chrono::duration_cast<chrono::microseconds>(stop_looping + - stop_adding); + printf("add time: %ldus\n", add_time.count()); + printf("loop time: %ldus\n", loop_time.count()); + + return 0; +} diff --git a/src/example/script.cpp b/src/example/script.cpp new file mode 100644 index 0000000..28605c7 --- /dev/null +++ b/src/example/script.cpp @@ -0,0 +1,33 @@ +/** \file + * + * Standalone example for usage of the script component and system + */ + +#include <crepe/util/log.h> +#include <crepe/ScriptSystem.h> +#include <crepe/ComponentManager.h> +#include <crepe/GameObject.h> + +#include <crepe/api/BehaviorScript.h> + +using namespace crepe; +using namespace std; + +class MyScript : public api::BehaviorScript { + void update() { + dbg_trace(); + } +}; + +int main() { + dbg_trace(); + + auto obj = GameObject(0, "name", "tag", 0); + obj.add_component<MyScript>(); + + auto & sys = ScriptSystem::get_instance(); + sys.update(); // -> MyScript::update + + return 0; +} + |