完成场景与场景管理
This commit is contained in:
@@ -45,6 +45,9 @@ add_executable(${TARGET}
|
||||
src/engine/object/game_object.cpp
|
||||
src/engine/component/sprite_component.cpp
|
||||
src/engine/component/transform_component.cpp
|
||||
src/engine/scene/scene.cpp
|
||||
src/engine/scene/scene_manager.cpp
|
||||
src/game/scene/game_scene.cpp
|
||||
)
|
||||
|
||||
# 链接库
|
||||
|
||||
@@ -5,17 +5,14 @@
|
||||
#include "../render/renderer.h"
|
||||
#include "../render/camera.h"
|
||||
#include "../input/input_manager.h"
|
||||
#include "../object/game_object.h"
|
||||
#include "../component/transform_component.h"
|
||||
#include "../component/sprite_component.h"
|
||||
#include "config.h"
|
||||
#include "../scene/scene_manager.h"
|
||||
#include "../../game/scene/game_scene.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace engine::core {
|
||||
|
||||
engine::object::GameObject game_object("test_game_object");
|
||||
|
||||
GameApp::GameApp() = default;
|
||||
|
||||
GameApp::~GameApp() {
|
||||
@@ -57,13 +54,14 @@ bool GameApp::init() {
|
||||
if (!initInputManager()) return false;
|
||||
|
||||
if (!initContext()) return false;
|
||||
if (!initSceneManager()) return false;
|
||||
|
||||
// 测试资源管理器
|
||||
testResourceManager();
|
||||
// 创建第一个场景并压入栈
|
||||
auto scene = std::make_unique<game::scene::GameScene>("GameScene", *context_, *scene_manager_);
|
||||
scene_manager_->requestPushScene(std::move(scene));
|
||||
|
||||
is_running_ = true;
|
||||
spdlog::trace("GameApp 初始化成功。");
|
||||
testGameObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,12 +72,12 @@ void GameApp::handleEvents() {
|
||||
return;
|
||||
}
|
||||
|
||||
testInputManager();
|
||||
scene_manager_->handleInput();
|
||||
}
|
||||
|
||||
void GameApp::update(float /* delta_time */) {
|
||||
void GameApp::update(float delta_time) {
|
||||
// 游戏逻辑更新
|
||||
testCamera();
|
||||
scene_manager_->update(delta_time);
|
||||
}
|
||||
|
||||
void GameApp::render() {
|
||||
@@ -87,8 +85,7 @@ void GameApp::render() {
|
||||
renderer_->clearScreen();
|
||||
|
||||
// 2. 具体渲染代码
|
||||
testRenderer();
|
||||
game_object.render(*context_);
|
||||
scene_manager_->render();
|
||||
|
||||
// 3. 更新屏幕显示
|
||||
renderer_->present();
|
||||
@@ -222,77 +219,16 @@ bool GameApp::initContext()
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- 测试用函数 ---
|
||||
|
||||
void GameApp::testResourceManager()
|
||||
bool GameApp::initSceneManager()
|
||||
{
|
||||
resource_manager_->getTexture("assets/textures/Actors/eagle-attack.png");
|
||||
resource_manager_->getFont("assets/fonts/VonwaonBitmap-16px.ttf", 16);
|
||||
resource_manager_->getSound("assets/audio/button_click.wav");
|
||||
|
||||
resource_manager_->unloadTexture("assets/textures/Actors/eagle-attack.png");
|
||||
resource_manager_->unloadFont("assets/fonts/VonwaonBitmap-16px.ttf", 16);
|
||||
resource_manager_->unloadSound("assets/audio/button_click.wav");
|
||||
}
|
||||
|
||||
void GameApp::testRenderer()
|
||||
{
|
||||
engine::render::Sprite sprite_world("assets/textures/Actors/frog.png");
|
||||
engine::render::Sprite sprite_ui("assets/textures/UI/buttons/Start1.png");
|
||||
engine::render::Sprite sprite_parallax("assets/textures/Layers/back.png");
|
||||
|
||||
static float rotation = 0.0f;
|
||||
rotation += 0.1f;
|
||||
|
||||
// 注意渲染顺序
|
||||
renderer_->drawParallax(*camera_, sprite_parallax, glm::vec2(100, 100), glm::vec2(0.5f, 0.5f), glm::bvec2(true, false));
|
||||
renderer_->drawSprite(*camera_, sprite_world, glm::vec2(200, 200), glm::vec2(1.0f, 1.0f), rotation);
|
||||
renderer_->drawUISprite(sprite_ui, glm::vec2(100, 100));
|
||||
|
||||
}
|
||||
|
||||
void GameApp::testCamera()
|
||||
{
|
||||
auto key_state = SDL_GetKeyboardState(nullptr);
|
||||
if (key_state[SDL_SCANCODE_UP]) camera_->move(glm::vec2(0, -1));
|
||||
if (key_state[SDL_SCANCODE_DOWN]) camera_->move(glm::vec2(0, 1));
|
||||
if (key_state[SDL_SCANCODE_LEFT]) camera_->move(glm::vec2(-1, 0));
|
||||
if (key_state[SDL_SCANCODE_RIGHT]) camera_->move(glm::vec2(1, 0));
|
||||
}
|
||||
|
||||
void GameApp::testInputManager()
|
||||
{
|
||||
std::vector<std::string> actions = {
|
||||
"move_up",
|
||||
"move_down",
|
||||
"move_left",
|
||||
"move_right",
|
||||
"jump",
|
||||
"attack",
|
||||
"pause",
|
||||
"MouseLeftClick",
|
||||
"MouseRightClick"
|
||||
};
|
||||
|
||||
for (const auto& action : actions) {
|
||||
if (input_manager_->isActionPressed(action)) {
|
||||
spdlog::info(" {} 按下 ", action);
|
||||
}
|
||||
if (input_manager_->isActionReleased(action)) {
|
||||
spdlog::info(" {} 抬起 ", action);
|
||||
}
|
||||
if (input_manager_->isActionDown(action)) {
|
||||
spdlog::info(" {} 按下中 ", action);
|
||||
}
|
||||
try {
|
||||
scene_manager_ = std::make_unique<engine::scene::SceneManager>(*context_);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("初始化场景管理器失败: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GameApp::testGameObject()
|
||||
{
|
||||
game_object.addComponent<engine::component::TransformComponent>(glm::vec2(100, 100));
|
||||
game_object.addComponent<engine::component::SpriteComponent>("assets/textures/Props/big-crate.png", *resource_manager_, engine::utils::Alignment::CENTER);
|
||||
game_object.getComponent<engine::component::TransformComponent>()->setScale(glm::vec2(2.0f, 2.0f));
|
||||
game_object.getComponent<engine::component::TransformComponent>()->setRotation(30.0f);
|
||||
spdlog::trace("场景管理器初始化成功。");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace engine::core
|
||||
@@ -18,6 +18,10 @@ namespace engine::input {
|
||||
class InputManager;
|
||||
}
|
||||
|
||||
namespace engine::scene {
|
||||
class SceneManager;
|
||||
}
|
||||
|
||||
namespace engine::core { // 命名空间的最佳实践:与文件路径一致
|
||||
class Time;
|
||||
class Config;
|
||||
@@ -40,6 +44,7 @@ private:
|
||||
std::unique_ptr<engine::core::Config> config_;
|
||||
std::unique_ptr<engine::input::InputManager> input_manager_;
|
||||
std::unique_ptr<engine::core::Context> context_;
|
||||
std::unique_ptr<engine::scene::SceneManager> scene_manager_;
|
||||
|
||||
public:
|
||||
GameApp();
|
||||
@@ -72,13 +77,7 @@ private:
|
||||
[[nodiscard]] bool initCamera();
|
||||
[[nodiscard]] bool initInputManager();
|
||||
[[nodiscard]] bool initContext();
|
||||
|
||||
// 测试用函数
|
||||
void testResourceManager();
|
||||
void testRenderer();
|
||||
void testCamera();
|
||||
void testInputManager();
|
||||
void testGameObject();
|
||||
[[nodiscard]] bool initSceneManager();
|
||||
};
|
||||
|
||||
} // namespace engine::core
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#include "scene.h"
|
||||
#include "../object/game_object.h"
|
||||
#include "scene_manager.h"
|
||||
#include <algorithm> // for std::remove_if
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace engine::scene {
|
||||
|
||||
Scene::Scene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager)
|
||||
: scene_name_(std::move(name)), context_(context), scene_manager_(scene_manager), is_initialized_(false) {
|
||||
spdlog::trace("场景 '{}' 构造完成。", scene_name_);
|
||||
}
|
||||
|
||||
Scene::~Scene() = default;
|
||||
|
||||
void Scene::init() {
|
||||
is_initialized_ = true; // 子类应该最后调用父类的 init 方法
|
||||
spdlog::trace("场景 '{}' 初始化完成。", scene_name_);
|
||||
}
|
||||
|
||||
void Scene::update(float delta_time) {
|
||||
if (!is_initialized_) return;
|
||||
|
||||
// 更新所有游戏对象,并删除需要移除的对象
|
||||
for (auto it = game_objects_.begin(); it != game_objects_.end();) {
|
||||
if (*it && !(*it)->isNeedRemove()) { // 正常更新游戏对象
|
||||
(*it)->update(delta_time, context_);
|
||||
++it;
|
||||
} else {
|
||||
if (*it) { // 安全删除需要移除的对象
|
||||
(*it)->clean();
|
||||
}
|
||||
it = game_objects_.erase(it); // 删除需要移除的对象,智能指针自动管理内存
|
||||
}
|
||||
}
|
||||
|
||||
processPendingAdditions(); // 处理待添加(延时添加)的游戏对象
|
||||
}
|
||||
|
||||
void Scene::render() {
|
||||
if (!is_initialized_) return;
|
||||
// 渲染所有游戏对象
|
||||
for (const auto& obj : game_objects_) {
|
||||
if (obj) obj->render(context_);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::handleInput() {
|
||||
if (!is_initialized_) return;
|
||||
|
||||
// 遍历所有游戏对象,并删除需要移除的对象
|
||||
for (auto it = game_objects_.begin(); it != game_objects_.end();) {
|
||||
if (*it && !(*it)->isNeedRemove()) {
|
||||
(*it)->handleInput(context_);
|
||||
++it;
|
||||
} else {
|
||||
// 安全删除需要移除的对象
|
||||
if (*it) (*it)->clean();
|
||||
it = game_objects_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::clean() {
|
||||
if (!is_initialized_) return;
|
||||
|
||||
for (const auto& obj : game_objects_) {
|
||||
if (obj) obj->clean();
|
||||
}
|
||||
game_objects_.clear();
|
||||
|
||||
is_initialized_ = false; // 清理完成后,设置场景为未初始化
|
||||
spdlog::trace("场景 '{}' 清理完成。", scene_name_);
|
||||
}
|
||||
|
||||
void Scene::addGameObject(std::unique_ptr<engine::object::GameObject>&& game_object) {
|
||||
if (game_object) game_objects_.push_back(std::move(game_object));
|
||||
else spdlog::warn("尝试向场景 '{}' 添加空游戏对象。", scene_name_);
|
||||
}
|
||||
|
||||
void Scene::safeAddGameObject(std::unique_ptr<engine::object::GameObject>&& game_object)
|
||||
{
|
||||
if (game_object) pending_additions_.push_back(std::move(game_object));
|
||||
else spdlog::warn("尝试向场景 '{}' 添加空游戏对象。", scene_name_);
|
||||
}
|
||||
|
||||
void Scene::removeGameObject(engine::object::GameObject* game_object_ptr) {
|
||||
if (!game_object_ptr) {
|
||||
spdlog::warn("尝试从场景 '{}' 中移除一个空的游戏对象指针。", scene_name_);
|
||||
return;
|
||||
}
|
||||
|
||||
// erase-remove 移除法不可用,因为智能指针与裸指针无法比较
|
||||
// 需要使用 std::remove_if 和 lambda 表达式自定义比较的方式
|
||||
auto it = std::remove_if(game_objects_.begin(), game_objects_.end(),
|
||||
[game_object_ptr](const std::unique_ptr<engine::object::GameObject>& p) {
|
||||
return p.get() == game_object_ptr; // 比较裸指针是否相等(自定义比较方式)
|
||||
});
|
||||
|
||||
if (it != game_objects_.end()) {
|
||||
(*it)->clean(); // 因为传入的是指针,因此只可能有一个元素被移除,不需要遍历it到末尾
|
||||
game_objects_.erase(it, game_objects_.end()); // 删除从it到末尾的元素(最后一个元素)
|
||||
spdlog::trace("从场景 '{}' 中移除游戏对象。", scene_name_);
|
||||
} else {
|
||||
spdlog::warn("游戏对象指针未找到在场景 '{}' 中。", scene_name_);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::safeRemoveGameObject(engine::object::GameObject* game_object_ptr)
|
||||
{
|
||||
game_object_ptr->setNeedRemove(true);
|
||||
}
|
||||
|
||||
engine::object::GameObject *Scene::findGameObjectByName(const std::string &name) const
|
||||
{
|
||||
// 找到第一个符合条件的游戏对象就返回
|
||||
for (const auto& obj : game_objects_) {
|
||||
if (obj && obj->getName() == name) {
|
||||
return obj.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Scene::processPendingAdditions()
|
||||
{
|
||||
// 处理待添加的游戏对象
|
||||
for (auto& game_object : pending_additions_) {
|
||||
addGameObject(std::move(game_object));
|
||||
}
|
||||
pending_additions_.clear();
|
||||
}
|
||||
|
||||
} // namespace engine::scene
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace engine::core {
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace engine::render {
|
||||
class Renderer;
|
||||
class Camera;
|
||||
}
|
||||
|
||||
namespace engine::input {
|
||||
class InputManager;
|
||||
}
|
||||
|
||||
namespace engine::object {
|
||||
class GameObject;
|
||||
}
|
||||
|
||||
namespace engine::scene {
|
||||
class SceneManager;
|
||||
|
||||
/**
|
||||
* @brief 场景基类,负责管理场景中的游戏对象和场景生命周期。
|
||||
*
|
||||
* 包含一组游戏对象,并提供更新、渲染、处理输入和清理的接口。
|
||||
* 派生类应实现具体的场景逻辑。
|
||||
*/
|
||||
class Scene {
|
||||
protected:
|
||||
std::string scene_name_; ///< @brief 场景名称
|
||||
engine::core::Context& context_; ///< @brief 上下文引用(隐式,构造时传入)
|
||||
engine::scene::SceneManager& scene_manager_; ///< @brief 场景管理器引用(构造时传入)
|
||||
bool is_initialized_ = false; ///< @brief 场景是否已初始化(非当前场景很可能未被删除,因此需要初始化标志避免重复初始化)
|
||||
std::vector<std::unique_ptr<engine::object::GameObject>> game_objects_; ///< @brief 场景中的游戏对象
|
||||
std::vector<std::unique_ptr<engine::object::GameObject>> pending_additions_; ///< @brief 待添加的游戏对象(延时添加)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数。
|
||||
*
|
||||
* @param name 场景的名称。
|
||||
* @param context 场景上下文。
|
||||
* @param scene_manager 场景管理器。
|
||||
*/
|
||||
Scene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager);
|
||||
|
||||
virtual ~Scene(); // 1. 基类必须声明虚析构函数才能让派生类析构函数被正确调用。
|
||||
// 2. 析构函数定义必须写在cpp中,不然需要引入GameObject头文件
|
||||
|
||||
// 禁止拷贝和移动构造
|
||||
Scene(const Scene&) = delete;
|
||||
Scene& operator=(const Scene&) = delete;
|
||||
Scene(Scene&&) = delete;
|
||||
Scene& operator=(Scene&&) = delete;
|
||||
|
||||
// 核心循环方法
|
||||
virtual void init(); ///< @brief 初始化场景。
|
||||
virtual void update(float delta_time); ///< @brief 更新场景。
|
||||
virtual void render(); ///< @brief 渲染场景。
|
||||
virtual void handleInput(); ///< @brief 处理输入。
|
||||
virtual void clean(); ///< @brief 清理场景。
|
||||
|
||||
/// @brief 直接向场景中添加一个游戏对象。(初始化时可用,游戏进行中不安全) (&&表示右值引用,与std::move搭配使用,避免拷贝)
|
||||
virtual void addGameObject(std::unique_ptr<engine::object::GameObject>&& game_object);
|
||||
|
||||
/// @brief 安全地添加游戏对象。(添加到pending_additions_中)
|
||||
virtual void safeAddGameObject(std::unique_ptr<engine::object::GameObject>&& game_object);
|
||||
|
||||
/// @brief 直接从场景中移除一个游戏对象。(一般不使用,但保留实现的逻辑)
|
||||
virtual void removeGameObject(engine::object::GameObject* game_object_ptr);
|
||||
|
||||
/// @brief 安全地移除游戏对象。(设置need_remove_标记)
|
||||
virtual void safeRemoveGameObject(engine::object::GameObject* game_object_ptr);
|
||||
|
||||
/// @brief 获取场景中的游戏对象容器。
|
||||
const std::vector<std::unique_ptr<engine::object::GameObject>>& getGameObjects() const { return game_objects_; }
|
||||
|
||||
/// @brief 根据名称查找游戏对象(返回找到的第一个对象)。
|
||||
engine::object::GameObject* findGameObjectByName(const std::string& name) const;
|
||||
|
||||
// getters and setters
|
||||
void setName(const std::string& name) { scene_name_ = name; } ///< @brief 设置场景名称
|
||||
const std::string& getName() const { return scene_name_; } ///< @brief 获取场景名称
|
||||
void setInitialized(bool initialized) { is_initialized_ = initialized; } ///< @brief 设置场景是否已初始化
|
||||
bool isInitialized() const { return is_initialized_; } ///< @brief 获取场景是否已初始化
|
||||
|
||||
engine::core::Context& getContext() const { return context_; } ///< @brief 获取上下文引用
|
||||
engine::scene::SceneManager& getSceneManager() const { return scene_manager_; } ///< @brief 获取场景管理器引用
|
||||
std::vector<std::unique_ptr<engine::object::GameObject>>& getGameObjects() { return game_objects_; } ///< @brief 获取场景中的游戏对象
|
||||
|
||||
protected:
|
||||
void processPendingAdditions(); ///< @brief 处理待添加的游戏对象。(每轮更新的最后调用)
|
||||
};
|
||||
|
||||
} // namespace engine::scene
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "scene_manager.h"
|
||||
#include "scene.h"
|
||||
#include "../core/context.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace engine::scene {
|
||||
|
||||
SceneManager::SceneManager(engine::core::Context& context)
|
||||
: context_(context) {
|
||||
spdlog::trace("场景管理器已创建。");
|
||||
}
|
||||
|
||||
SceneManager::~SceneManager() {
|
||||
spdlog::trace("场景管理器已销毁。");
|
||||
close(); // 即使不手动调用 close 也能确保清理
|
||||
}
|
||||
|
||||
Scene* SceneManager::getCurrentScene() const {
|
||||
if (scene_stack_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return scene_stack_.back().get(); // 返回栈顶场景的裸指针
|
||||
}
|
||||
|
||||
void SceneManager::update(float delta_time) {
|
||||
// 只更新栈顶(当前)场景
|
||||
Scene* current_scene = getCurrentScene();
|
||||
if (current_scene) {
|
||||
current_scene->update(delta_time);
|
||||
}
|
||||
// 执行可能的切换场景操作
|
||||
processPendingActions();
|
||||
}
|
||||
|
||||
void SceneManager::render() {
|
||||
// 渲染时需要叠加渲染所有场景,而不只是栈顶
|
||||
for (const auto& scene : scene_stack_) {
|
||||
if (scene) {
|
||||
scene->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::handleInput() {
|
||||
// 只考虑栈顶场景
|
||||
Scene* current_scene = getCurrentScene();
|
||||
if (current_scene) {
|
||||
current_scene->handleInput();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::close() {
|
||||
spdlog::trace("正在关闭场景管理器并清理场景栈...");
|
||||
// 清理栈中所有剩余的场景(从顶到底)
|
||||
while (!scene_stack_.empty()) {
|
||||
if (scene_stack_.back()) {
|
||||
spdlog::debug("正在清理场景 '{}' 。", scene_stack_.back()->getName());
|
||||
scene_stack_.back()->clean();
|
||||
}
|
||||
scene_stack_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::requestPopScene()
|
||||
{
|
||||
pending_action_ = PendingAction::Pop;
|
||||
}
|
||||
|
||||
void SceneManager::requestReplaceScene(std::unique_ptr<Scene>&& scene)
|
||||
{
|
||||
pending_action_ = PendingAction::Replace;
|
||||
pending_scene_ = std::move(scene);
|
||||
}
|
||||
|
||||
void SceneManager::requestPushScene(std::unique_ptr<Scene>&& scene)
|
||||
{
|
||||
pending_action_ = PendingAction::Push;
|
||||
pending_scene_ = std::move(scene);
|
||||
}
|
||||
|
||||
// --- Private Methods ---
|
||||
|
||||
void SceneManager::processPendingActions()
|
||||
{
|
||||
if (pending_action_ == PendingAction::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pending_action_) {
|
||||
case PendingAction::Pop:
|
||||
popScene();
|
||||
break;
|
||||
case PendingAction::Replace:
|
||||
replaceScene(std::move(pending_scene_));
|
||||
break;
|
||||
case PendingAction::Push:
|
||||
pushScene(std::move(pending_scene_));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pending_action_ = PendingAction::None;
|
||||
}
|
||||
|
||||
void SceneManager::pushScene(std::unique_ptr<Scene>&& scene) {
|
||||
if (!scene) {
|
||||
spdlog::warn("尝试将空场景压入栈。");
|
||||
return;
|
||||
}
|
||||
spdlog::debug("正在将场景 '{}' 压入栈。", scene->getName());
|
||||
|
||||
// 初始化新场景
|
||||
if (!scene->isInitialized()) { // 确保只初始化一次
|
||||
scene->init();
|
||||
}
|
||||
|
||||
// 将新场景移入栈顶
|
||||
scene_stack_.push_back(std::move(scene));
|
||||
}
|
||||
|
||||
void SceneManager::popScene() {
|
||||
if (scene_stack_.empty()) {
|
||||
spdlog::warn("尝试从空场景栈中弹出。");
|
||||
return;
|
||||
}
|
||||
spdlog::debug("正在从栈中弹出场景 '{}' 。", scene_stack_.back()->getName());
|
||||
|
||||
// 清理并移除栈顶场景
|
||||
if (scene_stack_.back()) {
|
||||
scene_stack_.back()->clean(); // 显式调用清理
|
||||
}
|
||||
scene_stack_.pop_back();
|
||||
}
|
||||
|
||||
void SceneManager::replaceScene(std::unique_ptr<Scene>&& scene) {
|
||||
if (!scene) {
|
||||
spdlog::warn("尝试用空场景替换。");
|
||||
return;
|
||||
}
|
||||
spdlog::debug("正在用场景 '{}' 替换场景 '{}' 。", scene->getName(), scene_stack_.back()->getName());
|
||||
|
||||
// 清理并移除场景栈中所有场景
|
||||
while (!scene_stack_.empty()) {
|
||||
if (scene_stack_.back()) {
|
||||
scene_stack_.back()->clean();
|
||||
}
|
||||
scene_stack_.pop_back();
|
||||
}
|
||||
|
||||
// 初始化新场景
|
||||
if (!scene->isInitialized()) {
|
||||
scene->init();
|
||||
}
|
||||
|
||||
// 将新场景压入栈顶
|
||||
scene_stack_.push_back(std::move(scene));
|
||||
}
|
||||
|
||||
} // namespace engine::scene
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// 前置声明
|
||||
namespace engine::core {
|
||||
class Context;
|
||||
}
|
||||
namespace engine::scene {
|
||||
class Scene;
|
||||
}
|
||||
|
||||
namespace engine::scene {
|
||||
|
||||
/**
|
||||
* @brief 管理游戏中的场景栈,处理场景切换和生命周期。
|
||||
*/
|
||||
class SceneManager final {
|
||||
private:
|
||||
engine::core::Context& context_; ///< @brief 引擎上下文引用
|
||||
std::vector<std::unique_ptr<Scene>> scene_stack_; ///< @brief 场景栈
|
||||
|
||||
enum class PendingAction { None, Push, Pop, Replace }; ///< @brief 待处理的动作
|
||||
PendingAction pending_action_ = PendingAction::None; ///< @brief 待处理的动作
|
||||
std::unique_ptr<Scene> pending_scene_; ///< @brief 待处理场景
|
||||
|
||||
public:
|
||||
explicit SceneManager(engine::core::Context& context);
|
||||
~SceneManager();
|
||||
|
||||
// 禁止拷贝和移动
|
||||
SceneManager(const SceneManager&) = delete;
|
||||
SceneManager& operator=(const SceneManager&) = delete;
|
||||
SceneManager(SceneManager&&) = delete;
|
||||
SceneManager& operator=(SceneManager&&) = delete;
|
||||
|
||||
// 延时切换场景
|
||||
void requestPushScene(std::unique_ptr<Scene>&& scene); ///< @brief 请求压入一个新场景。
|
||||
void requestPopScene(); ///< @brief 请求弹出当前场景。
|
||||
void requestReplaceScene(std::unique_ptr<Scene>&& scene); ///< @brief 请求替换当前场景。
|
||||
|
||||
// getters
|
||||
Scene* getCurrentScene() const; ///< @brief 获取当前活动场景(栈顶场景)的指针。
|
||||
engine::core::Context& getContext() const { return context_; } ///< @brief 获取引擎上下文引用。
|
||||
|
||||
// 核心循环函数
|
||||
void update(float delta_time);
|
||||
void render();
|
||||
void handleInput();
|
||||
void close();
|
||||
|
||||
private:
|
||||
void processPendingActions(); ///< @brief 处理挂起的场景操作(每轮更新最后调用)。
|
||||
// 直接切换场景
|
||||
void pushScene(std::unique_ptr<Scene>&& scene); ///< @brief 将一个新场景压入栈顶,使其成为活动场景。
|
||||
void popScene(); ///< @brief 移除栈顶场景。
|
||||
void replaceScene(std::unique_ptr<Scene>&& scene); ///< @brief 清理场景栈所有场景,将此场景设为栈顶场景。
|
||||
|
||||
};
|
||||
|
||||
} // namespace engine::scene
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "game_scene.h"
|
||||
#include "../../engine/core/context.h"
|
||||
#include "../../engine/object/game_object.h"
|
||||
#include "../../engine/component/transform_component.h"
|
||||
#include "../../engine/component/sprite_component.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <SDL3/SDL_rect.h>
|
||||
|
||||
namespace game::scene {
|
||||
|
||||
// 构造函数:调用基类构造函数
|
||||
GameScene::GameScene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager)
|
||||
: Scene(name, context, scene_manager) {
|
||||
spdlog::trace("GameScene 构造完成。");
|
||||
}
|
||||
|
||||
void GameScene::init() {
|
||||
// 创建 test_object
|
||||
createTestObject();
|
||||
|
||||
Scene::init();
|
||||
spdlog::trace("GameScene 初始化完成。");
|
||||
}
|
||||
|
||||
void GameScene::update(float delta_time) {
|
||||
Scene::update(delta_time);
|
||||
}
|
||||
|
||||
void GameScene::render() {
|
||||
Scene::render();
|
||||
}
|
||||
|
||||
void GameScene::handleInput() {
|
||||
Scene::handleInput();
|
||||
}
|
||||
|
||||
void GameScene::clean() {
|
||||
Scene::clean();
|
||||
}
|
||||
|
||||
// --- 私有方法 ---
|
||||
|
||||
void GameScene::createTestObject() {
|
||||
spdlog::trace("在 GameScene 中创建 test_object...");
|
||||
auto test_object = std::make_unique<engine::object::GameObject>("test_object");
|
||||
|
||||
// 添加组件
|
||||
test_object->addComponent<engine::component::TransformComponent>(glm::vec2(100.0f, 100.0f));
|
||||
test_object->addComponent<engine::component::SpriteComponent>("assets/textures/Props/big-crate.png", context_.getResourceManager());
|
||||
|
||||
// 将创建好的 GameObject 添加到场景中 (一定要用std::move,否则传递的是左值)
|
||||
addGameObject(std::move(test_object));
|
||||
spdlog::trace("test_object 创建并添加到 GameScene 中。");
|
||||
}
|
||||
|
||||
} // namespace game::scene
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "../../engine/scene/scene.h"
|
||||
#include <memory>
|
||||
|
||||
// 前置声明
|
||||
namespace engine::object {
|
||||
class GameObject;
|
||||
}
|
||||
|
||||
namespace game::scene {
|
||||
|
||||
/**
|
||||
* @brief 主要的游戏场景,包含玩家、敌人、关卡元素等。
|
||||
*/
|
||||
class GameScene final: public engine::scene::Scene {
|
||||
|
||||
public:
|
||||
GameScene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager);
|
||||
|
||||
// 覆盖场景基类的核心方法
|
||||
void init() override;
|
||||
void update(float delta_time) override;
|
||||
void render() override;
|
||||
void handleInput() override;
|
||||
void clean() override;
|
||||
|
||||
private:
|
||||
// 测试函数
|
||||
void createTestObject();
|
||||
};
|
||||
|
||||
} // namespace game::scene
|
||||
Reference in New Issue
Block a user