Progress on project structure and implementing ECS
This commit is contained in:
parent
c12c4a407c
commit
240b1594b4
71
include/ComponentArray.hpp
Normal file
71
include/ComponentArray.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
11
include/Components/Transform.hpp
Normal file
11
include/Components/Transform.hpp
Normal 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
72
include/EntityManager.hpp
Normal 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
15
include/Hydrangea.hpp
Normal 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
42
include/Math/Vec2.hpp
Normal 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
78
include/Math/Vec3.hpp
Normal 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
9
include/Types.hpp
Normal 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
26
include/Window.hpp
Normal 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
2
src/Hydrangea.cpp
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
51
src/Main.cpp
Normal file
51
src/Main.cpp
Normal 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
54
src/Window.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::cout << "Hello, Hydrangea!\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user