queue object destruction

This commit is contained in:
2026-04-22 08:13:20 +02:00
parent b211fbf2cd
commit a7a7f9f4e9
6 changed files with 45 additions and 1 deletions

View File

@@ -56,6 +56,7 @@ namespace Game {
static T* getEntityByName(const std::string& name); static T* getEntityByName(const std::string& name);
template<typename T> template<typename T>
static void destroyEntity(T* entity); static void destroyEntity(T* entity);
static void processPendingEntityRemovals();
private: private:
int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE; int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE;
@@ -140,7 +141,8 @@ namespace Game {
void GameManager::destroyEntity(T* entity) { void GameManager::destroyEntity(T* entity) {
static_assert(std::is_base_of_v<Object::Entity, T>, "T must derive from Object::Entity"); static_assert(std::is_base_of_v<Object::Entity, T>, "T must derive from Object::Entity");
if (entity) { if (entity) {
State::GameState::getInstance().removeEntity(entity->getName()); entity->setActive(false);
State::GameState::getInstance().queueEntityRemoval(entity->getName());
} }
} }
} }

View File

@@ -7,6 +7,7 @@
#include <object/entity.hpp> #include <object/entity.hpp>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include <vector>
namespace Game::State { namespace Game::State {
class GameState { class GameState {
@@ -23,6 +24,8 @@ namespace Game::State {
void wipe(); void wipe();
Object::Entity* getEntityByName(const std::string& name); // Get an entity by name, returns nullptr if no entity with the name exists Object::Entity* getEntityByName(const std::string& name); // Get an entity by name, returns nullptr if no entity with the name exists
std::vector<Object::Entity*> getEntitiesSnapshot(bool sortByZIndex = false); // Get a stable snapshot of entity pointers for iteration outside the lock std::vector<Object::Entity*> 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. // Add an entity to the gamestate; returns a pointer to the stored entity.
Object::Entity* addEntity(std::unique_ptr<Object::Entity> entity); Object::Entity* addEntity(std::unique_ptr<Object::Entity> entity);
@@ -31,5 +34,6 @@ namespace Game::State {
private: private:
mutable std::mutex mMutex; // Shared mutex for thread safety mutable std::mutex mMutex; // Shared mutex for thread safety
std::unordered_map<std::string, std::unique_ptr<Object::Entity>> mEntityMap; // Own entities while allowing O(1) lookup by name std::unordered_map<std::string, std::unique_ptr<Object::Entity>> mEntityMap; // Own entities while allowing O(1) lookup by name
std::vector<std::string> mPendingRemovals;
}; };
} }

View File

@@ -15,5 +15,8 @@ namespace Game::AGame {
void Enemy::onCollisionEnter(Object::Entity* other) { void Enemy::onCollisionEnter(Object::Entity* other) {
LOG("Enemy '" << getName() << "' collided with '" << other->getName() << "' (onCollisionEnter); Killing myself now!"); LOG("Enemy '" << getName() << "' collided with '" << other->getName() << "' (onCollisionEnter); Killing myself now!");
GameManager::setSharedData("enemyActiveCount", GameManager::getSharedData<int>("enemyActiveCount") - 1); GameManager::setSharedData("enemyActiveCount", GameManager::getSharedData<int>("enemyActiveCount") - 1);
// Find in state
GameManager::destroyEntity(this);
} }
} }

View File

@@ -35,6 +35,9 @@ namespace Game {
// Update components first // Update components first
entity->updateComponents(seconds); entity->updateComponents(seconds);
if (!entity->isActive()) {
continue;
}
entity->update(seconds); entity->update(seconds);
} }
@@ -42,6 +45,8 @@ namespace Game {
ERROR("Exception in GameManager thread: " << e.what()); ERROR("Exception in GameManager thread: " << e.what());
} }
GameManager::processPendingEntityRemovals();
mLastDelta = seconds; mLastDelta = seconds;
mLastUpdate = now; mLastUpdate = now;
@@ -75,4 +80,8 @@ namespace Game {
} }
} }
void GameManager::processPendingEntityRemovals() {
State::GameState::getInstance().processPendingRemovals();
}
} }

View File

@@ -82,9 +82,15 @@ namespace Game::Object::Components {
// Collision detected // Collision detected
if (!mCollidingWith.contains(other)) { if (!mCollidingWith.contains(other)) {
thisEntity->onCollisionEnter(other); thisEntity->onCollisionEnter(other);
if (!thisEntity->isActive()) {
return;
}
//other->onCollisionEnter(thisEntity); //other->onCollisionEnter(thisEntity);
} else { } else {
thisEntity->onCollisionStay(other); thisEntity->onCollisionStay(other);
if (!thisEntity->isActive()) {
return;
}
//other->onCollisionStay(thisEntity); //other->onCollisionStay(thisEntity);
} }
} }

View File

@@ -31,6 +31,25 @@ namespace Game::State {
return false; return false;
} }
void GameState::queueEntityRemoval(const std::string& name) {
std::lock_guard<std::mutex> lock(mMutex);
if (std::find(mPendingRemovals.begin(), mPendingRemovals.end(), name) == mPendingRemovals.end()) {
mPendingRemovals.push_back(name);
}
}
void GameState::processPendingRemovals() {
std::vector<std::string> pendingRemovals;
{
std::lock_guard<std::mutex> lock(mMutex);
pendingRemovals = std::move(mPendingRemovals);
}
for (const auto& name : pendingRemovals) {
removeEntity(name);
}
}
void GameState::wipe() { void GameState::wipe() {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
for (const auto& pair : mEntityMap) { for (const auto& pair : mEntityMap) {
@@ -39,6 +58,7 @@ namespace Game::State {
} }
} }
mEntityMap.clear(); mEntityMap.clear();
mPendingRemovals.clear();
} }
Object::Entity* GameState::getEntityByName(const std::string& name) { Object::Entity* GameState::getEntityByName(const std::string& name) {