diff --git a/as6/Entity.hpp b/as6/Entity.hpp index 06be90e..5aca883 100644 --- a/as6/Entity.hpp +++ b/as6/Entity.hpp @@ -33,6 +33,7 @@ class Component { struct Entity { std::vector> components; GameContext *context = nullptr; + bool queuedForFree = false; /** * Injects the global game context/state so the entity and its components can @@ -93,4 +94,20 @@ struct Entity { c->Update(dt); } } + + /** + * Marks the entity for deferred destruction at the end of a systems tick. + * Inspired by Godot! + */ + void QueueFree() { queuedForFree = true; } + + /** + * Propagates cleanup call to all owned components so they can release any + * resources or references before the entity is destroyed. + */ + void Cleanup() { + for (auto &c : components) { + c->Cleanup(); + } + } }; diff --git a/as6/SceneManager.hpp b/as6/SceneManager.hpp index 9ddf7b1..62039c6 100644 --- a/as6/SceneManager.hpp +++ b/as6/SceneManager.hpp @@ -39,6 +39,7 @@ class SceneManager { * @param args Arguments forwarded to the constructor of the new scene. */ template void ChangeScene(Args &&...args); + template void QueueSceneChange(Args &&...args); /** * @brief Changes to the provided next scene, invoking `Exit` on the old scene and @@ -47,6 +48,7 @@ class SceneManager { * @param nextScene The new scene to switch to. */ void ChangeScene(std::unique_ptr nextScene); + void QueueSceneChange(std::unique_ptr nextScene); void Update(float dt); void Draw(); @@ -54,6 +56,7 @@ class SceneManager { // use unique_ptr to enforce single ownership and ensure proper cleanup on // scene change std::unique_ptr current; + std::unique_ptr queued; }; class StartMenuScene; @@ -100,10 +103,22 @@ inline void SceneManager::ChangeScene(std::unique_ptr nextScene) { } } +inline void SceneManager::QueueSceneChange(std::unique_ptr nextScene) { + queued = std::move(nextScene); +} + inline void SceneManager::Update(float dt) { + if (queued) { + ChangeScene(std::move(queued)); + } + if (current) { current->Update(dt); } + + if (queued) { + ChangeScene(std::move(queued)); + } } inline void SceneManager::Draw() { @@ -114,7 +129,7 @@ inline void SceneManager::Draw() { inline void StartMenuScene::Update(float) { if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - manager.ChangeScene(); + manager.QueueSceneChange(); } } @@ -154,7 +169,7 @@ inline void GameplayScene::Enter() { inline void GameplayScene::Update(float dt) { UpdateAllSystems(entities, context, dt); if (wantsDeathScene) { - manager.ChangeScene(); + manager.QueueSceneChange(); } } @@ -198,12 +213,12 @@ inline void GameplayScene::Draw() { inline void DeathScene::Update(float) { if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - manager.ChangeScene(); + manager.QueueSceneChange(); return; } if (IsKeyPressed(KEY_ESCAPE)) { - manager.ChangeScene(); + manager.QueueSceneChange(); } } @@ -217,3 +232,7 @@ inline void DeathScene::Draw() { template inline void SceneManager::ChangeScene(Args &&...args) { ChangeScene(std::make_unique(*this, std::forward(args)...)); } + +template inline void SceneManager::QueueSceneChange(Args &&...args) { + QueueSceneChange(std::make_unique(*this, std::forward(args)...)); +} diff --git a/as6/Systems.hpp b/as6/Systems.hpp index 776b5e4..fc62952 100644 --- a/as6/Systems.hpp +++ b/as6/Systems.hpp @@ -4,6 +4,7 @@ #include "Entity.hpp" #include "GameContext.hpp" +#include #include #include @@ -60,4 +61,19 @@ void UpdateAllSystems(std::vector> &entities, GameContex } } } + + auto remover = [](const std::shared_ptr &entity) { + if (!entity) { + return true; + } + + if (entity->queuedForFree) { + entity->Cleanup(); + return true; + } + + return false; + }; + + entities.erase(std::remove_if(entities.begin(), entities.end(), remover), entities.end()); }