diff --git a/Extra2D/include/extra2d/scene/scene_manager.h b/Extra2D/include/extra2d/scene/scene_manager.h index 1f3f14a..66ade15 100644 --- a/Extra2D/include/extra2d/scene/scene_manager.h +++ b/Extra2D/include/extra2d/scene/scene_manager.h @@ -12,6 +12,8 @@ namespace extra2d { struct RenderCommand; +class TransitionScene; +enum class TransitionType; /** * @brief 场景管理器 - 管理场景的生命周期和切换 @@ -22,6 +24,9 @@ public: static SceneManager &get(); + // ------------------------------------------------------------------------ + // 基本场景操作(无过渡效果) + // ------------------------------------------------------------------------ void runWithScene(Ptr scene); void replaceScene(Ptr scene); void pushScene(Ptr scene); @@ -29,6 +34,61 @@ public: void popToRootScene(); void popToScene(const std::string &name); + // ------------------------------------------------------------------------ + // 带过渡效果的场景操作 + // ------------------------------------------------------------------------ + + /** + * @brief 替换当前场景(带过渡效果) + * @param scene 新场景 + * @param transition 过渡类型 + * @param duration 过渡持续时间(秒) + */ + void replaceScene(Ptr scene, TransitionType transition, + float duration = 0.5f); + + /** + * @brief 压入新场景(带过渡效果) + * @param scene 新场景 + * @param transition 过渡类型 + * @param duration 过渡持续时间(秒) + */ + void pushScene(Ptr scene, TransitionType transition, + float duration = 0.5f); + + /** + * @brief 弹出当前场景(带过渡效果) + * @param transition 过渡类型 + * @param duration 过渡持续时间(秒) + */ + void popScene(TransitionType transition, float duration = 0.5f); + + /** + * @brief 弹出到根场景(带过渡效果) + * @param transition 过渡类型 + * @param duration 过渡持续时间(秒) + */ + void popToRootScene(TransitionType transition, float duration = 0.5f); + + /** + * @brief 弹出到指定场景(带过渡效果) + * @param name 目标场景名称 + * @param transition 过渡类型 + * @param duration 过渡持续时间(秒) + */ + void popToScene(const std::string &name, TransitionType transition, + float duration = 0.5f); + + /** + * @brief 使用自定义过渡场景进入场景 + * @param scene 新场景 + * @param transitionScene 过渡场景 + */ + void enterScene(Ptr scene, Ptr transitionScene); + + // ------------------------------------------------------------------------ + // 场景查询 + // ------------------------------------------------------------------------ Ptr getCurrentScene() const; Ptr getPreviousScene() const; Ptr getRootScene() const; @@ -38,10 +98,16 @@ public: bool isEmpty() const { return sceneStack_.empty(); } bool hasScene(const std::string &name) const; + // ------------------------------------------------------------------------ + // 更新和渲染 + // ------------------------------------------------------------------------ void update(float dt); void render(RenderBackend &renderer); void collectRenderCommands(std::vector &commands); + // ------------------------------------------------------------------------ + // 过渡状态 + // ------------------------------------------------------------------------ bool isTransitioning() const { return isTransitioning_; } void setTransitionCallback(TransitionCallback callback) { transitionCallback_ = callback; @@ -59,6 +125,32 @@ public: void enterScene(Ptr scene); private: + /** + * @brief 启动过渡 + * @param from 源场景 + * @param to 目标场景 + * @param type 过渡类型 + * @param duration 过渡持续时间 + * @param stackAction 过渡完成后的栈操作 + */ + void startTransition(Ptr from, Ptr to, TransitionType type, + float duration, std::function stackAction); + + /** + * @brief 创建过渡场景 + * @param type 过渡类型 + * @param duration 过渡持续时间 + * @param inScene 目标场景 + * @return 过渡场景智能指针 + */ + Ptr createTransitionScene(TransitionType type, float duration, + Ptr inScene); + + /** + * @brief 完成过渡 + */ + void finishTransition(); + void doSceneSwitch(); void dispatchPointerEvents(Scene &scene); @@ -71,6 +163,9 @@ private: Ptr nextScene_; bool sendCleanupToScene_ = false; + Ptr activeTransitionScene_; + std::function transitionStackAction_; + Node *hoverTarget_ = nullptr; Node *captureTarget_ = nullptr; Vec2 lastPointerWorld_ = Vec2::Zero(); diff --git a/Extra2D/include/extra2d/scene/transition_box_scene.h b/Extra2D/include/extra2d/scene/transition_box_scene.h new file mode 100644 index 0000000..0a8a298 --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_box_scene.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// 方块/马赛克过渡场景 +// 实现原理: +// 1. 将屏幕分成多个方块 +// 2. 方块逐个消失,显示新场景 +// ============================================================================ +class TransitionBoxScene : public TransitionScene { +public: + /** + * @brief 创建方块过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param divisions 方块分割数(默认为 8,表示 8x8 网格) + */ + TransitionBoxScene(float duration, Ptr inScene, int divisions = 8); + + static Ptr create(float duration, Ptr inScene, + int divisions = 8); + +protected: + void onTransitionStart() override; + void renderContent(RenderBackend &renderer) override; + +private: + int divisions_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition_fade_scene.h b/Extra2D/include/extra2d/scene/transition_fade_scene.h new file mode 100644 index 0000000..41cd7c1 --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_fade_scene.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// ============================================================================ +// 淡入淡出过渡场景 +// 实现原理: +// 1. 创建一个纯色精灵作为遮罩层 +// 2. 第一阶段:遮罩从透明淡入到不透明(黑屏),同时显示旧场景 +// 3. 切换显示新场景 +// 4. 第二阶段:遮罩从不透明淡出到透明,显示新场景 +// ============================================================================ +class TransitionFadeScene : public TransitionScene { +public: + /** + * @brief 创建淡入淡出过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param color 遮罩颜色(默认为黑色) + */ + TransitionFadeScene(float duration, Ptr inScene, + const Color &color = Colors::Black); + + static Ptr create(float duration, Ptr inScene, + const Color &color = Colors::Black); + +protected: + /** + * @brief 启动过渡动画 + * 创建遮罩层并运行动作序列 + */ + void onTransitionStart() override; + + /** + * @brief 渲染内容 + * 根据进度控制新旧场景的显示 + */ + void renderContent(RenderBackend &renderer) override; + +private: + /** + * @brief 隐藏退出场景,显示进入场景 + */ + void hideOutShowIn(); + + Color maskColor_; // 遮罩颜色 + bool hasSwitched_ = false; // 是否已经切换场景 +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition_flip_scene.h b/Extra2D/include/extra2d/scene/transition_flip_scene.h new file mode 100644 index 0000000..9a0122b --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_flip_scene.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// 翻页过渡场景 +// 实现原理: +// 1. 前半段:旧场景翻转消失 +// 2. 后半段:新场景翻转出现 +// ============================================================================ +class TransitionFlipScene : public TransitionScene { +public: + enum class Axis { Horizontal, Vertical }; + + /** + * @brief 创建翻页过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param axis 翻转轴(水平或垂直) + */ + TransitionFlipScene(float duration, Ptr inScene, + Axis axis = Axis::Horizontal); + + static Ptr create(float duration, Ptr inScene, + Axis axis = Axis::Horizontal); + +protected: + void onTransitionStart() override; + void renderContent(RenderBackend &renderer) override; + +private: + Axis axis_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition_scale_scene.h b/Extra2D/include/extra2d/scene/transition_scale_scene.h new file mode 100644 index 0000000..0f1972e --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_scale_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// 缩放过渡场景 +// 实现原理: +// 1. 旧场景缩小消失 +// 2. 新场景放大出现 +// ============================================================================ +class TransitionScaleScene : public TransitionScene { +public: + /** + * @brief 创建缩放过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + */ + TransitionScaleScene(float duration, Ptr inScene); + + static Ptr create(float duration, Ptr inScene); + +protected: + void onTransitionStart() override; + void renderContent(RenderBackend &renderer) override; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition_scene.h b/Extra2D/include/extra2d/scene/transition_scene.h new file mode 100644 index 0000000..93c9b2f --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_scene.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// ============================================================================ +// 过渡方向 +// ============================================================================ +enum class TransitionDirection { Left, Right, Up, Down }; + +// ============================================================================ +// 过渡效果类型 +// ============================================================================ +enum class TransitionType { + None, + Fade, + SlideLeft, + SlideRight, + SlideUp, + SlideDown, + Scale, + Flip, + Box +}; + +// ============================================================================ +// 过渡场景基类 - 继承自 Scene,作为中介场景管理过渡效果 +// 设计参考 Cocos2d-x 的 TransitionScene +// ============================================================================ +class TransitionScene : public Scene { +public: + using FinishCallback = std::function; + + /** + * @brief 创建过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + */ + TransitionScene(float duration, Ptr inScene); + ~TransitionScene() override = default; + + // ------------------------------------------------------------------------ + // 场景管理 + // ------------------------------------------------------------------------ + + /** + * @brief 获取要进入的场景 + */ + Ptr getInScene() const { return inScene_; } + + /** + * @brief 获取要退出的场景 + */ + Ptr getOutScene() const { return outScene_; } + + /** + * @brief 设置要退出的场景(由 SceneManager 调用) + */ + void setOutScene(Ptr outScene) { outScene_ = outScene; } + + /** + * @brief 设置过渡完成回调 + */ + void setFinishCallback(FinishCallback callback) { finishCallback_ = callback; } + + /** + * @brief 获取过渡持续时间 + */ + float getDuration() const { return duration_; } + + /** + * @brief 获取当前进度 [0, 1] + */ + float getProgress() const { return progress_; } + + /** + * @brief 是否已完成 + */ + bool isFinished() const { return isFinished_; } + + /** + * @brief 完成过渡,通知 SceneManager 切换到目标场景 + */ + void finish(); + + // ------------------------------------------------------------------------ + // 渲染 - 在 TransitionScene 上渲染新旧两个子场景 + // ------------------------------------------------------------------------ + void renderContent(RenderBackend &renderer) override; + + // ------------------------------------------------------------------------ + // 生命周期 + // ------------------------------------------------------------------------ + void onEnter() override; + void onExit() override; + +protected: + /** + * @brief 子类实现具体的过渡逻辑 + * 在 onEnter 中设置动画,动画完成后调用 finish() + */ + virtual void onTransitionStart() = 0; + + /** + * @brief 绘制源场景(旧场景) + */ + virtual void drawOutScene(RenderBackend &renderer); + + /** + * @brief 绘制目标场景(新场景) + */ + virtual void drawInScene(RenderBackend &renderer); + + float duration_; + float elapsed_ = 0.0f; + float progress_ = 0.0f; + bool isFinished_ = false; + + Ptr inScene_; // 要进入的场景 + Ptr outScene_; // 要退出的场景 + + FinishCallback finishCallback_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition_slide_scene.h b/Extra2D/include/extra2d/scene/transition_slide_scene.h new file mode 100644 index 0000000..f41e490 --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_slide_scene.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// 滑动过渡场景 +// 实现原理: +// 1. 旧场景向指定方向滑出 +// 2. 新场景从相反方向滑入 +// ============================================================================ +class TransitionSlideScene : public TransitionScene { +public: + /** + * @brief 创建滑动过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param direction 滑动方向 + */ + TransitionSlideScene(float duration, Ptr inScene, + TransitionDirection direction); + + static Ptr create(float duration, Ptr inScene, + TransitionDirection direction); + +protected: + void onTransitionStart() override; + void renderContent(RenderBackend &renderer) override; + +private: + TransitionDirection direction_; +}; + +} // namespace extra2d diff --git a/Extra2D/src/scene/scene_manager.cpp b/Extra2D/src/scene/scene_manager.cpp index 184e5af..7a9387a 100644 --- a/Extra2D/src/scene/scene_manager.cpp +++ b/Extra2D/src/scene/scene_manager.cpp @@ -4,6 +4,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include namespace extra2d { @@ -115,6 +121,48 @@ void SceneManager::replaceScene(Ptr scene) { sceneStack_.push(scene); } +/** + * @brief 替换当前场景(带过渡效果) + * @param scene 新场景 + * @param transition 过渡类型 + * @param duration 过渡持续时间 + */ +void SceneManager::replaceScene(Ptr 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 要进入的场景智能指针 @@ -133,6 +181,67 @@ void SceneManager::enterScene(Ptr scene) { } } +/** + * @brief 使用自定义过渡场景进入场景 + * @param scene 新场景 + * @param transitionScene 过渡场景 + */ +void SceneManager::enterScene(Ptr scene, + Ptr 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 要压入的场景智能指针 @@ -153,6 +262,44 @@ void SceneManager::pushScene(Ptr scene) { sceneStack_.push(scene); } +/** + * @brief 压入场景到栈顶(带过渡效果) + * @param scene 新场景 + * @param transition 过渡类型 + * @param duration 过渡持续时间 + */ +void SceneManager::pushScene(Ptr 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 弹出当前场景 * @@ -173,6 +320,41 @@ void SceneManager::popScene() { } } +/** + * @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 弹出到根场景 * @@ -193,6 +375,33 @@ void SceneManager::popToRootScene() { 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 目标场景的名称 @@ -223,6 +432,37 @@ void SceneManager::popToScene(const std::string &name) { } } +/** + * @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 @@ -381,6 +621,124 @@ void SceneManager::end() { */ void SceneManager::purgeCachedScenes() { namedScenes_.clear(); } +/** + * @brief 启动过渡 + * @param from 源场景 + * @param to 目标场景 + * @param type 过渡类型 + * @param duration 过渡持续时间 + * @param stackAction 过渡完成后的栈操作 + */ +void SceneManager::startTransition(Ptr from, Ptr to, + TransitionType type, float duration, + std::function 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 SceneManager::createTransitionScene(TransitionType type, + float duration, + Ptr 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 目标场景 @@ -472,4 +830,7 @@ void SceneManager::dispatchPointerEvents(Scene &scene) { lastPointerWorld_ = worldPos; } +void SceneManager::doSceneSwitch() { +} + } // namespace extra2d diff --git a/Extra2D/src/scene/transition_box_scene.cpp b/Extra2D/src/scene/transition_box_scene.cpp new file mode 100644 index 0000000..00ca3c3 --- /dev/null +++ b/Extra2D/src/scene/transition_box_scene.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化方块过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param divisions 方块分割数 + */ +TransitionBoxScene::TransitionBoxScene(float duration, Ptr inScene, + int divisions) + : TransitionScene(duration, inScene), divisions_(divisions) {} + +/** + * @brief 创建方块过渡场景的静态工厂方法 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param divisions 方块分割数 + * @return 过渡场景智能指针 + */ +Ptr TransitionBoxScene::create(float duration, + Ptr inScene, + int divisions) { + return makePtr(duration, inScene, divisions); +} + +/** + * @brief 启动过渡动画 + * + * 方块过渡不需要特殊的初始化 + */ +void TransitionBoxScene::onTransitionStart() { +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 先渲染新场景,然后绘制方块遮罩逐渐消失 + */ +void TransitionBoxScene::renderContent(RenderBackend &renderer) { + auto &app = Application::get(); + float windowWidth = static_cast(app.window().width()); + float windowHeight = static_cast(app.window().height()); + + elapsed_ += 1.0f / 60.0f; + progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; + + if (inScene_) { + inScene_->renderContent(renderer); + } else if (outScene_) { + outScene_->renderContent(renderer); + } + + renderer.setViewport(0, 0, static_cast(windowWidth), + static_cast(windowHeight)); + + int div = std::max(1, divisions_); + int total = div * div; + int visible = std::clamp(static_cast(total * progress_), 0, total); + + float cellW = windowWidth / static_cast(div); + float cellH = windowHeight / static_cast(div); + + glm::mat4 overlayVP = + glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); + renderer.setViewProjection(overlayVP); + + for (int idx = visible; idx < total; ++idx) { + int x = idx % div; + int y = idx / div; + renderer.fillRect(Rect(x * cellW, y * cellH, cellW + 1.0f, cellH + 1.0f), + Colors::Black); + } + + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/transition_fade_scene.cpp b/Extra2D/src/scene/transition_fade_scene.cpp new file mode 100644 index 0000000..31a9b7c --- /dev/null +++ b/Extra2D/src/scene/transition_fade_scene.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化淡入淡出过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param color 遮罩颜色 + */ +TransitionFadeScene::TransitionFadeScene(float duration, Ptr inScene, + const Color &color) + : TransitionScene(duration, inScene), maskColor_(color) {} + +/** + * @brief 创建淡入淡出过渡场景的静态工厂方法 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param color 遮罩颜色 + * @return 过渡场景智能指针 + */ +Ptr TransitionFadeScene::create(float duration, + Ptr inScene, + const Color &color) { + return makePtr(duration, inScene, color); +} + +/** + * @brief 启动过渡动画 + * + * 使用定时器来更新进度,实际进度更新由 SceneManager 的 update 驱动 + */ +void TransitionFadeScene::onTransitionStart() { + E2D_LOG_DEBUG("TransitionFadeScene::onTransitionStart - 启动淡入淡出过渡"); +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 根据进度渲染新旧场景,并绘制遮罩层 + */ +void TransitionFadeScene::renderContent(RenderBackend &renderer) { + auto &app = Application::get(); + float windowWidth = static_cast(app.window().width()); + float windowHeight = static_cast(app.window().height()); + + elapsed_ += 1.0f / 60.0f; + progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; + + if (!hasSwitched_ && progress_ >= 0.5f) { + hideOutShowIn(); + } + + if (progress_ < 0.5f) { + drawOutScene(renderer); + } else { + drawInScene(renderer); + } + + float maskAlpha; + if (progress_ < 0.5f) { + maskAlpha = progress_ * 2.0f; + } else { + maskAlpha = (1.0f - progress_) * 2.0f; + } + + renderer.setViewport(0, 0, static_cast(windowWidth), + static_cast(windowHeight)); + + glm::mat4 overlayVP = + glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); + renderer.setViewProjection(overlayVP); + + Color maskColor = maskColor_; + maskColor.a = maskAlpha; + renderer.fillRect(Rect(0.0f, 0.0f, windowWidth, windowHeight), maskColor); + + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +/** + * @brief 切换场景显示 + * + * 标记已切换场景 + */ +void TransitionFadeScene::hideOutShowIn() { + hasSwitched_ = true; + E2D_LOG_DEBUG("TransitionFadeScene::hideOutShowIn - 切换场景显示"); +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/transition_flip_scene.cpp b/Extra2D/src/scene/transition_flip_scene.cpp new file mode 100644 index 0000000..4d5c077 --- /dev/null +++ b/Extra2D/src/scene/transition_flip_scene.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化翻页过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param axis 翻转轴 + */ +TransitionFlipScene::TransitionFlipScene(float duration, Ptr inScene, + Axis axis) + : TransitionScene(duration, inScene), axis_(axis) {} + +/** + * @brief 创建翻页过渡场景的静态工厂方法 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param axis 翻转轴 + * @return 过渡场景智能指针 + */ +Ptr TransitionFlipScene::create(float duration, + Ptr inScene, + Axis axis) { + return makePtr(duration, inScene, axis); +} + +/** + * @brief 启动过渡动画 + * + * 翻页过渡不需要特殊的初始化 + */ +void TransitionFlipScene::onTransitionStart() { +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 根据进度控制新旧场景的翻转角度 + */ +void TransitionFlipScene::renderContent(RenderBackend &renderer) { + elapsed_ += 1.0f / 60.0f; + progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; + + float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_ + : -1.0f + (4.0f - 2.0f * progress_) * progress_; + + float angle = easeProgress * PI_F; + + if (progress_ < 0.5f) { + if (outScene_) { + float currentAngle = angle; + + Camera *camera = outScene_->getActiveCamera(); + float originalRotation = camera ? camera->getRotation() : 0.0f; + + if (axis_ == Axis::Horizontal) { + if (camera) { + camera->setRotation(originalRotation + currentAngle * RAD_TO_DEG); + } + } else { + if (camera) { + camera->setRotation(originalRotation - currentAngle * RAD_TO_DEG); + } + } + + outScene_->renderContent(renderer); + + if (camera) { + camera->setRotation(originalRotation); + } + } + } else { + if (inScene_) { + float currentAngle = angle - PI_F; + + Camera *camera = inScene_->getActiveCamera(); + float originalRotation = camera ? camera->getRotation() : 0.0f; + + if (axis_ == Axis::Horizontal) { + if (camera) { + camera->setRotation(originalRotation + currentAngle * RAD_TO_DEG); + } + } else { + if (camera) { + camera->setRotation(originalRotation - currentAngle * RAD_TO_DEG); + } + } + + inScene_->renderContent(renderer); + + if (camera) { + camera->setRotation(originalRotation); + } + } + } + + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/transition_scale_scene.cpp b/Extra2D/src/scene/transition_scale_scene.cpp new file mode 100644 index 0000000..663345f --- /dev/null +++ b/Extra2D/src/scene/transition_scale_scene.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化缩放过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + */ +TransitionScaleScene::TransitionScaleScene(float duration, Ptr inScene) + : TransitionScene(duration, inScene) {} + +/** + * @brief 创建缩放过渡场景的静态工厂方法 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @return 过渡场景智能指针 + */ +Ptr TransitionScaleScene::create(float duration, + Ptr inScene) { + return makePtr(duration, inScene); +} + +/** + * @brief 启动过渡动画 + * + * 缩放过渡不需要特殊的初始化 + */ +void TransitionScaleScene::onTransitionStart() { +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 根据进度控制新旧场景的缩放比例 + */ +void TransitionScaleScene::renderContent(RenderBackend &renderer) { + elapsed_ += 1.0f / 60.0f; + progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; + + float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_ + : -1.0f + (4.0f - 2.0f * progress_) * progress_; + + if (outScene_) { + float scale = std::max(0.01f, 1.0f - easeProgress); + + Camera *camera = outScene_->getActiveCamera(); + float originalZoom = camera ? camera->getZoom() : 1.0f; + + if (camera) { + camera->setZoom(originalZoom * scale); + } + + outScene_->renderContent(renderer); + + if (camera) { + camera->setZoom(originalZoom); + } + } + + if (inScene_) { + float scale = std::max(0.01f, easeProgress); + + Camera *camera = inScene_->getActiveCamera(); + float originalZoom = camera ? camera->getZoom() : 1.0f; + + if (camera) { + camera->setZoom(originalZoom * scale); + } + + inScene_->renderContent(renderer); + + if (camera) { + camera->setZoom(originalZoom); + } + } + + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/transition_scene.cpp b/Extra2D/src/scene/transition_scene.cpp new file mode 100644 index 0000000..f512e9e --- /dev/null +++ b/Extra2D/src/scene/transition_scene.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + */ +TransitionScene::TransitionScene(float duration, Ptr inScene) + : duration_(duration), inScene_(inScene) {} + +/** + * @brief 场景进入时的回调 + * + * 调用退出场景的过渡开始回调,初始化进入场景,并启动过渡 + */ +void TransitionScene::onEnter() { + Scene::onEnter(); + + if (outScene_) { + outScene_->onExitTransitionDidStart(); + } + + if (inScene_) { + inScene_->onEnter(); + inScene_->onAttachToScene(inScene_.get()); + } + + onTransitionStart(); +} + +/** + * @brief 场景退出时的回调 + * + * 调用退出场景的 onExit 和进入场景的过渡完成回调 + */ +void TransitionScene::onExit() { + if (outScene_) { + outScene_->onExit(); + outScene_->onDetachFromScene(); + } + + if (inScene_) { + inScene_->onEnterTransitionDidFinish(); + } + + Scene::onExit(); +} + +/** + * @brief 完成过渡 + * + * 标记过渡完成并调用完成回调 + */ +void TransitionScene::finish() { + if (isFinished_) { + return; + } + + isFinished_ = true; + + E2D_LOG_DEBUG("TransitionScene::finish - 过渡完成,切换到目标场景"); + + if (finishCallback_) { + finishCallback_(); + } +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 默认先渲染退出场景,再渲染进入场景 + */ +void TransitionScene::renderContent(RenderBackend &renderer) { + drawOutScene(renderer); + drawInScene(renderer); +} + +/** + * @brief 绘制退出场景 + * @param renderer 渲染后端引用 + */ +void TransitionScene::drawOutScene(RenderBackend &renderer) { + if (outScene_) { + outScene_->renderContent(renderer); + } +} + +/** + * @brief 绘制进入场景 + * @param renderer 渲染后端引用 + */ +void TransitionScene::drawInScene(RenderBackend &renderer) { + if (inScene_) { + inScene_->renderContent(renderer); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/transition_slide_scene.cpp b/Extra2D/src/scene/transition_slide_scene.cpp new file mode 100644 index 0000000..9ab1a85 --- /dev/null +++ b/Extra2D/src/scene/transition_slide_scene.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数,初始化滑动过渡场景 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param direction 滑动方向 + */ +TransitionSlideScene::TransitionSlideScene(float duration, Ptr inScene, + TransitionDirection direction) + : TransitionScene(duration, inScene), direction_(direction) {} + +/** + * @brief 创建滑动过渡场景的静态工厂方法 + * @param duration 过渡持续时间(秒) + * @param inScene 要进入的目标场景 + * @param direction 滑动方向 + * @return 过渡场景智能指针 + */ +Ptr TransitionSlideScene::create( + float duration, Ptr inScene, TransitionDirection direction) { + return makePtr(duration, inScene, direction); +} + +/** + * @brief 启动过渡动画 + * + * 滑动过渡不需要特殊的初始化 + */ +void TransitionSlideScene::onTransitionStart() { +} + +/** + * @brief 渲染过渡内容 + * @param renderer 渲染后端引用 + * + * 根据进度控制新旧场景的滑动位置 + */ +void TransitionSlideScene::renderContent(RenderBackend &renderer) { + float screenWidth = 800.0f; + float screenHeight = 600.0f; + + if (outScene_) { + Size viewportSize = outScene_->getViewportSize(); + if (viewportSize.width > 0 && viewportSize.height > 0) { + screenWidth = viewportSize.width; + screenHeight = viewportSize.height; + } + } else if (inScene_) { + Size viewportSize = inScene_->getViewportSize(); + if (viewportSize.width > 0 && viewportSize.height > 0) { + screenWidth = viewportSize.width; + screenHeight = viewportSize.height; + } + } + + elapsed_ += 1.0f / 60.0f; + progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; + + float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_ + : -1.0f + (4.0f - 2.0f * progress_) * progress_; + + if (outScene_) { + float offsetX = 0.0f; + float offsetY = 0.0f; + + switch (direction_) { + case TransitionDirection::Left: + offsetX = -screenWidth * easeProgress; + break; + case TransitionDirection::Right: + offsetX = screenWidth * easeProgress; + break; + case TransitionDirection::Up: + offsetY = -screenHeight * easeProgress; + break; + case TransitionDirection::Down: + offsetY = screenHeight * easeProgress; + break; + } + + Camera *camera = outScene_->getActiveCamera(); + Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); + + if (camera) { + camera->setPos(originalPos.x + offsetX, originalPos.y + offsetY); + } + + outScene_->renderContent(renderer); + + if (camera) { + camera->setPos(originalPos); + } + } + + if (inScene_) { + float offsetX = 0.0f; + float offsetY = 0.0f; + + switch (direction_) { + case TransitionDirection::Left: + offsetX = screenWidth * (1.0f - easeProgress); + break; + case TransitionDirection::Right: + offsetX = -screenWidth * (1.0f - easeProgress); + break; + case TransitionDirection::Up: + offsetY = screenHeight * (1.0f - easeProgress); + break; + case TransitionDirection::Down: + offsetY = -screenHeight * (1.0f - easeProgress); + break; + } + + Camera *camera = inScene_->getActiveCamera(); + Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); + + if (camera) { + camera->setPos(originalPos.x + offsetX, originalPos.y + offsetY); + } + + inScene_->renderContent(renderer); + + if (camera) { + camera->setPos(originalPos); + } + } + + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +} // namespace extra2d