diff --git a/include/game/gamemanager.hpp b/include/game/gamemanager.hpp index d9d13e0..e12f438 100644 --- a/include/game/gamemanager.hpp +++ b/include/game/gamemanager.hpp @@ -56,6 +56,7 @@ namespace Game { static T* getEntityByName(const std::string& name); template static void destroyEntity(T* entity); + static void processPendingEntityRemovals(); private: int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE; @@ -140,7 +141,8 @@ namespace Game { void GameManager::destroyEntity(T* entity) { static_assert(std::is_base_of_v, "T must derive from Object::Entity"); if (entity) { - State::GameState::getInstance().removeEntity(entity->getName()); + entity->setActive(false); + State::GameState::getInstance().queueEntityRemoval(entity->getName()); } } } \ No newline at end of file diff --git a/include/state/gamestate.hpp b/include/state/gamestate.hpp index 8d881a8..899c3e2 100644 --- a/include/state/gamestate.hpp +++ b/include/state/gamestate.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Game::State { class GameState { @@ -23,6 +24,8 @@ namespace Game::State { void wipe(); Object::Entity* getEntityByName(const std::string& name); // Get an entity by name, returns nullptr if no entity with the name exists std::vector getEntitiesSnapshot(bool sortByZIndex = false); // Get a stable snapshot of entity pointers for iteration outside the lock + void queueEntityRemoval(const std::string& name); + void processPendingRemovals(); // Add an entity to the gamestate; returns a pointer to the stored entity. Object::Entity* addEntity(std::unique_ptr entity); @@ -31,5 +34,6 @@ namespace Game::State { private: mutable std::mutex mMutex; // Shared mutex for thread safety std::unordered_map> mEntityMap; // Own entities while allowing O(1) lookup by name + std::vector mPendingRemovals; }; } \ No newline at end of file diff --git a/src/game/agame/enemy.cpp b/src/game/agame/enemy.cpp index a69c36d..e6eb593 100644 --- a/src/game/agame/enemy.cpp +++ b/src/game/agame/enemy.cpp @@ -15,5 +15,8 @@ namespace Game::AGame { void Enemy::onCollisionEnter(Object::Entity* other) { LOG("Enemy '" << getName() << "' collided with '" << other->getName() << "' (onCollisionEnter); Killing myself now!"); GameManager::setSharedData("enemyActiveCount", GameManager::getSharedData("enemyActiveCount") - 1); + + // Find in state + GameManager::destroyEntity(this); } } \ No newline at end of file diff --git a/src/game/gamemanager.cpp b/src/game/gamemanager.cpp index 0ddbd2f..e4b0a5d 100644 --- a/src/game/gamemanager.cpp +++ b/src/game/gamemanager.cpp @@ -35,6 +35,9 @@ namespace Game { // Update components first entity->updateComponents(seconds); + if (!entity->isActive()) { + continue; + } entity->update(seconds); } @@ -42,6 +45,8 @@ namespace Game { ERROR("Exception in GameManager thread: " << e.what()); } + GameManager::processPendingEntityRemovals(); + mLastDelta = seconds; mLastUpdate = now; @@ -74,5 +79,9 @@ namespace Game { mSharedBools.erase(key); } } + + void GameManager::processPendingEntityRemovals() { + State::GameState::getInstance().processPendingRemovals(); + } } \ No newline at end of file diff --git a/src/object/components/boxcollider.cpp b/src/object/components/boxcollider.cpp index 07a966d..c060a92 100644 --- a/src/object/components/boxcollider.cpp +++ b/src/object/components/boxcollider.cpp @@ -82,9 +82,15 @@ namespace Game::Object::Components { // Collision detected if (!mCollidingWith.contains(other)) { thisEntity->onCollisionEnter(other); + if (!thisEntity->isActive()) { + return; + } //other->onCollisionEnter(thisEntity); } else { thisEntity->onCollisionStay(other); + if (!thisEntity->isActive()) { + return; + } //other->onCollisionStay(thisEntity); } } diff --git a/src/state/gamestate.cpp b/src/state/gamestate.cpp index be37440..6b50276 100644 --- a/src/state/gamestate.cpp +++ b/src/state/gamestate.cpp @@ -31,6 +31,25 @@ namespace Game::State { return false; } + void GameState::queueEntityRemoval(const std::string& name) { + std::lock_guard lock(mMutex); + if (std::find(mPendingRemovals.begin(), mPendingRemovals.end(), name) == mPendingRemovals.end()) { + mPendingRemovals.push_back(name); + } + } + + void GameState::processPendingRemovals() { + std::vector pendingRemovals; + { + std::lock_guard lock(mMutex); + pendingRemovals = std::move(mPendingRemovals); + } + + for (const auto& name : pendingRemovals) { + removeEntity(name); + } + } + void GameState::wipe() { std::lock_guard lock(mMutex); for (const auto& pair : mEntityMap) { @@ -39,6 +58,7 @@ namespace Game::State { } } mEntityMap.clear(); + mPendingRemovals.clear(); } Object::Entity* GameState::getEntityByName(const std::string& name) {