diff options
Diffstat (limited to 'mwe')
28 files changed, 846 insertions, 19 deletions
diff --git a/mwe/ecs-homemade/CMakeLists.txt b/mwe/ecs-homemade/CMakeLists.txt new file mode 100644 index 0000000..6267c1a --- /dev/null +++ b/mwe/ecs-homemade/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ecs-homemade) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Use the debug mode (otherwise breakpoints are not compiled) +set(CMAKE_BUILD_TYPE Debug) + +add_executable(ecs-homemade +	src/main.cpp +	src/ComponentManager.cpp +	src/Components.cpp +	src/GameObjectMax.cpp +) +target_include_directories(ecs-homemade PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h new file mode 100644 index 0000000..0ba358e --- /dev/null +++ b/mwe/ecs-homemade/inc/ComponentManager.h @@ -0,0 +1,58 @@ +#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 + +	//Add a component of a specific type +	template <typename T, typename... Args> +	T & AddComponent(std::uint32_t id, Args &&... args); +	//Deletes all components of a specific type and id +	template <typename T> +	void DeleteComponentsById(std::uint32_t id); +	//Deletes all components of a specific type +	template <typename T> +	void DeleteComponents(); +	//Deletes all components of a specific id +	void DeleteAllComponentsOfId(std::uint32_t id); +	//Deletes all components +	void DeleteAllComponents(); + +	//Get a vector<> of all components at specific type and id +	template <typename T> +	std::vector<std::reference_wrapper<T>> +	GetComponentsByID(std::uint32_t id) const; +	//Get a vector<> of all components of a specific type +	template <typename T> +	std::vector<std::reference_wrapper<T>> GetComponentsByType() const; + +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..92db1d4 --- /dev/null +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -0,0 +1,135 @@ + +template <typename T, typename... Args> +T & ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { +	//Determine the type of T (this is used as the key of the unordered_map<>) +	std::type_index type = typeid(T); + +	//Check if this component type is already in the unordered_map<> +	if (mComponents.find(type) == mComponents.end()) { +		//If not, create a new (empty) vector<> of vector<unique_ptr<Component>> +		mComponents[type] +			= std::vector<std::vector<std::unique_ptr<Component>>>(); +	} + +	//Resize the vector<> if the id is greater than the current size +	if (id >= mComponents[type].size()) { +		//Initialize new slots to nullptr (resize does automatically init to nullptr) +		mComponents[type].resize(id + 1); +	} + +	//Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> +	mComponents[type][id].push_back( +		std::make_unique<T>(std::forward<Args>(args)...)); + +	return static_cast<T &>(*mComponents[type][id].back().get()); +} + +template <typename T> +void ComponentManager::DeleteComponentsById(std::uint32_t id) { +	//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 (mComponents.find(type) != mComponents.end()) { +		//Get the correct vector<> +		std::vector<std::vector<std::unique_ptr<Component>>> & componentArray +			= mComponents[type]; + +		//Make sure that the id (that we are looking for) is within the boundaries of the vector<> +		if (id < componentArray.size()) { +			//Clear the whole vector<> of this specific type and id +			componentArray[id].clear(); +		} +	} +} + +template <typename T> +void ComponentManager::DeleteComponents() { +	//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 (mComponents.find(type) != mComponents.end()) { +		//Clear the whole vector<> of this specific type +		mComponents[type].clear(); +	} +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::GetComponentsByID(std::uint32_t id) const { +	//Determine the type of T (this is used as the key of the unordered_map<>) +	std::type_index type = typeid(T); + +	//Create an empty vector<> +	std::vector<std::reference_wrapper<T>> componentVector; + +	//Find the type (in the unordered_map<>) +	if (mComponents.find(type) != mComponents.end()) { + +		//Get the correct vector<> +		const std::vector<std::vector<std::unique_ptr<Component>>> & +			componentArray +			= mComponents.at(type); + +		//Make sure that the id (that we are looking for) is within the boundaries of the vector<> +		if (id < componentArray.size()) { +			//Loop trough the whole vector<> +			for (const std::unique_ptr<Component> & componentPtr : +				 componentArray[id]) { +				//Cast the unique_ptr to a raw pointer +				T * castedComponent = static_cast<T *>(componentPtr.get()); + +				//Ensure that the cast was successful +				if (castedComponent) { +					//Add the dereferenced raw pointer to the vector<> +					componentVector.push_back(*castedComponent); +				} +			} +		} +	} + +	//Return the vector<> +	return componentVector; +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::GetComponentsByType() const { +	//Determine the type of T (this is used as the key of the unordered_map<>) +	std::type_index type = typeid(T); + +	//Create an empty vector<> +	std::vector<std::reference_wrapper<T>> componentVector; +	//std::uint32_t id = 0;	//Set the id to 0 (the id will also be stored in the returned vector<>) + +	//Find the type (in the unordered_map<>) +	if (mComponents.find(type) != mComponents.end()) { + +		//Get the correct vector<> +		const std::vector<std::vector<std::unique_ptr<Component>>> & +			componentArray +			= mComponents.at(type); + +		//Loop through the whole vector<> +		for (const std::vector<std::unique_ptr<Component>> & component : +			 componentArray) { +			//Loop trough the whole vector<> +			for (const std::unique_ptr<Component> & componentPtr : component) { +				//Cast the unique_ptr to a raw pointer +				T * castedComponent = static_cast<T *>(componentPtr.get()); + +				//Ensure that the cast was successful +				if (castedComponent) { +					//Pair the dereferenced raw pointer and the id and add it to the vector<> +					componentVector.emplace_back(std::ref(*castedComponent)); +				} +			} + +			//++id;	//Increase the id (the id will also be stored in the returned vector<>) +		} +	} + +	//Return the vector<> +	return componentVector; +} diff --git a/mwe/ecs-homemade/inc/Components.h b/mwe/ecs-homemade/inc/Components.h new file mode 100644 index 0000000..ad491e7 --- /dev/null +++ b/mwe/ecs-homemade/inc/Components.h @@ -0,0 +1,66 @@ +#pragma once + +#include <memory> +#include <string> + +class Component { +public: +	Component(); + +	bool mActive; +}; + +class Sprite : public Component { +public: +	Sprite(std::string path); + +	std::string mPath; +}; + +class Rigidbody : public Component { +public: +	Rigidbody(int mass, int gravityScale, int bodyType); + +	int mMass; +	int mGravityScale; +	int mBodyType; +}; + +class Colider : public Component { +public: +	Colider(int size); + +	int mSize; +}; + +class IBehaviour { +public: +	virtual ~IBehaviour() = default; +	virtual void onStart() = 0; +	virtual void onUpdate() = 0; +}; + +template <typename T> +class BehaviourWrapper : public IBehaviour { +public: +	BehaviourWrapper(); +	void onStart() override; +	void onUpdate() override; + +private: +	T instance; +}; + +class BehaviourScript : public Component { +public: +	template <typename T> +	void addScript(); + +	void onStart(); +	void onUpdate(); + +private: +	std::unique_ptr<IBehaviour> behaviour; +}; + +#include "Components.hpp" diff --git a/mwe/ecs-homemade/inc/Components.hpp b/mwe/ecs-homemade/inc/Components.hpp new file mode 100644 index 0000000..436a28c --- /dev/null +++ b/mwe/ecs-homemade/inc/Components.hpp @@ -0,0 +1,20 @@ +#include "Components.h" +#include <iostream> + +template <typename T> +BehaviourWrapper<T>::BehaviourWrapper() : instance() {} + +template <typename T> +void BehaviourWrapper<T>::onStart() { +	instance.onStart(); +} + +template <typename T> +void BehaviourWrapper<T>::onUpdate() { +	instance.onUpdate(); +} + +template <typename T> +void BehaviourScript::addScript() { +	behaviour = std::make_unique<BehaviourWrapper<T>>(); +} diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h new file mode 100644 index 0000000..5fab44a --- /dev/null +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -0,0 +1,20 @@ +#pragma once + +#include <cstdint> +#include <string> + +class GameObject { +public: +	GameObject(std::uint32_t id, std::string name, std::string tag, int layer); + +	template <typename T, typename... Args> +	T & AddComponent(Args &&... args); + +	std::uint32_t mId; +	std::string mName; +	std::string mTag; +	bool mActive; +	int mLayer; +}; + +#include "GameObjectMax.hpp" diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp new file mode 100644 index 0000000..2f433bb --- /dev/null +++ b/mwe/ecs-homemade/inc/GameObjectMax.hpp @@ -0,0 +1,7 @@ +#include "ComponentManager.h" + +template <typename T, typename... Args> +T & GameObject::AddComponent(Args &&... args) { +	return 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/Components.cpp b/mwe/ecs-homemade/src/Components.cpp new file mode 100644 index 0000000..de8753e --- /dev/null +++ b/mwe/ecs-homemade/src/Components.cpp @@ -0,0 +1,23 @@ +#include "Components.h" +#include <iostream> + +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) {} + +Colider::Colider(int size) : mSize(size) {} + +void BehaviourScript::onStart() { +	if (behaviour) { +		behaviour->onStart(); +	} +} + +void BehaviourScript::onUpdate() { +	if (behaviour) { +		behaviour->onUpdate(); +	} +} diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp new file mode 100644 index 0000000..b0c5af7 --- /dev/null +++ b/mwe/ecs-homemade/src/GameObjectMax.cpp @@ -0,0 +1,7 @@ +#include "GameObjectMax.h" + +#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) {} diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp new file mode 100644 index 0000000..70c5d2c --- /dev/null +++ b/mwe/ecs-homemade/src/main.cpp @@ -0,0 +1,75 @@ +#include <chrono> +#include <cstdint> +#include <iostream> +#include <vector> + +#include "ComponentManager.h" +#include "Components.h" +#include "GameObjectMax.h" + +class myScript { +public: +	void onStart() { std::cout << "In onStart" << std::endl; } + +	void onUpdate() { std::cout << "In onUpdate" << std::endl; } +}; + +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); +		gameObject[i]->AddComponent<BehaviourScript>().addScript<myScript>(); +	} + +	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; +	} + +	std::vector<std::reference_wrapper<BehaviourScript>> scripts +		= ComponentManager::GetInstance() +			  .GetComponentsByType<BehaviourScript>(); +	for (BehaviourScript & script : scripts) { +		//script.onStart(); +		//script.onUpdate(); +	} + +	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/CMakeLists.txt b/mwe/ecs-memory-efficient/CMakeLists.txt new file mode 100644 index 0000000..d072907 --- /dev/null +++ b/mwe/ecs-memory-efficient/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ecs-memory-efficient) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Use the debug mode (otherwise breakpoints are not compiled) +set(CMAKE_BUILD_TYPE Debug) + +add_executable(ecs-memory-efficient +	src/main.cpp +	src/ComponentManager.cpp +	src/Components.cpp +	src/GameObjectMax.cpp +) +target_include_directories(ecs-memory-efficient PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.h b/mwe/ecs-memory-efficient/inc/ComponentManager.h new file mode 100644 index 0000000..8279a9a --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Components.h" +#include "ContiguousContainer.h" + +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... Args> +	void addSpriteComponent(Args &&... args); +	template <typename... Args> +	void addRigidbodyComponent(Args &&... args); +	template <typename... Args> +	void addColiderComponent(Args &&... args); + +	std::vector<std::reference_wrapper<Sprite>> getAllSpriteReferences(); +	std::vector<std::reference_wrapper<Rigidbody>> getAllRigidbodyReferences(); +	std::vector<std::reference_wrapper<Colider>> getAllColiderReferences(); + +private: +	static ComponentManager mInstance; //Singleton + +	ComponentManager(); //Singleton + +	ContiguousContainer<Sprite> mSpriteContainer; +	ContiguousContainer<Rigidbody> mRigidbodyContainer; +	ContiguousContainer<Colider> mColiderContainer; +}; + +#include "ComponentManager.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.hpp b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp new file mode 100644 index 0000000..a914a6b --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp @@ -0,0 +1,15 @@ + +template <typename... Args> +void ComponentManager::addSpriteComponent(Args &&... args) { +	mSpriteContainer.pushBack(std::forward<Args>(args)...); +} + +template <typename... Args> +void ComponentManager::addRigidbodyComponent(Args &&... args) { +	mRigidbodyContainer.pushBack(std::forward<Args>(args)...); +} + +template <typename... Args> +void ComponentManager::addColiderComponent(Args &&... args) { +	mColiderContainer.pushBack(std::forward<Args>(args)...); +} diff --git a/mwe/ecs-memory-efficient/inc/Components.h b/mwe/ecs-memory-efficient/inc/Components.h new file mode 100644 index 0000000..98c5fe7 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/Components.h @@ -0,0 +1,33 @@ +#pragma once + +#include <string> + +class Component { +public: +	Component(); + +	bool mActive; +}; + +class Sprite : public Component { +public: +	Sprite(std::string path); + +	std::string mPath; +}; + +class Rigidbody : public Component { +public: +	Rigidbody(int mass, int gravityScale, int bodyType); + +	int mMass; +	int mGravityScale; +	int mBodyType; +}; + +class Colider : public Component { +public: +	Colider(int size); + +	int mSize; +}; diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.h b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h new file mode 100644 index 0000000..e3b57ba --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h @@ -0,0 +1,34 @@ +#pragma once + +#include <cstdlib> // For malloc and free +#include <new> // For placement new +#include <stdexcept> // For std::bad_alloc +#include <utility> // For std::move and std::forward +#include <vector> // For returning references + +template <typename T> +class ContiguousContainer { +public: +	ContiguousContainer(); +	~ContiguousContainer(); + +	// Use perfect forwarding for pushBack +	template <typename... Args> +	void pushBack(Args &&... args); + +	void popBack(); +	T & operator[](size_t index); +	size_t getSize() const; + +	// Function to return references to all stored objects +	std::vector<std::reference_wrapper<T>> getAllReferences(); + +private: +	T * mData; +	size_t mSize; +	size_t mCapacity; + +	void resize(size_t new_capacity); // Resize function to allocate more space +}; + +#include "ContiguousContainer.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp new file mode 100644 index 0000000..408d5aa --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp @@ -0,0 +1,84 @@ +template <typename T> +ContiguousContainer<T>::ContiguousContainer() : mSize(0), mCapacity(10) { +	// Allocate memory for 10 objects initially +	mData = static_cast<T *>(malloc(mCapacity * sizeof(T))); +	if (!mData) { +		throw std::bad_alloc(); +	} +} + +template <typename T> +ContiguousContainer<T>::~ContiguousContainer() { +	// Destroy all constructed objects +	for (size_t i = 0; i < mSize; ++i) { +		mData[i].~T(); +	} +	// Free the allocated memory +	free(mData); +} + +template <typename T> +template <typename... Args> +void ContiguousContainer<T>::pushBack(Args &&... args) { +	if (mSize == mCapacity) { +		// Double the capacity if the container is full +		resize(mCapacity * 2); +	} +	// Use placement new with perfect forwarding to construct the object in place +	new (mData + mSize) T(std::forward<Args>(args)...); +	++mSize; +} + +template <typename T> +void ContiguousContainer<T>::popBack() { +	if (mSize > 0) { +		--mSize; +		// Explicitly call the destructor +		mData[mSize].~T(); +	} +} + +template <typename T> +T & ContiguousContainer<T>::operator[](size_t index) { +	if (index >= mSize) { +		throw std::out_of_range("Index out of range"); +	} +	return mData[index]; +} + +template <typename T> +size_t ContiguousContainer<T>::getSize() const { +	return mSize; +} + +// Function that returns a vector of references to all stored objects +template <typename T> +std::vector<std::reference_wrapper<T>> +ContiguousContainer<T>::getAllReferences() { +	std::vector<std::reference_wrapper<T>> references; +	references.reserve(mSize); // Reserve space to avoid reallocation +	for (size_t i = 0; i < mSize; ++i) { +		references.push_back(std::ref(mData[i])); +	} +	return references; +} + +template <typename T> +void ContiguousContainer<T>::resize(size_t new_capacity) { +	// Allocate new memory block with the updated capacity +	T * new_data = static_cast<T *>(malloc(new_capacity * sizeof(T))); +	if (!new_data) { +		throw std::bad_alloc(); +	} + +	// Move or copy existing objects to the new memory block +	for (size_t i = 0; i < mSize; ++i) { +		new (new_data + i) T(std::move(mData[i])); // Move the objects +		mData[i].~T(); // Call the destructor for the old object +	} + +	// Free the old memory block +	free(mData); +	mData = new_data; +	mCapacity = new_capacity; +} diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.h b/mwe/ecs-memory-efficient/inc/GameObjectMax.h new file mode 100644 index 0000000..760e330 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.h @@ -0,0 +1,24 @@ +#pragma once + +#include <cstdint> +#include <string> + +class GameObject { +public: +	GameObject(std::uint32_t id, std::string name, std::string tag, int layer); + +	template <typename... Args> +	void addSpriteComponent(Args &&... args); +	template <typename... Args> +	void addRigidbodyComponent(Args &&... args); +	template <typename... Args> +	void addColiderComponent(Args &&... args); + +	std::uint32_t mId; +	std::string mName; +	std::string mTag; +	bool mActive; +	int mLayer; +}; + +#include "GameObjectMax.hpp" diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp new file mode 100644 index 0000000..be3ffa2 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp @@ -0,0 +1,19 @@ +#include "ComponentManager.h" + +template <typename... Args> +void GameObject::addSpriteComponent(Args &&... args) { +	ComponentManager::GetInstance().addSpriteComponent( +		std::forward<Args>(args)...); +} + +template <typename... Args> +void GameObject::addRigidbodyComponent(Args &&... args) { +	ComponentManager::GetInstance().addRigidbodyComponent( +		std::forward<Args>(args)...); +} + +template <typename... Args> +void GameObject::addColiderComponent(Args &&... args) { +	ComponentManager::GetInstance().addColiderComponent( +		std::forward<Args>(args)...); +} diff --git a/mwe/ecs-memory-efficient/src/ComponentManager.cpp b/mwe/ecs-memory-efficient/src/ComponentManager.cpp new file mode 100644 index 0000000..8c1fd23 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/ComponentManager.cpp @@ -0,0 +1,22 @@ +#include "ComponentManager.h" + +ComponentManager ComponentManager::mInstance; + +ComponentManager & ComponentManager::GetInstance() { return mInstance; } + +ComponentManager::ComponentManager() {} + +std::vector<std::reference_wrapper<Sprite>> +ComponentManager::getAllSpriteReferences() { +	return mSpriteContainer.getAllReferences(); +} + +std::vector<std::reference_wrapper<Rigidbody>> +ComponentManager::getAllRigidbodyReferences() { +	return mRigidbodyContainer.getAllReferences(); +} + +std::vector<std::reference_wrapper<Colider>> +ComponentManager::getAllColiderReferences() { +	return mColiderContainer.getAllReferences(); +} diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp new file mode 100644 index 0000000..c8347b3 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/Components.cpp @@ -0,0 +1,11 @@ +#include "Components.h" +#include <iostream> + +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) {} + +Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp new file mode 100644 index 0000000..b0c5af7 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp @@ -0,0 +1,7 @@ +#include "GameObjectMax.h" + +#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) {} diff --git a/mwe/ecs-memory-efficient/src/main.cpp b/mwe/ecs-memory-efficient/src/main.cpp new file mode 100644 index 0000000..9c6f2aa --- /dev/null +++ b/mwe/ecs-memory-efficient/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]->addSpriteComponent("C:/Test"); +		gameObject[i]->addRigidbodyComponent(0, 0, i); +		gameObject[i]->addColiderComponent(i); +	} + +	auto stopAdding = std::chrono::high_resolution_clock::now(); + +	//This is what systems would do: + +	std::vector<std::reference_wrapper<Sprite>> allSprites +		= ComponentManager::GetInstance().getAllSpriteReferences(); +	for (Sprite & sprite : allSprites) { +		//std::cout << sprite.mPath << std::endl; +	} +	//std::cout << std::endl; + +	std::vector<std::reference_wrapper<Rigidbody>> allRigidbody +		= ComponentManager::GetInstance().getAllRigidbodyReferences(); +	for (Rigidbody & rigidbody : allRigidbody) { +		//std::cout << rigidbody.mMass << " " << rigidbody.mGravityScale << " " << rigidbody.mBodyType << std::endl; +	} +	//std::cout << std::endl; + +	std::vector<std::reference_wrapper<Colider>> allColider +		= ComponentManager::GetInstance().getAllColiderReferences(); +	for (Colider & colider : allColider) { +		//std::cout << colider.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/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;  |