From 45ccc88794451e13871ea872ead0e4ab5f4fd816 Mon Sep 17 00:00:00 2001 From: Redacted Date: Mon, 6 Jan 2025 17:06:54 -0500 Subject: [PATCH] Pushing what I have. --- CMakeLists.txt | 2 +- include/Engine/types/Scene.h | 27 ++----- include/Engine/types/Track.h | 32 ++++++++ include/Engine/types/entity/Camera.h | 2 + include/Engine/types/entity/Entity.h | 7 +- include/Engine/types/entity/Hud.h | 6 +- include/Engine/types/entity/Trigger.h | 51 +++++++++++++ .../Engine/types/entity/mixins/BoxCollider.h | 6 +- include/Engine/types/entity/mixins/Living.h | 16 ++++ .../Engine/types/entity/mixins/Renderable.h | 12 +-- include/Game/entity/Box.h | 2 +- include/Game/scene/Loading.h | 2 +- src/Engine/types/Scene.cpp | 73 ++++--------------- src/Engine/types/Track.cpp | 62 ++++++++++++++++ src/Engine/types/entity/Camera.cpp | 11 +++ src/Engine/types/entity/Entity.cpp | 27 +++++-- src/Engine/types/entity/Trigger.cpp | 42 +++++++++++ .../types/entity/mixins/BoxCollider.cpp | 4 +- src/Game/Scene/ControllableBox.cpp | 4 +- 19 files changed, 283 insertions(+), 105 deletions(-) create mode 100644 include/Engine/types/Track.h create mode 100644 include/Engine/types/entity/Trigger.h create mode 100644 include/Engine/types/entity/mixins/Living.h create mode 100644 src/Engine/types/Track.cpp create mode 100644 src/Engine/types/entity/Camera.cpp create mode 100644 src/Engine/types/entity/Trigger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 24002cb..39e97f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ include (cmake/CPM.cmake) CPMAddPackage( NAME JGL - URL https://git.redacted.cc/josh/JGL/archive/Prerelease-45.zip + URL https://git.redacted.cc/josh/JGL/archive/Prerelease-46.zip ) CPMAddPackage( diff --git a/include/Engine/types/Scene.h b/include/Engine/types/Scene.h index 649a0f4..aacd15d 100644 --- a/include/Engine/types/Scene.h +++ b/include/Engine/types/Scene.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,45 +11,31 @@ namespace Engine { class Scene; } -using namespace JGL; - class Engine::Scene { protected: std::string name; - Hud* heads_up_display = nullptr; Camera* active_camera = nullptr; + Entity* entity_list = nullptr; std::vector instanced_textures{}; std::vector instanced_alpha_masks{}; - - std::vector layers{}; - std::vector entity_list{}; -protected: - [[nodiscard]] std::vector GetFlatEntityList(const std::vector& entity_list) const; public: - [[nodiscard]] bool EntityListContains(const Entity* entity) const; + [[nodiscard]] Entity* GetEntityList() { return entity_list; } [[nodiscard]] size_t EntityCount() const; [[nodiscard]] std::string GetName() const; [[nodiscard]] Camera* GetActiveCamera() const; [[nodiscard]] InstancedTexture* GetInstancedTexture(const InstancedSprite* user); [[nodiscard]] InstancedTexture* GetInstancedAlphaMask(const InstancedSprite* user); +public: + void SetActiveCamera(Camera* camera) { active_camera = camera; }; void DestroyInstancedTexture(const InstancedTexture* itx); void DestroyInstancedAlphaMask(const InstancedTexture* alpha_mask); -public: - void AppendEntity(Entity* entity); - - // Removes and deallocates. - void DestroyEntity(Entity* entity); - - // Only removes from the list. - void RemoveEntity(Entity* entity); - virtual void Unload(); virtual void Init() {} virtual void Update(); - virtual void Render(RenderTarget* render_target = nullptr); + virtual void Render(JGL::RenderTarget* render_target = nullptr); public: - explicit Scene(const std::string& name) : name(name) {} + explicit Scene(const std::string& name) : name(name) { entity_list = new Entity(); } virtual ~Scene(); }; diff --git a/include/Engine/types/Track.h b/include/Engine/types/Track.h new file mode 100644 index 0000000..bf5c35b --- /dev/null +++ b/include/Engine/types/Track.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +namespace Engine { + class BoxCollider; + class Movable; + class Track; +} + +/* I would expect you to set up such that the direct children would be made to perfectly + * follow the track. The children of those children would be offset by the parents + * movement every frame. This way, by default, Things could move on a train on the track. - Redacted. */ +class Engine::Track : public Entity, public Renderable, public Living { +private: + std::vector nodes{}; + std::vector child_node_position{}; + void TranslateChild(Entity* entity); +protected: + Vector2 GetNextNode(const Movable* movable); +public: + // Returns whether the track forms a perfect loop *the first node and last node are the same* + [[nodiscard]] bool Loops() const { return !nodes.empty() && nodes.front() == nodes.back(); } + /// @returns The nearest node to a given entity. + Vector2 ClosestNodeTo(const Movable* movable) const; + Vector2 ClosestNodeTo(const BoxCollider* box_collider) const; + + void Render() override; + void Update() override; +}; diff --git a/include/Engine/types/entity/Camera.h b/include/Engine/types/entity/Camera.h index 2c62530..a86a89a 100644 --- a/include/Engine/types/entity/Camera.h +++ b/include/Engine/types/entity/Camera.h @@ -9,6 +9,8 @@ namespace Engine { class Engine::Camera : public Entity, public Movable, public Renderable { public: + void ShouldRender(bool state) override; + [[nodiscard]] bool ShouldRender() const override; void Render() override {}; public: explicit Camera(const Vector2& position) : Renderable(0), Movable(position) {} diff --git a/include/Engine/types/entity/Entity.h b/include/Engine/types/entity/Entity.h index 5b0d859..d44fdbc 100644 --- a/include/Engine/types/entity/Entity.h +++ b/include/Engine/types/entity/Entity.h @@ -15,14 +15,17 @@ protected: // Epoch micro-seconds. long creation_time = 0.0f; std::vector children{}; - void UpdateChildren(); public: + void UpdateChildren(); // Parent child structure. - [[nodiscard]] bool AppendChild(Entity* entity); + void AppendChild(Entity* entity); void DestroyChild(Entity* entity); void RemoveChild(Entity* entity); public: [[nodiscard]] std::vector GetChildren() const; + [[nodiscard]] static std::vector GetAllDescendents(const Entity* entity); + [[nodiscard]] bool HasChildren() const { return !children.empty(); } + [[nodiscard]] bool HasParent() const { return parent; } [[nodiscard]] Entity* GetParent() const; // Microseconds. diff --git a/include/Engine/types/entity/Hud.h b/include/Engine/types/entity/Hud.h index de8bad2..34d9c0d 100644 --- a/include/Engine/types/entity/Hud.h +++ b/include/Engine/types/entity/Hud.h @@ -1,12 +1,14 @@ #pragma once +#include #include namespace Engine { class Hud; } -class Engine::Hud : public Engine::Renderable { +class Engine::Hud : public Entity, public Renderable { public: - Hud() : Renderable(0) {}; + /// You want these to be rendered last. + explicit Hud(const unsigned int layer = std::numeric_limits::min()) : Renderable(layer) {}; ~Hud() override = default; }; diff --git a/include/Engine/types/entity/Trigger.h b/include/Engine/types/entity/Trigger.h new file mode 100644 index 0000000..cd24f37 --- /dev/null +++ b/include/Engine/types/entity/Trigger.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Engine { + class Trigger; + class DynamicTrigger; +} + +// Inheribable trigger. +class Engine::Trigger : public Entity, public Living, public BoxCollider, public Movable, public Renderable { +protected: + Vector2 size; +public: + [[nodiscard]] AABB2D GetBounds() const override; + void Render() override; + void SetSize(const Vector2& new_size) { size = new_size; } +public: + explicit Trigger(const Vector2& position, const Vector2& size, bool enabled = true) : + size(size), Entity(), Living(enabled), BoxCollider(enabled), Movable(position), Renderable(0, false) {} + + ~Trigger() override = default; +}; + +class Engine::DynamicTrigger final : public Trigger { +private: + void RunActions(); + std::function condition = nullptr; + std::vector> actions{}; +public: + void Update() final; + void SetCondition(const std::function& condition); + void AddAction(const std::function& action); + void ClearActions(); +public: + explicit DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled = true) : Trigger(position, size, enabled) {}; + + DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled, const std::function& condition, const std::vector>& actions) + : condition(condition), actions(actions), Trigger(position, size, enabled) {} + + DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled, const std::function& condition, const std::function& action) : + Trigger(position, size, enabled) , condition(condition) { actions.push_back(action); } + + ~DynamicTrigger() final = default; +}; \ No newline at end of file diff --git a/include/Engine/types/entity/mixins/BoxCollider.h b/include/Engine/types/entity/mixins/BoxCollider.h index 0460426..711d71d 100644 --- a/include/Engine/types/entity/mixins/BoxCollider.h +++ b/include/Engine/types/entity/mixins/BoxCollider.h @@ -9,9 +9,9 @@ class Engine::BoxCollider { private: bool enabled = false; public: - [[nodiscard]] virtual AABB2D GetBounds() = 0; - [[nodiscard]] bool BoxCollides(BoxCollider* rhs); - [[nodiscard]] bool BoxCollides(const Vector2& rhs); + [[nodiscard]] virtual AABB2D GetBounds() const = 0; + [[nodiscard]] bool BoxCollides(BoxCollider* rhs) const; + [[nodiscard]] bool BoxCollides(const Vector2& rhs) const; public: void EnableBoxCollision() { enabled = true; } void DisableBoxCollision() { enabled = false; } diff --git a/include/Engine/types/entity/mixins/Living.h b/include/Engine/types/entity/mixins/Living.h new file mode 100644 index 0000000..080367f --- /dev/null +++ b/include/Engine/types/entity/mixins/Living.h @@ -0,0 +1,16 @@ +#pragma once + +namespace Engine { + class Living; +} + +class Engine::Living { +protected: + bool alive = true; +public: + [[nodiscard]] bool IsAlive() { return alive; } + void SetAlive(bool state) { alive = state; } +public: + explicit Living(bool alive = true) : alive(alive) {} + virtual ~Living() = default; +}; \ No newline at end of file diff --git a/include/Engine/types/entity/mixins/Renderable.h b/include/Engine/types/entity/mixins/Renderable.h index 90c1c9a..9bf5807 100644 --- a/include/Engine/types/entity/mixins/Renderable.h +++ b/include/Engine/types/entity/mixins/Renderable.h @@ -1,21 +1,23 @@ #pragma once -#include namespace Engine { class Renderable; } class Engine::Renderable { -protected: - // Higher numbers are farther to the back. +private: unsigned int layer = 0; + bool should_render = true; public: [[nodiscard]] unsigned int GetLayer() const { return layer; } - void SetLayer(unsigned int new_layer) { layer = new_layer; } + [[nodiscard]] virtual bool ShouldRender() const { return should_render; }; public: + void SetLayer(unsigned int new_layer) { layer = new_layer; } + virtual void ShouldRender(bool state) { should_render = state; } // *must* be overridden. virtual void Render() = 0; public: - explicit Renderable(unsigned int layer) : layer(layer) {} + // The default layer is 1 because zero would almost always be the HUD / score / some kind of overlay. + explicit Renderable(unsigned int layer = 1, bool should_render = true) : should_render(should_render), layer(layer) {} virtual ~Renderable() = default; }; \ No newline at end of file diff --git a/include/Game/entity/Box.h b/include/Game/entity/Box.h index a964b80..b22f7a6 100644 --- a/include/Game/entity/Box.h +++ b/include/Game/entity/Box.h @@ -10,5 +10,5 @@ class Game::Box final : public Engine::InstancedSprite { public: void Update() final; public: - explicit Box(const Vector2& position, unsigned int depth = 0, float rad_rotation = 0.0f) : Engine::InstancedSprite(position, depth, "assets/sprites/Re3D.png", rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") {} + explicit Box(const Vector2& position, unsigned int layer = 1, float rad_rotation = 0.0f) : Engine::InstancedSprite(position, layer, "assets/sprites/Re3D.png", rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") {} }; \ No newline at end of file diff --git a/include/Game/scene/Loading.h b/include/Game/scene/Loading.h index b84f130..0764af9 100644 --- a/include/Game/scene/Loading.h +++ b/include/Game/scene/Loading.h @@ -6,7 +6,7 @@ class LoadingScreen final : public Engine::Scene { protected: float elapsed = 0.0f; float angle = 0.0f; - Texture* RedactedSoftware = nullptr; + JGL::Texture* RedactedSoftware = nullptr; public: explicit LoadingScreen(const std::string& name) : Scene(name) {} diff --git a/src/Engine/types/Scene.cpp b/src/Engine/types/Scene.cpp index fc214a0..78ffd17 100644 --- a/src/Engine/types/Scene.cpp +++ b/src/Engine/types/Scene.cpp @@ -5,36 +5,21 @@ #include using namespace Engine; -bool Scene::EntityListContains(const Entity* entity) const { - auto flat = GetFlatEntityList(entity_list); - - for (auto* e : flat) - if (e == entity) - return true; - return false; -} - size_t Scene::EntityCount() const { - return GetFlatEntityList(entity_list).size(); + return Entity::GetAllDescendents(entity_list).size(); } void Scene::Update() { - if (active_camera) - active_camera->Update(); - - for (auto& e : entity_list) - if (auto* c = dynamic_cast(e); c == nullptr) - e->Update(); + entity_list->UpdateChildren(); } void Scene::Render(RenderTarget* render_target) { // The Camera and the HUD are ignored here because they're special cases. std::vector display_list{}; - for (auto* e : GetFlatEntityList(entity_list)) + for (auto* e : Entity::GetAllDescendents(entity_list)) if (auto* r = dynamic_cast(e)) - if (auto* c = dynamic_cast(r); c == nullptr) - if (auto* h = dynamic_cast(c); h == nullptr) - display_list.push_back(r); + if (r->ShouldRender()) + display_list.push_back(r); std::sort(display_list.begin(), display_list.end(), [](Renderable* a, Renderable* b) { return a->GetLayer() > b->GetLayer(); @@ -46,50 +31,32 @@ void Scene::Render(RenderTarget* render_target) { active_camera->Render(); for (auto& r : display_list) - r->Render(); - - if (heads_up_display) - heads_up_display->Render(); + if (r->ShouldRender()) + r->Render(); J2D::End(); } Scene::~Scene() { - for (auto* e : entity_list) - delete e; - for (auto* itx : instanced_textures) delete itx; for (auto* a : instanced_alpha_masks) delete a; - delete heads_up_display; + delete entity_list;; delete active_camera; } void Scene::Unload() { - for (auto* e : entity_list) - delete e; + for (auto* itx : instanced_textures) + delete itx; - delete heads_up_display; -} + for (auto* a : instanced_alpha_masks) + delete a; -void Scene::AppendEntity(Entity* entity) { - if (!EntityListContains(entity)) - entity_list.push_back(entity); -} - -void Scene::DestroyEntity(Entity *entity) { - auto it = std::find(entity_list.begin(), entity_list.end(), entity); - if (it != entity_list.end()) - delete *it, entity_list.erase(it); -} - -void Scene::RemoveEntity(Entity* entity) { - auto it = std::find(entity_list.begin(), entity_list.end(), entity); - if (it != entity_list.end()) - entity_list.erase(it); + delete entity_list; + delete active_camera; } std::string Scene::GetName() const { @@ -100,18 +67,6 @@ Camera* Scene::GetActiveCamera() const { return active_camera; } -std::vector Scene::GetFlatEntityList(const std::vector& ent_list) const { - std::vector result{}; - - for (auto* e : ent_list) { - auto children = GetFlatEntityList(e->GetChildren()); - for (auto* c : children) - result.push_back(c); - result.push_back(e); - } - return result; -} - InstancedTexture* Scene::GetInstancedTexture(const InstancedSprite* user) { for (auto* itx : instanced_textures) if (itx->InUseBy(user)) diff --git a/src/Engine/types/Track.cpp b/src/Engine/types/Track.cpp new file mode 100644 index 0000000..432490c --- /dev/null +++ b/src/Engine/types/Track.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +using namespace Engine; +void Track::Render() { + if (!nodes.empty()) + J2D::DrawLines(Colors::Purples::DarkViolet, nodes.data(), nodes.size()); +} + +Vector2 Track::ClosestNodeTo(const Movable* movable) const { + if (nodes.empty()) + throw std::runtime_error("Getting the closest node to a given movable but there is no nodes?"); + + Vector2 result; + float minimum = std::numeric_limits::max(); + for (const auto& n : nodes) { + float distance_sq = Vector2::DistanceSq(n, movable->GetPosition()); + if (distance_sq < minimum) + minimum = distance_sq, result = n; + } + return result; +} + +Vector2 Track::ClosestNodeTo(const BoxCollider* box_collider) const { + if (nodes.empty()) + throw std::runtime_error("Getting the closest node to a given box_collider but there is no nodes?"); + + Vector2 result; + float minimum = std::numeric_limits::max(); + auto aabb_minimum = box_collider->GetBounds().minPoint; + auto aabb_maximum = box_collider->GetBounds().maxPoint; + + for (const auto& n : nodes) { + Vector2 closest_aabb_point_to_current_node = { std::max(aabb_minimum.x, std::min(n.x, aabb_maximum.x)), std::max(aabb_minimum.y, std::min(n.y, aabb_maximum.y)) }; + float distance_squared = Vector2::DistanceSq(n, closest_aabb_point_to_current_node); + + if (distance_squared < minimum) + minimum = distance_squared, result = n; + } + return result; +} + +void Track::Update() { + +} + +void Track::TranslateChild(Entity* entity) { + + /* + if (auto* m = dynamic_cast(entity)) + m->Move() + auto m_pos = movable->GetPosition(); + */ +} + +Vector2 Track::GetNextNode(const Movable* movable) { + auto m_pos = movable->GetPosition(); + + +} diff --git a/src/Engine/types/entity/Camera.cpp b/src/Engine/types/entity/Camera.cpp new file mode 100644 index 0000000..756d483 --- /dev/null +++ b/src/Engine/types/entity/Camera.cpp @@ -0,0 +1,11 @@ +#include +#include + +void Engine::Camera::ShouldRender(bool state) { + if (state) + Globals::CurrentScene->SetActiveCamera(this); +} + +bool Engine::Camera::ShouldRender() const { + return Globals::CurrentScene->GetActiveCamera() == this; +} diff --git a/src/Engine/types/entity/Entity.cpp b/src/Engine/types/entity/Entity.cpp index dd10c9e..bf4f0cc 100644 --- a/src/Engine/types/entity/Entity.cpp +++ b/src/Engine/types/entity/Entity.cpp @@ -2,13 +2,12 @@ #include #include -bool Engine::Entity::AppendChild(Entity* entity) { - if (!Globals::CurrentScene->EntityListContains(entity)) { - entity->parent = this; - children.push_back(entity); - return true; - } - return false; +void Engine::Entity::AppendChild(Entity* entity) { + if (entity->parent) + entity->parent->RemoveChild(entity); + + entity->parent = this; + children.push_back(entity); } Engine::Entity::~Entity() { @@ -34,6 +33,7 @@ void Engine::Entity::RemoveChild(Entity* entity) { auto it = std::find(children.begin(), children.end(), entity); if (it != children.end()) children.erase(it); + entity->parent = nullptr; } long Engine::Entity::GetCreationTime() const { @@ -51,3 +51,16 @@ Engine::Entity* Engine::Entity::GetParent() const { std::vector Engine::Entity::GetChildren() const { return children; } + +std::vector Engine::Entity::GetAllDescendents(const Engine::Entity* entity) { + std::vector result = entity->GetChildren(); + + if (result.empty()) + return result; + + for (auto* c : entity->GetChildren()) { + auto descendants = Entity::GetAllDescendents(c); + result.insert(result.end(), descendants.begin(), descendants.end()); + } + return result; +} diff --git a/src/Engine/types/entity/Trigger.cpp b/src/Engine/types/entity/Trigger.cpp new file mode 100644 index 0000000..021ac4a --- /dev/null +++ b/src/Engine/types/entity/Trigger.cpp @@ -0,0 +1,42 @@ +#include +#include + +using namespace Engine; + +void Trigger::Render() { + if (ShouldRender()) { + auto size = GetBounds().maxPoint - GetBounds().minPoint; + J2D::OutlineRect(Colors::Red, position, size); + } +} + +AABB2D Trigger::GetBounds() const { + return { position, position + size }; +} + + +void DynamicTrigger::AddAction(const std::function& action) { + actions.push_back(action); +} + +void DynamicTrigger::Update() { + bool success = false; + if (condition) + success = condition(); + + if (success) + RunActions(); +} + +void DynamicTrigger::RunActions() { + for (const auto& a : actions) + a(); +} + +void DynamicTrigger::ClearActions() { + actions = {}; +} + +void DynamicTrigger::SetCondition(const std::function & c) { + condition = c; +} diff --git a/src/Engine/types/entity/mixins/BoxCollider.cpp b/src/Engine/types/entity/mixins/BoxCollider.cpp index 6201e9a..25be0b9 100644 --- a/src/Engine/types/entity/mixins/BoxCollider.cpp +++ b/src/Engine/types/entity/mixins/BoxCollider.cpp @@ -1,7 +1,7 @@ #include using namespace Engine; -bool BoxCollider::BoxCollides(BoxCollider* rhs) { +bool BoxCollider::BoxCollides(BoxCollider* rhs) const { if (GetBounds().Intersects(rhs->GetBounds())) return true; if (GetBounds().Contains(rhs->GetBounds())) @@ -9,6 +9,6 @@ bool BoxCollider::BoxCollides(BoxCollider* rhs) { return false; } -bool BoxCollider::BoxCollides(const Vector2& rhs) { +bool BoxCollider::BoxCollides(const Vector2& rhs) const { return GetBounds().Contains(rhs); } diff --git a/src/Game/Scene/ControllableBox.cpp b/src/Game/Scene/ControllableBox.cpp index 8447e78..6bb9d92 100644 --- a/src/Game/Scene/ControllableBox.cpp +++ b/src/Game/Scene/ControllableBox.cpp @@ -5,6 +5,6 @@ void ControllableBox::Init() { auto* hud = new Game::DemoGameHud(); auto *b = new Game::Box({0, 0}); - AppendEntity(b); - heads_up_display = hud; + entity_list->AppendChild(b); + entity_list->AppendChild(hud); }