Progress on project structure and implementing ECS

This commit is contained in:
Garrett Dickinson 2022-07-04 18:40:24 -05:00
parent c12c4a407c
commit 240b1594b4
12 changed files with 431 additions and 6 deletions

View File

@ -0,0 +1,71 @@
//
// Continue at
// https://austinmorlan.com/posts/entity_component_system/#the-entity
//
#pragma once
#include "Types.hpp"
#include <array>
#include <cassert>
#include <unordered_map>
class IComponentArray
{
public:
virtual ~IComponentArray() = default;
virtual void EntityDestroyed(Entity entity) = 0;
};
template <typename T>
class ComponentArray : public IComponentArray
{
private:
std::array<T, MAX_ENTITIES> mComponentArray{};
std::unordered_map<Entity, size_t> mEntityToIndexMap{};
std::unordered_map<size_t, Entity> 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);
}
}
};

View File

@ -0,0 +1,11 @@
#pragma once
#include <SDL2/SDL.h>
#include "../Math/Vec2.hpp"
struct Transform
{
Vec2 position;
Vec2 rotation;
Vec2 scale;
};

72
include/EntityManager.hpp Normal file
View File

@ -0,0 +1,72 @@
#include "Types.hpp"
#include <array>
#include <cassert>
#include <queue>
#ifndef WINDOW_H
#define WINDOW_H
class EntityManager
{
private:
// Queue of unused entity IDs
std::queue<Entity> mAvailableEntities{};
// Array of signatures where the index corresponds to the entity ID
std::array<Signature, MAX_ENTITIES> 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

15
include/Hydrangea.hpp Normal file
View File

@ -0,0 +1,15 @@
#include <iostream>
#include <strings.h>
#ifndef HYDRANGEA_H
#define HYDRANGEA_H
class Hydrangea
{
private:
public:
};
#endif

42
include/Math/Vec2.hpp Normal file
View File

@ -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;
};

78
include/Math/Vec3.hpp Normal file
View File

@ -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;
};

9
include/Types.hpp Normal file
View File

@ -0,0 +1,9 @@
#include <bitset>
#include <cstdint>
// 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<MAX_COMPONENTS>;

26
include/Window.hpp Normal file
View File

@ -0,0 +1,26 @@
#include <iostream>
#include <strings.h>
#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

2
src/Hydrangea.cpp Normal file
View File

@ -0,0 +1,2 @@
#include <iostream>

51
src/Main.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <SDL2/SDL.h>
#include <iostream>
#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;
}

54
src/Window.cpp Normal file
View File

@ -0,0 +1,54 @@
#include <SDL2/SDL.h>
#include <cstring>
#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();
}

View File

@ -1,6 +0,0 @@
#include <iostream>
int main() {
std::cout << "Hello, Hydrangea!\n";
return 0;
}