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