Extra2D/Extra2D/src/scene/scene_manager.cpp

836 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <algorithm>
#include <extra2d/app/application.h>
#include <extra2d/graphics/render_backend.h>
#include <extra2d/graphics/render_command.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/transition_box_scene.h>
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/scene/transition_scene.h>
#include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/utils/logger.h>
namespace extra2d {
namespace {
/**
* @brief 命中测试 - 从节点树中找到最上层的可交互节点
* @param node 要测试的节点
* @param worldPos 世界坐标位置
* @return 命中的节点指针未命中返回nullptr
*/
Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
if (!node || !node->isVisible()) {
return nullptr;
}
std::vector<Ptr<Node>> children = node->getChildren();
std::stable_sort(children.begin(), children.end(),
[](const Ptr<Node> &a, const Ptr<Node> &b) {
return a->getZOrder() < b->getZOrder();
});
for (auto it = children.rbegin(); it != children.rend(); ++it) {
if (Node *hit = hitTestTopmost(*it, worldPos)) {
return hit;
}
}
if (node->getEventDispatcher().getTotalListenerCount() == 0) {
return nullptr;
}
Rect bounds = node->getBounds();
if (!bounds.empty() && bounds.containsPoint(worldPos)) {
return node.get();
}
return nullptr;
}
/**
* @brief 向节点分发事件
* @param node 目标节点
* @param event 要分发的事件
*/
void dispatchToNode(Node *node, Event &event) {
if (!node) {
return;
}
node->getEventDispatcher().dispatch(event);
}
} // namespace
/**
* @brief 获取场景管理器单例
* @return 场景管理器的全局唯一实例引用
*/
SceneManager &SceneManager::get() {
static SceneManager instance;
return instance;
}
/**
* @brief 运行指定场景
* @param scene 要运行的场景智能指针
*
* 此方法应在应用启动时调用一次,设置初始场景
*/
void SceneManager::runWithScene(Ptr<Scene> scene) {
if (!scene) {
return;
}
if (!sceneStack_.empty()) {
E2D_LOG_WARN("SceneManager: runWithScene should only be called once");
return;
}
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief 替换当前场景
* @param scene 新场景智能指针
*
* 移除当前场景并替换为新场景,场景栈大小保持不变
*/
void SceneManager::replaceScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
auto oldScene = sceneStack_.top();
oldScene->onExit();
oldScene->onDetachFromScene();
sceneStack_.pop();
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief 替换当前场景(带过渡效果)
* @param scene 新场景
* @param transition 过渡类型
* @param duration 过渡持续时间
*/
void SceneManager::replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
auto oldScene = sceneStack_.top();
startTransition(oldScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
auto outScene = activeTransitionScene_->getOutScene();
if (outScene) {
outScene->onExit();
outScene->onDetachFromScene();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/**
* @brief 进入场景
* @param scene 要进入的场景智能指针
*
* 如果场景栈为空则运行场景,否则替换当前场景
*/
void SceneManager::enterScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
} else {
replaceScene(scene);
}
}
/**
* @brief 使用自定义过渡场景进入场景
* @param scene 新场景
* @param transitionScene 过渡场景
*/
void SceneManager::enterScene(Ptr<Scene> scene,
Ptr<TransitionScene> transitionScene) {
if (!scene || isTransitioning_) {
return;
}
if (!transitionScene) {
enterScene(scene);
return;
}
auto current = getCurrentScene();
if (!current) {
enterScene(scene);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(current);
transitionScene->setFinishCallback([this]() { finishTransition(); });
current->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = [this, transitionScene]() {
auto outScene = transitionScene->getOutScene();
if (!sceneStack_.empty() && outScene) {
if (sceneStack_.top().get() == transitionScene.get()) {
sceneStack_.pop();
}
outScene->onExit();
outScene->onDetachFromScene();
}
auto inScene = transitionScene->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
};
}
/**
* @brief 压入场景到栈顶
* @param scene 要压入的场景智能指针
*
* 将新场景压入栈顶,暂停当前场景
*/
void SceneManager::pushScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (!sceneStack_.empty()) {
sceneStack_.top()->pause();
}
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief 压入场景到栈顶(带过渡效果)
* @param scene 新场景
* @param transition 过渡类型
* @param duration 过渡持续时间
*/
void SceneManager::pushScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
sceneStack_.top()->pause();
auto currentScene = sceneStack_.top();
startTransition(currentScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/**
* @brief 弹出当前场景
*
* 移除栈顶场景并恢复上一个场景
*/
void SceneManager::popScene() {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto current = sceneStack_.top();
current->onExit();
current->onDetachFromScene();
sceneStack_.pop();
if (!sceneStack_.empty()) {
sceneStack_.top()->resume();
}
}
/**
* @brief 弹出当前场景(带过渡效果)
* @param transition 过渡类型
* @param duration 过渡持续时间
*/
void SceneManager::popScene(TransitionType transition, float duration) {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto current = sceneStack_.top();
auto previous = getPreviousScene();
startTransition(current, previous, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
auto outScene = activeTransitionScene_->getOutScene();
if (outScene) {
outScene->onExit();
outScene->onDetachFromScene();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene && !sceneStack_.empty() && sceneStack_.top() == inScene) {
inScene->resume();
}
}
});
}
/**
* @brief 弹出到根场景
*
* 移除所有场景直到只剩根场景
*/
void SceneManager::popToRootScene() {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
while (sceneStack_.size() > 1) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
sceneStack_.top()->resume();
}
/**
* @brief 弹出到根场景(带过渡效果)
* @param transition 过渡类型
* @param duration 过渡持续时间
*/
void SceneManager::popToRootScene(TransitionType transition, float duration) {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto root = getRootScene();
auto current = sceneStack_.top();
startTransition(current, root, transition, duration, [this, root]() {
while (!sceneStack_.empty() && sceneStack_.top().get() != root.get()) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (!sceneStack_.empty() && sceneStack_.top().get() == root.get()) {
root->resume();
}
});
}
/**
* @brief 弹出到指定名称的场景
* @param name 目标场景的名称
*
* 移除栈顶场景直到找到指定名称的场景
*/
void SceneManager::popToScene(const std::string &name) {
if (isTransitioning_) {
return;
}
std::stack<Ptr<Scene>> tempStack;
Ptr<Scene> target = nullptr;
while (!sceneStack_.empty()) {
auto scene = sceneStack_.top();
if (scene->getName() == name) {
target = scene;
break;
}
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (target) {
target->resume();
}
}
/**
* @brief 弹出到指定名称的场景(带过渡效果)
* @param name 目标场景名称
* @param transition 过渡类型
* @param duration 过渡持续时间
*/
void SceneManager::popToScene(const std::string &name,
TransitionType transition, float duration) {
if (isTransitioning_) {
return;
}
auto target = getSceneByName(name);
if (target && target != sceneStack_.top()) {
auto current = sceneStack_.top();
startTransition(current, target, transition, duration, [this, target]() {
while (!sceneStack_.empty() && sceneStack_.top() != target) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (!sceneStack_.empty() && sceneStack_.top() == target) {
target->resume();
}
});
}
}
/**
* @brief 获取当前场景
* @return 当前栈顶场景的智能指针栈为空时返回nullptr
*/
Ptr<Scene> SceneManager::getCurrentScene() const {
if (sceneStack_.empty()) {
return nullptr;
}
return sceneStack_.top();
}
/**
* @brief 获取前一个场景
* @return 栈顶下一个场景的智能指针不存在时返回nullptr
*/
Ptr<Scene> SceneManager::getPreviousScene() const {
if (sceneStack_.size() < 2) {
return nullptr;
}
auto tempStack = sceneStack_;
tempStack.pop();
return tempStack.top();
}
/**
* @brief 获取根场景
* @return 栈底场景的智能指针栈为空时返回nullptr
*/
Ptr<Scene> SceneManager::getRootScene() const {
if (sceneStack_.empty()) {
return nullptr;
}
auto tempStack = sceneStack_;
Ptr<Scene> root;
while (!tempStack.empty()) {
root = tempStack.top();
tempStack.pop();
}
return root;
}
/**
* @brief 通过名称获取场景
* @param name 场景名称
* @return 找到的场景智能指针未找到返回nullptr
*/
Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const {
auto it = namedScenes_.find(name);
if (it != namedScenes_.end()) {
return it->second;
}
auto tempStack = sceneStack_;
while (!tempStack.empty()) {
auto scene = tempStack.top();
if (scene->getName() == name) {
return scene;
}
tempStack.pop();
}
return nullptr;
}
/**
* @brief 检查是否存在指定名称的场景
* @param name 场景名称
* @return 存在返回true否则返回false
*/
bool SceneManager::hasScene(const std::string &name) const {
return getSceneByName(name) != nullptr;
}
/**
* @brief 更新场景管理器
* @param dt 帧间隔时间(秒)
*
* 更新当前场景并分发指针事件
*/
void SceneManager::update(float dt) {
if (isTransitioning_) {
hoverTarget_ = nullptr;
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
}
if (!sceneStack_.empty()) {
auto &scene = *sceneStack_.top();
scene.updateScene(dt);
if (!isTransitioning_) {
dispatchPointerEvents(scene);
}
}
}
/**
* @brief 渲染当前场景
* @param renderer 渲染后端引用
*
* 使用当前场景的背景色清除帧缓冲并渲染场景内容
*/
void SceneManager::render(RenderBackend &renderer) {
Color clearColor = Colors::Black;
if (!sceneStack_.empty()) {
clearColor = sceneStack_.top()->getBackgroundColor();
}
E2D_LOG_TRACE("SceneManager::render - beginFrame with color({}, {}, {})",
clearColor.r, clearColor.g, clearColor.b);
renderer.beginFrame(clearColor);
if (!sceneStack_.empty()) {
E2D_LOG_TRACE("SceneManager::render - rendering scene content");
sceneStack_.top()->renderContent(renderer);
} else {
E2D_LOG_WARN("SceneManager::render - no scene to render");
}
renderer.endFrame();
E2D_LOG_TRACE("SceneManager::render - endFrame");
}
/**
* @brief 收集渲染命令
* @param commands 渲染命令输出向量
*
* 从当前场景收集所有渲染命令
*/
void SceneManager::collectRenderCommands(std::vector<RenderCommand> &commands) {
if (!sceneStack_.empty()) {
sceneStack_.top()->collectRenderCommands(commands, 0);
}
}
/**
* @brief 结束场景管理器
*
* 清空场景栈并触发所有场景的退出回调
*/
void SceneManager::end() {
while (!sceneStack_.empty()) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
namedScenes_.clear();
}
/**
* @brief 清除缓存的场景
*
* 清除命名场景缓存
*/
void SceneManager::purgeCachedScenes() { namedScenes_.clear(); }
/**
* @brief 启动过渡
* @param from 源场景
* @param to 目标场景
* @param type 过渡类型
* @param duration 过渡持续时间
* @param stackAction 过渡完成后的栈操作
*/
void SceneManager::startTransition(Ptr<Scene> from, Ptr<Scene> to,
TransitionType type, float duration,
std::function<void()> stackAction) {
if (!from || !to) {
return;
}
auto transitionScene = createTransitionScene(type, duration, to);
if (!transitionScene) {
replaceScene(to);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(from);
transitionScene->setFinishCallback([this]() { finishTransition(); });
from->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = std::move(stackAction);
}
/**
* @brief 创建过渡场景
* @param type 过渡类型
* @param duration 过渡持续时间
* @param inScene 目标场景
* @return 过渡场景智能指针
*/
Ptr<TransitionScene> SceneManager::createTransitionScene(TransitionType type,
float duration,
Ptr<Scene> inScene) {
if (!inScene) {
return nullptr;
}
switch (type) {
case TransitionType::Fade:
return TransitionFadeScene::create(duration, inScene);
case TransitionType::SlideLeft:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Left);
case TransitionType::SlideRight:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Right);
case TransitionType::SlideUp:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Up);
case TransitionType::SlideDown:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Down);
case TransitionType::Scale:
return TransitionScaleScene::create(duration, inScene);
case TransitionType::Flip:
return TransitionFlipScene::create(duration, inScene);
case TransitionType::Box:
return TransitionBoxScene::create(duration, inScene);
default:
return TransitionFadeScene::create(duration, inScene);
}
}
/**
* @brief 完成过渡
*
* 执行栈操作并清理过渡状态
*/
void SceneManager::finishTransition() {
Node *lastHoverTarget = hoverTarget_;
isTransitioning_ = false;
hoverTarget_ = nullptr;
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
if (transitionStackAction_) {
transitionStackAction_();
}
if (lastHoverTarget) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, lastHoverTarget};
dispatchToNode(lastHoverTarget, evt);
}
activeTransitionScene_.reset();
transitionStackAction_ = nullptr;
if (transitionCallback_) {
transitionCallback_();
transitionCallback_ = nullptr;
}
}
/**
* @brief 分发指针事件
* @param scene 目标场景
*
* 处理鼠标悬停、移动、点击和滚轮事件
*/
void SceneManager::dispatchPointerEvents(Scene &scene) {
auto &input = Application::get().input();
Vec2 screenPos = input.mouse();
Vec2 worldPos = screenPos;
if (auto *camera = scene.getActiveCamera()) {
worldPos = camera->screenToWorld(screenPos);
}
Ptr<Node> root = scene.shared_from_this();
Node *newHover = hitTestTopmost(root, worldPos);
if (newHover != hoverTarget_) {
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
}
hoverTarget_ = newHover;
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverEnter;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
}
}
if (!hasLastPointerWorld_) {
lastPointerWorld_ = worldPos;
hasLastPointerWorld_ = true;
}
Vec2 delta = worldPos - lastPointerWorld_;
if (hoverTarget_ && (delta.x != 0.0f || delta.y != 0.0f)) {
Event evt = Event::createMouseMove(worldPos, delta);
dispatchToNode(hoverTarget_, evt);
}
float scrollDelta = input.scrollDelta();
if (hoverTarget_ && scrollDelta != 0.0f) {
Event evt = Event::createMouseScroll(Vec2(0.0f, scrollDelta), worldPos);
dispatchToNode(hoverTarget_, evt);
}
if (input.pressed(Mouse::Left)) {
captureTarget_ = hoverTarget_;
if (captureTarget_) {
Event evt =
Event::createMousePress(static_cast<int>(Mouse::Left), 0, worldPos);
dispatchToNode(captureTarget_, evt);
Event pressed;
pressed.type = EventType::UIPress;
pressed.data = CustomEvent{0, captureTarget_};
dispatchToNode(captureTarget_, pressed);
}
}
if (input.released(Mouse::Left)) {
Node *target = captureTarget_ ? captureTarget_ : hoverTarget_;
if (target) {
Event evt =
Event::createMouseRelease(static_cast<int>(Mouse::Left), 0, worldPos);
dispatchToNode(target, evt);
Event released;
released.type = EventType::UIRelease;
released.data = CustomEvent{0, target};
dispatchToNode(target, released);
}
if (captureTarget_ && captureTarget_ == hoverTarget_) {
Event clicked;
clicked.type = EventType::UIClick;
clicked.data = CustomEvent{0, captureTarget_};
dispatchToNode(captureTarget_, clicked);
}
captureTarget_ = nullptr;
}
lastPointerWorld_ = worldPos;
}
void SceneManager::doSceneSwitch() {}
} // namespace extra2d