Start implementing Game

-added ecc.sh to export compile commands
-changed EntityMemoryPool to not be singleton so that each EntityManager can have its own pool
-Entities now store a reference to the EntityManager that spawned them so they get data from the right pool
-added Random.h from learncpp.com
-DrawQueue can now be used with std::sort
-Array containers have swap function
This commit is contained in:
Joseph Aquino 2025-07-31 03:36:14 -04:00
parent 9f446ae238
commit dafc9beef5
27 changed files with 392 additions and 114 deletions

View File

@ -0,0 +1,14 @@
#pragma once
#include <utility.h>
struct Action
{
Action(u8 name_in, u8 type_in)
: name(name_in)
, type(type_in)
{ }
u8 name;
u8 type;
};

View File

@ -0,0 +1,6 @@
#pragma once
class Assets
{
};

View File

@ -2,6 +2,7 @@
#include <cstddef>
#include <iterator>
class Sprite;
namespace container
@ -9,13 +10,15 @@ namespace container
class DrawQueue
{
using size_type = unsigned int;
using size_type = size_t;
public:
class iterator
{
public:
using value_type = Sprite;
using pointer = Sprite*;
using reference = Sprite&;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
@ -24,14 +27,24 @@ public:
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
iterator operator+(difference_type) const;
iterator operator-(difference_type) const;
iterator& operator+=(difference_type);
iterator& operator-=(difference_type);
difference_type operator-(iterator) const;
friend iterator operator+(difference_type, iterator);
Sprite& operator*();
Sprite* operator->();
Sprite& operator[](size_type);
bool operator==(iterator) const;
bool operator!=(iterator) const;
private:
bool operator>(iterator) const;
bool operator<(iterator) const;
bool operator>=(iterator) const;
bool operator<=(iterator) const;
private:
Sprite* m_ptr;
};

View File

@ -130,6 +130,13 @@ public:
inline constexpr CapacityType size() const { return capacity; }
void swap(index_t first, index_t second)
{
auto temp = m_data[first];
m_data[first] = m_data[second];
m_data[second] = temp;
}
iterator begin() { return iterator(data()); }
iterator end() { return iterator(data() + capacity); }
@ -196,7 +203,6 @@ public:
{
public:
using value_type = data_type;
using reference = reference;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
@ -288,7 +294,6 @@ public:
{
public:
using value_type = data_type;
using reference = reference;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
@ -424,6 +429,16 @@ public:
void setFalseAt(CapacityType index)
{ m_data[index / 8] &= ~(1 << (index % 8)); }
void swap(index_t first, index_t second)
{
auto firstElem = reference(m_data + (first / 8), 1 << (first % 8));
auto secondElem = reference(m_data + (second / 8), 1 << (second % 8));
bool temp = firstElem;
firstElem = secondElem;
secondElem = temp;
}
iterator begin() { return iterator(data(), 0); }
iterator end() { return iterator(data() + (capacity / 8), capacity % 8); }
@ -434,4 +449,5 @@ private:
data_type* m_data;
};
}//namespace
}//namespace

View File

@ -109,6 +109,13 @@ public:
inline constexpr CapacityType size() const { return capacity; }
void swap(index_t first, index_t second)
{
auto temp = m_data[first];
m_data[first] = m_data[second];
m_data[second] = temp;
}
iterator begin() { return iterator(data()); }
iterator end() { return iterator(data() + capacity); }
@ -372,6 +379,15 @@ public:
void setFalseAt(CapacityType index)
{ m_data[index / 8] &= ~(1 << (index % 8)); }
void swap(index_t first, index_t second)
{
auto firstElem = reference(m_data + (first / 8), 1 << (first % 8));
auto secondElem = reference(m_data + (second / 8), 1 << (second % 8));
bool temp = firstElem;
firstElem = secondElem;
secondElem = temp;
}
iterator begin() { return iterator(data(), 0); }
iterator end() { return iterator(data() + (capacity / 8), capacity % 8); }

View File

@ -22,7 +22,13 @@ class Sprite
{
public:
Sprite() = default;
Sprite(u8 x_in, u8 y_in, u8 textureIndex_in, u8 drawDepth_in)
: x(x_in)
, y(y_in)
, textureIndex(textureIndex_in)
, drawDepth(drawDepth_in)
{ }
u8 x{};
u8 y{};
u8 textureIndex{};

View File

@ -1,5 +1,6 @@
#pragma once
#include <cstddef>
#include <utility.h>
#include <Entities/Components.h>
#include <Entities/EntityMemoryPool.h>
@ -11,48 +12,61 @@ public:
friend class EntityMemoryPool;
Entity() = delete;
Entity(index_t index_in)
: m_index(index_in)
Entity(EntityMemoryPool& dataLocation, index_t index_in)
: m_data(dataLocation)
, m_index(index_in)
{ }
void add()
{ m_data.getAlive(m_index).setTrue(); }
public:
// template<typename T>
// bool hasComponent() const;
template<typename T>
T& getComponent() const
{ return EntityMemoryPool::instance().getComponent<T>(m_index); }
{ return m_data.getComponent<T>(m_index); }
template<typename T>
void addComponent(const T& data)
{
T& component = EntityMemoryPool::instance().getComponent<T>(m_index);
T& component = m_data.getComponent<T>(m_index);
component = data;
}
template<typename T>
void removeComponent()
{
T& component = m_data.getComponent<T>(m_index);
component = T();
}
size_t id() const
{ return EntityMemoryPool::instance().getId(m_index); }
{ return m_data.getId(m_index); }
tag_t tag() const
{ return EntityMemoryPool::instance().getTag(m_index); }
{ return m_data.getTag(m_index); }
bool alive() const
{ return EntityMemoryPool::instance().getAlive(m_index); }
{ return m_data.getAlive(m_index); }
bool visible() const
{ return EntityMemoryPool::instance().getVisiblity(m_index); }
{ return m_data.getVisiblity(m_index); }
void makeVisible()
{ EntityMemoryPool::instance().getVisiblity(m_index).setTrue(); }
{ m_data.getVisiblity(m_index).setTrue(); }
void makeInvisible()
{ EntityMemoryPool::instance().getVisiblity(m_index).setFalse(); }
{ m_data.getVisiblity(m_index).setFalse(); }
void flipVisibility()
{ EntityMemoryPool::instance().getVisiblity(m_index).flip(); }
{ m_data.getVisiblity(m_index).flip(); }
void destroy()
{ EntityMemoryPool::instance().getAlive(m_index).setFalse(); }
{ m_data.getAlive(m_index).setFalse(); }
private:
EntityMemoryPool& m_data;
index_t m_index;
};

View File

@ -1,5 +1,6 @@
#pragma once
#include <cstddef>
#include <utility.h>
#include <Entities/EntityMemoryPool.h>
#include <Containers.h>
@ -19,6 +20,8 @@ public:
Entity addEntity(tag_t);
private:
EntityMemoryPool m_entityData;
size_t m_nextId{};
index_t m_numEntities[util::TAG_COUNT]{};
index_t m_numEntitiesToAdd[util::TAG_COUNT]{};
#ifdef CORE_DEBUG

View File

@ -12,8 +12,8 @@ class Entity;
using ComponentsContainer = std::tuple
<
container::HeapArray<Transform, util::MAX_ENTITIES>,
container::HeapArray<Sprite, util::MAX_ENTITIES>,
container::HeapArray<BoundingBox, util::MAX_ENTITIES>
container::HeapArray<BoundingBox, util::MAX_ENTITIES>,
container::HeapArray<Sprite, util::MAX_ENTITIES>
>;
class EntityMemoryPool
@ -34,26 +34,18 @@ private:
EntityMemoryPool(const EntityMemoryPool&) = delete;
static EntityMemoryPool& instance()
{
static EntityMemoryPool pool{};
return pool;
}
template<typename T>
T& getComponent(index_t index)
{ return std::get<container::HeapArray<T, util::MAX_ENTITIES>>(m_components)[index]; }
tag_t getTag(index_t index) const
{ return m_tags[index]; }
auto getAlive(index_t index)
{ return m_aliveStates[index]; }
size_t getId(index_t index) const
{ return m_ids[index]; }
auto getVisiblity(index_t index)
{ return m_visibility[index]; }
tag_t getTag(index_t) const;
size_t getId(index_t) const;
};

View File

@ -14,12 +14,12 @@ private:
friend class EntityMemoryPool;
EntityView() = delete;
EntityView(index_t, index_t);
EntityView(EntityMemoryPool&, index_t, index_t);
public:
class iterator
{
public:
iterator(index_t);
iterator(EntityMemoryPool&, index_t);
iterator& operator++();
iterator operator++(int);
iterator& operator--();
@ -29,6 +29,7 @@ public:
bool operator==(iterator);
bool operator!=(iterator);
private:
EntityMemoryPool& m_data;
index_t m_currentEntity{};
};
@ -38,6 +39,7 @@ public:
iterator end();
private:
EntityMemoryPool& m_data;
index_t m_start{};
index_t m_size{};
};

View File

@ -0,0 +1,74 @@
#pragma once
#ifndef RANDOM_MT_H
#define RANDOM_MT_H
#include <chrono>
#include <random>
// This header-only Random namespace implements a self-seeding Mersenne Twister.
// Requires C++17 or newer.
// It can be #included into as many code files as needed (The inline keyword avoids ODR violations)
// Freely redistributable, courtesy of learncpp.com (https://www.learncpp.com/cpp-tutorial/global-random-numbers-random-h/)
namespace Random
{
// Returns a seeded Mersenne Twister
// Note: we'd prefer to return a std::seed_seq (to initialize a std::mt19937), but std::seed can't be copied, so it can't be returned by value.
// Instead, we'll create a std::mt19937, seed it, and then return the std::mt19937 (which can be copied).
inline std::mt19937 generate()
{
std::random_device rd{};
// Create seed_seq with clock and 7 random numbers from std::random_device
std::seed_seq ss{
static_cast<std::seed_seq::result_type>(std::chrono::steady_clock::now().time_since_epoch().count()),
rd(), rd(), rd(), rd(), rd(), rd(), rd() };
return std::mt19937{ ss };
}
// Here's our global std::mt19937 object.
// The inline keyword means we only have one global instance for our whole program.
inline std::mt19937 mt{ generate() }; // generates a seeded std::mt19937 and copies it into our global object
// Generate a random int between [min, max] (inclusive)
// * also handles cases where the two arguments have different types but can be converted to int
inline int get(int min, int max)
{
return std::uniform_int_distribution{min, max}(mt);
}
// The following function templates can be used to generate random numbers in other cases
// See https://www.learncpp.com/cpp-tutorial/function-template-instantiation/
// You can ignore these if you don't understand them
// Generate a random value between [min, max] (inclusive)
// * min and max must have the same type
// * return value has same type as min and max
// * Supported types:
// * short, int, long, long long
// * unsigned short, unsigned int, unsigned long, or unsigned long long
// Sample call: Random::get(1L, 6L); // returns long
// Sample call: Random::get(1u, 6u); // returns unsigned int
template <typename T>
T get(T min, T max)
{
return std::uniform_int_distribution<T>{min, max}(mt);
}
// Generate a random value between [min, max] (inclusive)
// * min and max can have different types
// * return type must be explicitly specified as a template argument
// * min and max will be converted to the return type
// Sample call: Random::get<std::size_t>(0, 6); // returns std::size_t
// Sample call: Random::get<std::size_t>(0, 6u); // returns std::size_t
// Sample call: Random::get<std::int>(0, 6u); // returns int
template <typename R, typename S, typename T>
R get(S min, T max)
{
return get<R>(static_cast<R>(min), static_cast<R>(max));
}
}
#endif

View File

@ -1,6 +1,7 @@
#pragma once
#include <SFML/Graphics/Sprite.hpp>
#include <cstddef>
#include <cstdint>
#include <string_view>
@ -16,7 +17,7 @@ using s64 = int64_t;
using tag_t = uint8_t;
using subtag_t = uint8_t;
using index_t = uint16_t;
using index_t = size_t;
namespace Tag
{

View File

@ -0,0 +1,2 @@
#include <Assets.h>

View File

@ -1,9 +1,13 @@
#include "log.h"
#include "utility.h"
#include <log.h>
#include <Containers/DrawQueue.h>
#include <Entities/Components.h>
#include <algorithm>
container::DrawQueue::iterator operator+(container::DrawQueue::iterator::difference_type offset, container::DrawQueue::iterator it)
{
return it + offset;
}
namespace container
{
//iterator
@ -37,6 +41,33 @@ namespace container
return it;
}
DrawQueue::iterator DrawQueue::iterator::operator+(difference_type offset) const
{
return m_ptr + offset;
}
DrawQueue::iterator DrawQueue::iterator::operator-(difference_type offset) const
{
return m_ptr - offset;
}
DrawQueue::iterator& DrawQueue::iterator::operator+=(difference_type offset)
{
m_ptr += offset;
return *this;
}
DrawQueue::iterator& DrawQueue::iterator::operator-=(difference_type offset)
{
m_ptr -= offset;
return *this;
}
DrawQueue::iterator::difference_type DrawQueue::iterator::operator-(iterator other) const
{
return m_ptr - other.m_ptr;
}
Sprite& DrawQueue::iterator::operator*()
{
return *m_ptr;
@ -62,6 +93,26 @@ namespace container
return m_ptr != other.m_ptr;
}
bool DrawQueue::iterator::operator>(iterator other) const
{
return m_ptr > other.m_ptr;
}
bool DrawQueue::iterator::operator<(iterator other) const
{
return m_ptr < other.m_ptr;
}
bool DrawQueue::iterator::operator>=(iterator other) const
{
return m_ptr >= other.m_ptr;
}
bool DrawQueue::iterator::operator<=(iterator other) const
{
return m_ptr <= other.m_ptr;
}
//const_iterator
DrawQueue::const_iterator::const_iterator(const Sprite* input)
: m_ptr(input)
@ -226,31 +277,12 @@ namespace container
m_data[m_size] = input;
sort();
std::sort(begin(), end(), [](Sprite first, Sprite second){ return first.drawDepth < second.drawDepth;});
}
void DrawQueue::sort()
{
Sprite temp;
size_type i = 0, end = m_size - 1;
while (end != 0)
{
if (m_data[i].drawDepth > m_data[i + 1].drawDepth)
{
temp = m_data[i];
m_data[i] = m_data[i + 1];
m_data[i + 1] = temp;
}
i++;
if (i == end)
{
i = 0;
end--;
}
}
std::sort(begin(), end(), [](Sprite first, Sprite second){ return first.drawDepth < second.drawDepth;});
}
DrawQueue::iterator DrawQueue::begin()

View File

@ -1,3 +1,4 @@
#include <Entities/EntityMemoryPool.h>
#include <Entities/EntityView.h>
#include <log.h>
#include <Entities/EntityManager.h>
@ -9,26 +10,65 @@ inline constexpr index_t PLAYER_INDEX = util::tagStart[Tag::player];
int EntityManager::update()
{
//adding entities
for(int i = 0; i < util::TAG_COUNT; i++)
{
#ifdef CORE_DEBUG
if (m_numEntities[i] + m_numEntitiesToAdd[i] >= util::getTagSize(i))
{
LOG("Error: Entity Overflow");
return ErrorCode::entityOverflow;
}
#endif
EntityView toAdd(m_entityData, util::getTagStart(i) + m_numEntities[i], m_numEntitiesToAdd[i] - 1);
for (auto ent : toAdd)
{
ent.add();
m_entityData.m_ids[ent.m_index] = m_nextId++;
m_numEntities[i]++;
#ifdef CORE_DEBUG
m_totalEntities++;
#endif
}
m_numEntitiesToAdd[i] = 0;
//removing entites
EntityView entites(m_entityData, util::getTagStart(i), m_numEntities[i]);
Entity swapWith(m_entityData, 0);
for (auto ent : entites)
{
if (ent.alive()) continue;
if (ent.m_index != m_numEntities[i] - 1)//not at the end
{
swapWith.m_index = m_numEntities[i] - 1;
std::apply([=](auto&&... args) {((args.swap(ent.m_index, swapWith.m_index)), ...); }, m_entityData.m_components);
m_numEntities[i]--;
continue;
}
m_numEntities[i]--;
}
}
return 0;
}
inline Entity EntityManager::player()
{
return Entity(PLAYER_INDEX);
return Entity(m_entityData, PLAYER_INDEX);
}
EntityView EntityManager::getEntities(tag_t tag)
{
return EntityView(util::getTagStart(tag), m_numEntities[tag]);
return EntityView(m_entityData, util::getTagStart(tag), m_numEntities[tag]);
}
Entity EntityManager::addEntity(tag_t tag)
@ -37,5 +77,5 @@ Entity EntityManager::addEntity(tag_t tag)
m_totalEntities++;
#endif
return Entity(util::tagStart[tag] + m_numEntities[tag] + m_numEntitiesToAdd[tag]++);
return Entity(m_entityData, util::tagStart[tag] + m_numEntities[tag] + m_numEntitiesToAdd[tag]++);
}

View File

@ -2,24 +2,23 @@
#include <Entities/Entity.h>
#include <cstddef>
#include <vector>
#include <utility.h>
#include <Entities/EntityView.h>
EntityMemoryPool::EntityMemoryPool()
{
/*
std::apply([=](auto&&... args) {((args.reserve(util::MAX_ENTITIES)), ...); }, m_components);
m_tags.reserve(util::MAX_ENTITIES);
m_aliveStates.reserve(util::MAX_ENTITIES);
*/
for (int i = 0; i < util::TAG_COUNT; i++)
{
EntityView x(util::tagStart[i], util::tagSize[i]);
EntityView x(*this, util::tagStart[i], util::tagSize[i]);
for (auto entity : x)
{
m_tags[entity.m_index] = i;
}
}
}
}
tag_t EntityMemoryPool::getTag(index_t index) const
{ return m_tags[index]; }
size_t EntityMemoryPool::getId(index_t index) const
{ return m_ids[index]; }

View File

@ -1,21 +1,24 @@
#include "Entities/EntityMemoryPool.h"
#include <Entities/EntityView.h>
#include <Entities/Entity.h>
#include <utility.h>
EntityView::EntityView(index_t start, index_t size)
: m_start(start)
EntityView::EntityView(EntityMemoryPool& data, index_t start, index_t size)
: m_data(data)
, m_start(start)
, m_size(size)
{ }
Entity EntityView::operator[](index_t index) const
{
return Entity(m_start + index);
return Entity(m_data, m_start + index);
}
//non-const iterator
EntityView::iterator::iterator(index_t index)
: m_currentEntity(index)
EntityView::iterator::iterator(EntityMemoryPool& data, index_t index)
: m_data(data)
, m_currentEntity(index)
{ }
EntityView::iterator& EntityView::iterator::operator++()
@ -27,7 +30,7 @@ EntityView::iterator& EntityView::iterator::operator++()
EntityView::iterator EntityView::iterator::operator++(int)
{
m_currentEntity++;
return m_currentEntity - 1;
return iterator(m_data, m_currentEntity - 1);
}
EntityView::iterator& EntityView::iterator::operator--()
@ -39,17 +42,17 @@ EntityView::iterator& EntityView::iterator::operator--()
EntityView::iterator EntityView::iterator::operator--(int)
{
m_currentEntity--;
return m_currentEntity + 1;
return iterator(m_data, m_currentEntity + 1);
}
Entity EntityView::iterator::operator*()
{
return m_currentEntity;
return Entity(m_data, m_currentEntity);
}
Entity EntityView::iterator::operator[](int index)
{
return m_currentEntity + index;
return Entity(m_data, m_currentEntity + index);
}
bool EntityView::iterator::operator==(iterator other)
@ -64,10 +67,10 @@ bool EntityView::iterator::operator!=(iterator other)
EntityView::iterator EntityView::begin()
{
return iterator(m_start);
return iterator(m_data, m_start);
}
EntityView::iterator EntityView::end()
{
return iterator(m_start + m_size);
return iterator(m_data, m_start + m_size);
}

View File

@ -0,0 +1,13 @@
#pragma once
namespace global
{
enum class Scenes : int
{
Play,
SceneCount
};
inline constexpr int SCENE_COUNT {(int)Scenes::SceneCount};
}//namespace

View File

@ -1,7 +0,0 @@
//#include "imgui.h"
#include <SFML/Graphics.hpp>
class Game
{
};

37
Game/include/GameEngine.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <ConstGlobals.h>
#include <SFML/Graphics.hpp>
#include <Scenes.h>
#include <utility.h>
#include <tuple>
using SceneContainer = std::tuple
<
Play_s
>;
class GameEngine
{
public:
void changeScene(Scene* scene_in)
{
m_previousScene = m_currentScene;
m_currentScene = scene_in;
}
Scene* getCurrentScene()
{
return m_currentScene;
}
private:
sf::RenderWindow m_window;
SceneContainer m_scenes;
Scene* m_currentScene;
Scene* m_previousScene;
bool m_running;
};

4
Game/include/Scenes.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <Scenes/Scene.h>
#include <Scenes/Play.h>

View File

@ -0,0 +1,8 @@
#pragma once
#include "Scene.h"
class Play_s final : public Scene
{
};

View File

@ -0,0 +1,6 @@
#pragma once
class Scene
{
};

View File

1
Game/src/GameEngine.cpp Normal file
View File

@ -0,0 +1 @@
#include <GameEngine.h>

View File

@ -1,4 +1,3 @@
#define DRAW_SCREEN 1
#include <Containers.h>
@ -8,36 +7,17 @@
#include <imgui.h>
#include <Entities.h>
#include <iostream>
#include <utility.h>
#include <array>
int main(int argc, char* argv[])
{
LOG("\n\n\033[32mTODO: ");
LOG("-Implement swapback functionality when removing entites");
LOG("-Implement adding and removing of entities");
LOG("-Implement EntityManager::update()");
LOG("\033[0m");
EntityManager em;
auto x = em.getEntities(Tag::enemy);
for (auto entt : x)
{
LOG(entt.tag());
}
container::HeapArray<bool, 10> arr;
arr.setTrueAt(0);
arr.setTrueAt(3);
arr.setFalseAt(3);
for (auto ent : arr)
{
LOG(ent);
}
#if DRAW_SCREEN == 1
auto window = sf::RenderWindow(sf::VideoMode({ 1920u, 1080u }), "2d-platformer");
window.setFramerateLimit(60);
if (!ImGui::SFML::Init(window))
@ -71,11 +51,11 @@ int main(int argc, char* argv[])
ImGui::End();
window.clear();
ImGui::SFML::Render(window);
#if DRAW_SCREEN == 1
window.display();
#endif
}
ImGui::SFML::Shutdown();
#endif
}

3
ecc.sh Executable file
View File

@ -0,0 +1,3 @@
#! /bin/bash
./vendor/premake5/premake5 ecc