feat: add delayed scene destruction to prevent null references

This commit is contained in:
2026-01-05 14:29:08 +08:00
parent cc1771f511
commit 2ed0c6445d
4 changed files with 23 additions and 3 deletions

View File

@@ -102,5 +102,10 @@ void GameApplication::run() {
m_windowManager->endUI(); m_windowManager->endUI();
m_windowManager->Present(); m_windowManager->Present();
cleanup();
} }
void GameApplication::cleanup() {
m_sceneManager->destoryQuitedScene();
}

View File

@@ -22,7 +22,7 @@ private:
std::unique_ptr<UIRenderer> m_uiRenderer; std::unique_ptr<UIRenderer> m_uiRenderer;
std::unique_ptr<DebugManager> m_debugManager; std::unique_ptr<DebugManager> m_debugManager;
Config m_config; Config m_config;
void cleanup();
public: public:
GameApplication(); GameApplication();

View File

@@ -150,6 +150,7 @@ void SceneManager::changeScene(const std::string& sceneName) {
// 退出当前场景 // 退出当前场景
if (m_scene) { if (m_scene) {
m_scene->onExit(); m_scene->onExit();
m_quitedScene = m_scene;
} }
// 切换到目标场景 // 切换到目标场景
m_scene = target; m_scene = target;
@@ -205,4 +206,14 @@ void SceneManager::handleSceneEvent(const SceneEvent& event) {
SDL_Log("SceneManager::handleSceneEvent: Unhandled event type!\n"); SDL_Log("SceneManager::handleSceneEvent: Unhandled event type!\n");
break; break;
} }
}
void SceneManager::destoryQuitedScene() {
if (!m_quitedScene) {
return;
}
const auto info = typeid(*m_quitedScene.get()).name();
m_quitedScene.reset();
std::cout << "SceneManager: " << info << " destroyed\n";
} }

View File

@@ -79,7 +79,7 @@ public:
void popScene(); void popScene();
/** /**
* @brief 替换当前场景 * @brief 替换当前场景,在帧结束销毁退出的场景如果要保留场景应该用pushScene
* @param sceneName 场景名称,用于缓存查找或工厂创建 * @param sceneName 场景名称,用于缓存查找或工厂创建
* @details 当前场景将接收 onExit() 回调并被替换。若缓存中存在该名称的场景则复用, * @details 当前场景将接收 onExit() 回调并被替换。若缓存中存在该名称的场景则复用,
* 否则通过工厂函数创建新场景。新场景成为当前场景并接收 onEnter() 回调 * 否则通过工厂函数创建新场景。新场景成为当前场景并接收 onEnter() 回调
@@ -117,6 +117,10 @@ public:
* @param event 场景事件对象 * @param event 场景事件对象
*/ */
void handleSceneEvent(const SceneEvent& event); void handleSceneEvent(const SceneEvent& event);
/**
* @brief 在帧结束的时候销毁退出的场景
*/
void destoryQuitedScene();
private: private:
SDL_Renderer* m_renderer; ///< SDL 渲染器指针 SDL_Renderer* m_renderer; ///< SDL 渲染器指针
@@ -126,7 +130,7 @@ private:
std::stack<std::shared_ptr<Scene>> m_scenes; ///< 场景栈,存储场景层级关系(使用 shared_ptr std::stack<std::shared_ptr<Scene>> m_scenes; ///< 场景栈,存储场景层级关系(使用 shared_ptr
std::unordered_map<std::string, std::shared_ptr<Scene>> m_sceneCache; ///< 场景缓存,按名字缓存场景以便切换时复用 std::unordered_map<std::string, std::shared_ptr<Scene>> m_sceneCache; ///< 场景缓存,按名字缓存场景以便切换时复用
std::unordered_map<std::string, std::function<std::shared_ptr<Scene>()>> m_sceneFactories; ///< 场景工厂映射,按名字动态创建场景实例 std::unordered_map<std::string, std::function<std::shared_ptr<Scene>()>> m_sceneFactories; ///< 场景工厂映射,按名字动态创建场景实例
std::shared_ptr<Scene> m_quitedScene; ///< 已经退出的场景,准备在最后销毁
/** /**
* @brief 获取窗口尺寸的辅助方法 * @brief 获取窗口尺寸的辅助方法
* @return 返回 {宽度, 高度},如果获取失败则返回默认值 {1600, 900} * @return 返回 {宽度, 高度},如果获取失败则返回默认值 {1600, 900}