diff options
author | max-001 <maxsmits21@kpnmail.nl> | 2024-10-16 13:09:28 +0200 |
---|---|---|
committer | max-001 <maxsmits21@kpnmail.nl> | 2024-10-16 13:09:28 +0200 |
commit | 85514636cbf9ae34afc8d6c863e9760f291e6478 (patch) | |
tree | 4e745d504a0c55386aa20f079dfdaa0d32ac31aa /mwe | |
parent | 809db83cd515c6c2b1d404811354208cf97a5c07 (diff) | |
parent | 579824011d5e8776e2079d6624a39535517760ff (diff) |
Merge remote-tracking branch 'origin/master' into max/POC-ECS-memory-efficient
Diffstat (limited to 'mwe')
-rw-r--r-- | mwe/audio/soloud/main.cpp | 3 | ||||
-rw-r--r-- | mwe/dynlink/exec/CMakeLists.txt | 10 | ||||
-rw-r--r-- | mwe/dynlink/exec/main.c | 2 | ||||
-rw-r--r-- | mwe/dynlink/lib/CMakeLists.txt | 16 | ||||
-rw-r--r-- | mwe/dynlink/lib/test.c (renamed from mwe/dynlink/lib/lib.c) | 0 | ||||
-rw-r--r-- | mwe/dynlink/lib/test.h (renamed from mwe/dynlink/lib/lib.h) | 0 | ||||
-rw-r--r-- | mwe/ecs-homemade/inc/ComponentManager.h | 55 | ||||
-rw-r--r-- | mwe/ecs-homemade/inc/ComponentManager.hpp | 127 | ||||
-rw-r--r-- | mwe/ecs-homemade/inc/GameObjectMax.hpp | 7 | ||||
-rw-r--r-- | mwe/ecs-homemade/src/ComponentManager.cpp | 23 | ||||
-rw-r--r-- | mwe/ecs-homemade/src/main.cpp | 59 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/Components.cpp | 3 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/GameObjectMax.cpp | 4 | ||||
-rw-r--r-- | mwe/gameloop/include/gameObject.h | 1 | ||||
-rw-r--r-- | mwe/gameloop/include/timer.h | 2 | ||||
-rw-r--r-- | mwe/gameloop/src/loopManager.cpp | 34 | ||||
-rw-r--r-- | mwe/gameloop/src/main.cpp | 3 | ||||
-rw-r--r-- | mwe/gameloop/src/timer.cpp | 13 |
18 files changed, 336 insertions, 26 deletions
diff --git a/mwe/audio/soloud/main.cpp b/mwe/audio/soloud/main.cpp index 25ba003..50df0b7 100644 --- a/mwe/audio/soloud/main.cpp +++ b/mwe/audio/soloud/main.cpp @@ -38,8 +38,7 @@ int main() { this_thread::sleep_for(500ms); // play all samples simultaniously - for (unsigned i = 0; i < 3; i++) - soloud.play(sfx[i]); + for (unsigned i = 0; i < 3; i++) soloud.play(sfx[i]); this_thread::sleep_for(1000ms); // stop all audio and exit diff --git a/mwe/dynlink/exec/CMakeLists.txt b/mwe/dynlink/exec/CMakeLists.txt index 5335f0f..465b45e 100644 --- a/mwe/dynlink/exec/CMakeLists.txt +++ b/mwe/dynlink/exec/CMakeLists.txt @@ -5,12 +5,20 @@ set(CMAKE_CXX_STANDARD 20) project(main C CXX) +add_executable(main main.c) + # Since we have the source code for the test library, we might as well let # CMake use it so it automatically picks up the interface headers and compiles # the library for us. The same can be achieved manually, but is more code. add_subdirectory(../lib test) -add_executable(main main.c) +# include(ExternalProject) +# ExternalProject_Add(test_ext +# SOURCE_DIR ../../lib +# CMAKE_ARGS -DCMAKE_INSTALL_PREFIX="${CMAKE_BINARY_DIR}/ext" +# ) +# add_dependencies(main test_ext) +# find_package(test REQUIRED) # Make sure ld.so (linux) looks in the same folder as the final executable for # the .so dependency diff --git a/mwe/dynlink/exec/main.c b/mwe/dynlink/exec/main.c index 2bbdc20..ff884f2 100644 --- a/mwe/dynlink/exec/main.c +++ b/mwe/dynlink/exec/main.c @@ -1,4 +1,4 @@ -#include <lib.h> +#include <test.h> int main() { library_function(); diff --git a/mwe/dynlink/lib/CMakeLists.txt b/mwe/dynlink/lib/CMakeLists.txt index 2d15353..7efc91a 100644 --- a/mwe/dynlink/lib/CMakeLists.txt +++ b/mwe/dynlink/lib/CMakeLists.txt @@ -5,6 +5,20 @@ set(CMAKE_CXX_STANDARD 20) project(lib C CXX) -add_library(test SHARED lib.c) +add_library(test SHARED) + target_include_directories(test SYSTEM INTERFACE .) +target_sources(test PUBLIC + test.c +) + +target_sources(test PUBLIC FILE_SET HEADERS FILES + test.h +) + +install( + TARGETS test + FILE_SET HEADERS DESTINATION include +) + diff --git a/mwe/dynlink/lib/lib.c b/mwe/dynlink/lib/test.c index c7a78e4..c7a78e4 100644 --- a/mwe/dynlink/lib/lib.c +++ b/mwe/dynlink/lib/test.c diff --git a/mwe/dynlink/lib/lib.h b/mwe/dynlink/lib/test.h index 093eadb..093eadb 100644 --- a/mwe/dynlink/lib/lib.h +++ b/mwe/dynlink/lib/test.h diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h new file mode 100644 index 0000000..d368516 --- /dev/null +++ b/mwe/ecs-homemade/inc/ComponentManager.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Components.h" +#include <cstdint> +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <utility> +#include <vector> + +class ComponentManager { +public: + static ComponentManager & GetInstance(); //Singleton + + ComponentManager(const ComponentManager &) = delete; //Singleton + ComponentManager(ComponentManager &&) = delete; //Singleton + ComponentManager & operator=(const ComponentManager &) = delete; //Singleton + ComponentManager & operator=(ComponentManager &&) = delete; //Singleton + + template <typename T, typename... Args> + void AddComponent(std::uint32_t id, + Args &&... args); //Add a component of a specific type + template <typename T> + void DeleteComponentsById( + std::uint32_t id); //Deletes all components of a specific type and id + template <typename T> + void DeleteComponents(); //Deletes all components of a specific type + void DeleteAllComponentsOfId( + std::uint32_t id); //Deletes all components of a specific id + void DeleteAllComponents(); //Deletes all components + + template <typename T> + std::vector<std::reference_wrapper<T>> GetComponentsByID(std::uint32_t id) + const; //Get a vector<> of all components at specific type and id + template <typename T> + std::vector<std::reference_wrapper<T>> GetComponentsByType() + const; //Get a vector<> of all components of a specific type + +private: + static ComponentManager mInstance; //Singleton + + ComponentManager(); //Singleton + + /* + * 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>>>> + mComponents; +}; + +#include "ComponentManager.hpp" diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp new file mode 100644 index 0000000..d6da8e8 --- /dev/null +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -0,0 +1,127 @@ + +template <typename T, typename... Args> +void ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) + == mComponents + .end()) { //Check if this component type is already in the unordered_map<> + mComponents[type] = std::vector<std::vector<std::unique_ptr< + Component>>>(); //If not, create a new (empty) vector<> of vector<unique_ptr<Component>> + } + + if (id + >= mComponents[type] + .size()) { //Resize the vector<> if the id is greater than the current size + mComponents[type].resize( + id + + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) + } + + mComponents[type][id].push_back(std::make_unique<T>(std::forward<Args>( + args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> +} + +template <typename T> +void ComponentManager::DeleteComponentsById(std::uint32_t id) { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + std::vector<std::vector<std::unique_ptr<Component>>> & componentArray + = mComponents[type]; //Get the correct vector<> + + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id] + .clear(); //Clear the whole vector<> of this specific type and id + } + } +} + +template <typename T> +void ComponentManager::DeleteComponents() { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + mComponents[type] + .clear(); //Clear the whole vector<> of this specific type + } +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::GetComponentsByID(std::uint32_t id) const { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + std::vector<std::reference_wrapper<T>> + componentVector; //Create an empty vector<> + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + + const std::vector<std::vector<std::unique_ptr<Component>>> & + componentArray + = mComponents.at(type); //Get the correct vector<> + + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + for (const std::unique_ptr<Component> & componentPtr : + componentArray[id]) { //Loop trough the whole vector<> + T * castedComponent = static_cast<T *>( + componentPtr.get()); //Cast the unique_ptr to a raw pointer + + if (castedComponent) { //Ensure that the cast was successful + componentVector.push_back( + *castedComponent); //Add the dereferenced raw pointer to the vector<> + } + } + } + } + + return componentVector; //Return the vector<> +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::GetComponentsByType() const { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + std::vector<std::reference_wrapper<T>> + componentVector; //Create an empty vector<> + //std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + + const std::vector<std::vector<std::unique_ptr<Component>>> & + componentArray + = mComponents.at(type); //Get the correct vector<> + + for (const std::vector<std::unique_ptr<Component>> & component : + componentArray) { //Loop through the whole vector<> + for (const std::unique_ptr<Component> & componentPtr : + component) { //Loop trough the whole vector<> + T * castedComponent = static_cast<T *>( + componentPtr.get()); //Cast the unique_ptr to a raw pointer + + if (castedComponent) { //Ensure that the cast was successful + componentVector.emplace_back(std::ref( + *castedComponent)); //Pair the dereferenced raw pointer and the id and add it to the vector<> + } + } + + //++id; //Increase the id (the id will also be stored in the returned vector<>) + } + } + + return componentVector; //Return the vector<> +} diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp new file mode 100644 index 0000000..91d51ea --- /dev/null +++ b/mwe/ecs-homemade/inc/GameObjectMax.hpp @@ -0,0 +1,7 @@ +#include "ComponentManager.h" + +template <typename T, typename... Args> +void GameObject::AddComponent(Args &&... args) { + ComponentManager::GetInstance().AddComponent<T>( + mId, std::forward<Args>(args)...); +} diff --git a/mwe/ecs-homemade/src/ComponentManager.cpp b/mwe/ecs-homemade/src/ComponentManager.cpp new file mode 100644 index 0000000..536c152 --- /dev/null +++ b/mwe/ecs-homemade/src/ComponentManager.cpp @@ -0,0 +1,23 @@ +#include "ComponentManager.h" + +ComponentManager ComponentManager::mInstance; + +ComponentManager & ComponentManager::GetInstance() { return mInstance; } + +ComponentManager::ComponentManager() {} + +void ComponentManager::DeleteAllComponentsOfId(std::uint32_t id) { + for (auto & [type, componentArray] : + mComponents) { //Loop through all the types (in the unordered_map<>) + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id] + .clear(); //Clear the components at this specific id + } + } +} + +void ComponentManager::DeleteAllComponents() { + mComponents.clear(); //Clear the whole unordered_map<> +} diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp new file mode 100644 index 0000000..330e154 --- /dev/null +++ b/mwe/ecs-homemade/src/main.cpp @@ -0,0 +1,59 @@ +#include <chrono> +#include <cstdint> +#include <iostream> +#include <vector> + +#include "ComponentManager.h" +#include "Components.h" +#include "GameObjectMax.h" + +int main() { + auto startAdding = std::chrono::high_resolution_clock::now(); + + GameObject * gameObject[100000]; + + for (int i = 0; i < 100000; ++i) { + gameObject[i] = new GameObject(i, "Name", "Tag", 0); + + gameObject[i]->AddComponent<Sprite>("C:/Test"); + gameObject[i]->AddComponent<Rigidbody>(0, 0, i); + gameObject[i]->AddComponent<Colider>(i); + } + + auto stopAdding = std::chrono::high_resolution_clock::now(); + + //This is what systems would do: + + std::vector<std::reference_wrapper<Sprite>> sprites + = ComponentManager::GetInstance().GetComponentsByType<Sprite>(); + for (Sprite & sprite : sprites) { + //std::cout << sprite.get().mPath << std::endl; + } + //std::cout << std::endl; + + std::vector<std::reference_wrapper<Rigidbody>> rigidBodies + = ComponentManager::GetInstance().GetComponentsByType<Rigidbody>(); + for (Rigidbody & rigidbody : rigidBodies) { + //std::cout << rigidbody.get().mMass << " " << rigidbody.get().mGravityScale << " " << rigidbody.get().mBodyType << std::endl; + } + //std::cout << std::endl; + + std::vector<std::reference_wrapper<Colider>> coliders + = ComponentManager::GetInstance().GetComponentsByType<Colider>(); + for (Colider & colider : coliders) { + //std::cout << colider.get().mSize << std::endl; + } + + auto stopLooping = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < 100000; ++i) { + delete gameObject[i]; + } + + auto Addtime = std::chrono::duration_cast<std::chrono::microseconds>( + stopAdding - startAdding); + auto LoopTime = std::chrono::duration_cast<std::chrono::microseconds>( + stopLooping - stopAdding); + std::cout << "AddTime: " << Addtime.count() << " us" << std::endl; + std::cout << "LoopTime: " << LoopTime.count() << " us" << std::endl; +} diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp index 69b5eaa..c8347b3 100644 --- a/mwe/ecs-memory-efficient/src/Components.cpp +++ b/mwe/ecs-memory-efficient/src/Components.cpp @@ -5,6 +5,7 @@ Component::Component() : mActive(true) {} Sprite::Sprite(std::string path) : mPath(path) {} -Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) + : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp index 62c41de..b0c5af7 100644 --- a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp +++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp @@ -2,4 +2,6 @@ #include "ComponentManager.h" -GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} +GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, + int layer) + : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} diff --git a/mwe/gameloop/include/gameObject.h b/mwe/gameloop/include/gameObject.h index 69f4d52..abdc9b0 100644 --- a/mwe/gameloop/include/gameObject.h +++ b/mwe/gameloop/include/gameObject.h @@ -19,6 +19,7 @@ public: void setHeight(float value); void setVelX(float value); void setVelY(float value); + int direction; private: std::string name = ""; diff --git a/mwe/gameloop/include/timer.h b/mwe/gameloop/include/timer.h index 22383b2..8273746 100644 --- a/mwe/gameloop/include/timer.h +++ b/mwe/gameloop/include/timer.h @@ -13,6 +13,8 @@ public: double getFixedDeltaTime() const; void setFPS(int FPS); int getFPS() const; + double getGameScale(); + void setGameScale(double); void enforceFrameRate(); double getLag() const; diff --git a/mwe/gameloop/src/loopManager.cpp b/mwe/gameloop/src/loopManager.cpp index cb532cc..0392853 100644 --- a/mwe/gameloop/src/loopManager.cpp +++ b/mwe/gameloop/src/loopManager.cpp @@ -11,7 +11,14 @@ void LoopManager::processInput() { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { gameRunning = false; + } else if (event.key.keysym.sym == SDLK_i) { + LoopTimer::getInstance().setGameScale( + LoopTimer::getInstance().getGameScale() + 0.1); + } else if (event.key.keysym.sym == SDLK_k) { + LoopTimer::getInstance().setGameScale( + LoopTimer::getInstance().getGameScale() - 0.1); } + break; } } @@ -41,11 +48,11 @@ void LoopManager::loop() { void LoopManager::setup() { gameRunning = window.initWindow(); LoopTimer::getInstance().start(); - LoopTimer::getInstance().setFPS(50); + LoopTimer::getInstance().setFPS(500); - for (int i = 0; i < 2; i++) { + for (int i = 1; i < 3; i++) { GameObject * square - = new GameObject("square2", i * 40, i * 40, 20, 20, 0, 0); + = new GameObject("square2", i * 60, i * 60, 20, 20, 0, 0); objectList.push_back(square); } } @@ -57,13 +64,26 @@ void LoopManager::render() { } void LoopManager::update() { - fprintf(stderr, "**********normal update********** \n"); + fprintf(stderr, "********** normal update ********** \n"); LoopTimer & timer = LoopTimer::getInstance(); float delta = timer.getDeltaTime(); - for (int i = 0; i < objectList.size(); i++) { - objectList[i]->setX(objectList[i]->getX() + 50 * delta); - objectList[i]->setY(objectList[i]->getY() + 50 * delta); + GameObject * obj = objectList[i]; + + // Move the object based on its direction + if (obj->direction == 1) { + obj->setX(obj->getX() + 50 * delta); + } else { + obj->setX(obj->getX() - 50 * delta); + } + + if (obj->getX() > 500) { + obj->setX(500); + obj->direction = 0; // Switch direction to left + } else if (obj->getX() < 50) { + obj->setX(50); // Clamp the position to the boundary + obj->direction = 1; // Switch direction to right + } } } diff --git a/mwe/gameloop/src/main.cpp b/mwe/gameloop/src/main.cpp index 889a30a..c0f216a 100644 --- a/mwe/gameloop/src/main.cpp +++ b/mwe/gameloop/src/main.cpp @@ -4,9 +4,6 @@ //#include "window.h" #include "loopManager.h" #include "timer.h" -//Screen dimension constants - -//Starts up SDL and creates window int main(int argc, char * args[]) { LoopManager gameLoop; diff --git a/mwe/gameloop/src/timer.cpp b/mwe/gameloop/src/timer.cpp index 61e144d..97baef6 100644 --- a/mwe/gameloop/src/timer.cpp +++ b/mwe/gameloop/src/timer.cpp @@ -1,15 +1,11 @@ #include "timer.h" - -// Constructor (private) LoopTimer::LoopTimer() {} -// Get the singleton instance of the timer LoopTimer & LoopTimer::getInstance() { static LoopTimer instance; return instance; } -// Start the timer (initialize frame time) void LoopTimer::start() { lastFrameTime = SDL_GetTicks64(); elapsedTime = 0; @@ -17,16 +13,14 @@ void LoopTimer::start() { deltaTime = 0; } -// Update the timer, calculate deltaTime void LoopTimer::update() { uint64_t currentFrameTime = SDL_GetTicks64(); - deltaTime - = (currentFrameTime - lastFrameTime) / 1000.0; // Convert to seconds + deltaTime = (currentFrameTime - lastFrameTime) / 1000.0; if (deltaTime > maximumDeltaTime) { deltaTime = maximumDeltaTime; } - + deltaTime *= gameScale; elapsedTime += deltaTime; lastFrameTime = currentFrameTime; } @@ -44,7 +38,8 @@ void LoopTimer::setFPS(int FPS) { } int LoopTimer::getFPS() const { return FPS; } - +void LoopTimer::setGameScale(double value) { gameScale = value; }; +double LoopTimer::getGameScale() { return gameScale; } void LoopTimer::enforceFrameRate() { uint64_t currentFrameTime = SDL_GetTicks64(); double frameDuration = (currentFrameTime - lastFrameTime) / 1000.0; |