From 240b1594b4dcf3e4c2c3a45b815eade66ed42381 Mon Sep 17 00:00:00 2001 From: Garrett Dickinson Date: Mon, 4 Jul 2022 18:40:24 -0500 Subject: [PATCH] Progress on project structure and implementing ECS --- include/ComponentArray.hpp | 71 +++++++++++++++++++++++++++++ include/Components/Transform.hpp | 11 +++++ include/EntityManager.hpp | 72 +++++++++++++++++++++++++++++ include/Hydrangea.hpp | 15 ++++++ include/Math/Vec2.hpp | 42 +++++++++++++++++ include/Math/Vec3.hpp | 78 ++++++++++++++++++++++++++++++++ include/Types.hpp | 9 ++++ include/Window.hpp | 26 +++++++++++ src/Hydrangea.cpp | 2 + src/Main.cpp | 51 +++++++++++++++++++++ src/Window.cpp | 54 ++++++++++++++++++++++ src/main.cpp | 6 --- 12 files changed, 431 insertions(+), 6 deletions(-) create mode 100644 include/ComponentArray.hpp create mode 100644 include/Components/Transform.hpp create mode 100644 include/EntityManager.hpp create mode 100644 include/Hydrangea.hpp create mode 100644 include/Math/Vec2.hpp create mode 100644 include/Math/Vec3.hpp create mode 100644 include/Types.hpp create mode 100644 include/Window.hpp create mode 100644 src/Hydrangea.cpp create mode 100644 src/Main.cpp create mode 100644 src/Window.cpp delete mode 100644 src/main.cpp diff --git a/include/ComponentArray.hpp b/include/ComponentArray.hpp new file mode 100644 index 0000000..006a554 --- /dev/null +++ b/include/ComponentArray.hpp @@ -0,0 +1,71 @@ +// +// Continue at +// https://austinmorlan.com/posts/entity_component_system/#the-entity +// + +#pragma once + +#include "Types.hpp" +#include +#include +#include + + +class IComponentArray +{ +public: + virtual ~IComponentArray() = default; + virtual void EntityDestroyed(Entity entity) = 0; +}; + + +template +class ComponentArray : public IComponentArray +{ +private: + std::array mComponentArray{}; + std::unordered_map mEntityToIndexMap{}; + std::unordered_map mIndexToEntityMap{}; + size_t mSize{}; +public: + void InsertData(Entity entity, T component) + { + assert(mEntityToIndexMap.find(entity) == mEntityToIndexMap.end() && "Component added to same entity more than once."); + // Put new entry at end + size_t newIndex = mSize; + mEntityToIndexMap[entity] = newIndex; + mIndexToEntityMap[newIndex] = entity; + mComponentArray[newIndex] = component; + ++mSize; + } + + void RemoveData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Removing non-existent component."); + // Copy element at end into deleted element's place to maintain density + size_t indexOfRemovedEntity = mEntityToIndexMap[entity]; + size_t indexOfLastElement = mSize - 1; + mComponentArray[indexOfRemovedEntity] = mComponentArray[indexOfLastElement]; + // Update map to point to moved spot + Entity entityOfLastElement = mIndexToEntityMap[indexOfLastElement]; + mEntityToIndexMap[entityOfLastElement] = indexOfRemovedEntity; + mIndexToEntityMap[indexOfRemovedEntity] = entityOfLastElement; + mEntityToIndexMap.erase(entity); + mIndexToEntityMap.erase(indexOfLastElement); + --mSize; + } + + T &GetData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Retrieving non-existent component."); + return mComponentArray[mEntityToIndexMap[entity]]; + } + + void EntityDestroyed(Entity entity) override + { + if (mEntityToIndexMap.find(entity) != mEntityToIndexMap.end()) + { + RemoveData(entity); + } + } +}; diff --git a/include/Components/Transform.hpp b/include/Components/Transform.hpp new file mode 100644 index 0000000..87cecd0 --- /dev/null +++ b/include/Components/Transform.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../Math/Vec2.hpp" + +struct Transform +{ + Vec2 position; + Vec2 rotation; + Vec2 scale; +}; \ No newline at end of file diff --git a/include/EntityManager.hpp b/include/EntityManager.hpp new file mode 100644 index 0000000..ef5c014 --- /dev/null +++ b/include/EntityManager.hpp @@ -0,0 +1,72 @@ +#include "Types.hpp" +#include +#include +#include + +#ifndef WINDOW_H +#define WINDOW_H + +class EntityManager +{ +private: + // Queue of unused entity IDs + std::queue mAvailableEntities{}; + + // Array of signatures where the index corresponds to the entity ID + std::array mSignatures{}; + + // Total living entities - used to keep limits on how many exist + uint32_t mLivingEntityCount{}; + +public: + EntityManager() + { + // Initialize the queue with all possible entity IDs + for (Entity entity = 0; entity < MAX_ENTITIES; ++entity) + { + mAvailableEntities.push(entity); + } + } + + Entity CreateEntity() + { + assert(mLivingEntityCount < MAX_ENTITIES && "Too many entities in existence."); + + // Take an ID from the front of the queue + Entity id = mAvailableEntities.front(); + mAvailableEntities.pop(); + ++mLivingEntityCount; + + return id; + } + + void DestroyEntity(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Invalidate the destroyed entity's signature + mSignatures[entity].reset(); + + // Put the destroyed ID at the back of the queue + mAvailableEntities.push(entity); + --mLivingEntityCount; + } + + void SetSignature(Entity entity, Signature signature) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Put this entity's signature into the array + mSignatures[entity] = signature; + } + + Signature GetSignature(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + // Get this entity's signature from the array + return mSignatures[entity]; + } +}; + +#endif \ No newline at end of file diff --git a/include/Hydrangea.hpp b/include/Hydrangea.hpp new file mode 100644 index 0000000..1ada493 --- /dev/null +++ b/include/Hydrangea.hpp @@ -0,0 +1,15 @@ +#include +#include + +#ifndef HYDRANGEA_H +#define HYDRANGEA_H + +class Hydrangea +{ +private: + +public: + +}; + +#endif \ No newline at end of file diff --git a/include/Math/Vec2.hpp b/include/Math/Vec2.hpp new file mode 100644 index 0000000..5cc6c58 --- /dev/null +++ b/include/Math/Vec2.hpp @@ -0,0 +1,42 @@ +#pragma once +class Vec2 +{ +public: + Vec2() + : x(0.0f), y(0.0f) + {} + + Vec2(float x, float y) + : x(x), y(y) + {} + + Vec2 operator+(Vec2 const& v) + { + return Vec2( + x + v.x, + y + v.y); + } + + Vec2 operator+=(Vec2 const& v) + { + x += v.x; + y += v.y; + return *this; + } + + Vec2 operator-(Vec2 const& v) + { + return Vec2( + x - v.x, + y - v.y); + } + + Vec2 operator-=(Vec2 const& v) + { + x -= v.x; + y -= v.y; + return *this; + } + + float x, y; +}; diff --git a/include/Math/Vec3.hpp b/include/Math/Vec3.hpp new file mode 100644 index 0000000..c794ec0 --- /dev/null +++ b/include/Math/Vec3.hpp @@ -0,0 +1,78 @@ +#pragma once +class Vec3 +{ +public: + Vec3() + : x(0.0f), y(0.0f), z(0.0f) + {} + + Vec3(float x, float y, float z) + : x(x), y(y), z(z) + {} + + Vec3 operator+(Vec3 const& rhs) const + { + return Vec3( + x + rhs.x, + y + rhs.y, + z + rhs.z); + } + + Vec3 operator+=(Vec3 const& rhs) + { + x += rhs.x; + y += rhs.y; + z += rhs.z; + return *this; + } + + Vec3 operator-(Vec3 const& rhs) const + { + return Vec3( + x - rhs.x, + y - rhs.y, + z - rhs.z); + } + + Vec3 operator-=(Vec3 const& rhs) + { + x -= rhs.x; + y -= rhs.y; + z -= rhs.z; + return *this; + } + + Vec3 operator*(Vec3 const& rhs) const + { + return Vec3( + x * rhs.x, + y * rhs.y, + z * rhs.z); + } + + Vec3 operator*=(Vec3 const& rhs) + { + x *= rhs.x; + y *= rhs.y; + z *= rhs.z; + return *this; + } + + Vec3 operator*(float rhs) const + { + return Vec3( + x * rhs, + y * rhs, + z * rhs); + } + + Vec3 operator*=(float rhs) + { + x *= rhs; + y *= rhs; + z *= rhs; + return *this; + } + + float x, y, z; +}; diff --git a/include/Types.hpp b/include/Types.hpp new file mode 100644 index 0000000..e263a70 --- /dev/null +++ b/include/Types.hpp @@ -0,0 +1,9 @@ +#include +#include + +// ECS +using Entity = std::uint32_t; +const Entity MAX_ENTITIES = 5000; +using ComponentType = std::uint8_t; +const ComponentType MAX_COMPONENTS = 32; +using Signature = std::bitset; \ No newline at end of file diff --git a/include/Window.hpp b/include/Window.hpp new file mode 100644 index 0000000..dbccc24 --- /dev/null +++ b/include/Window.hpp @@ -0,0 +1,26 @@ +#include +#include + +#ifndef WINDOW_H +#define WINDOW_H + +class Window +{ +private: + SDL_Window *window = NULL; + SDL_Surface *screenSurface = NULL; + int height = 480; + int width = 640; + std::string title = "Hydrangea"; + +public: + Window(int heightIn, int widthIn, std::string titleIn); + bool Init(); + void Close(); + SDL_Window *getWindow(); + SDL_Surface *getSurface(); + int getWidth(); + int getHeight(); +}; + +#endif \ No newline at end of file diff --git a/src/Hydrangea.cpp b/src/Hydrangea.cpp new file mode 100644 index 0000000..7f51ced --- /dev/null +++ b/src/Hydrangea.cpp @@ -0,0 +1,2 @@ +#include + diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..7bbb767 --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include "../include/Window.hpp" + +// Screen dimension constants +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480; + +int main() +{ + std::cout << "Hello, Hydrangea!\n"; + + Window *myWindow = new Window(SCREEN_HEIGHT,SCREEN_WIDTH, "Hello, Hydrangea!"); + + // Start up SDL and create window + if (!myWindow->Init()) + { + printf("[Error] Failed to initialize window!\n"); + } + else + { + bool isRunning = true; + SDL_Event e; + + while (isRunning) + { + // Handle events on queue + while (SDL_PollEvent(&e) != 0) + { + // User presses the X button + if (e.type == SDL_QUIT) + { + isRunning = false; + } + // User presses the Escape key + else if (e.type == SDL_KEYDOWN) + { + if (e.key.keysym.sym == SDLK_ESCAPE) + { + isRunning = false; + } + } + } + } + + myWindow->Close(); + } + + return 0; +} diff --git a/src/Window.cpp b/src/Window.cpp new file mode 100644 index 0000000..e8479d9 --- /dev/null +++ b/src/Window.cpp @@ -0,0 +1,54 @@ +#include +#include +#include "../include/Window.hpp" + +Window::Window(int heightIn, int widthIn, std::string titleIn) +{ + height = heightIn; + width = widthIn; + title = titleIn; +} + +bool Window::Init() +{ + // Initialization flag + bool success = true; + + // Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); + success = false; + } + else + { + // Create window + window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN); + + if (window == NULL) + { + printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); + success = false; + } + else + { + // Get window surface + screenSurface = SDL_GetWindowSurface(window); + } + } + + return success; +} + + +void Window::Close() +{ + // Deallocate any surfaces we have + + //Destroy window + SDL_DestroyWindow(window); + window = NULL; + + //Quit SDL subsystems + SDL_Quit(); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index aa7023a..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - std::cout << "Hello, Hydrangea!\n"; - return 0; -} \ No newline at end of file