From b78c4935902149789c2d55f101da1805462c43e0 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Fri, 13 Feb 2026 13:56:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9C=BA=E6=99=AF=E7=B3=BB=E7=BB=9F):=20?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=9C=BA=E6=99=AF=E8=BF=87=E6=B8=A1=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=A7=86=E5=8F=A3=E9=80=82?= =?UTF-8?q?=E9=85=8D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构过渡系统为基于 TransitionScene 的场景中介模式,新增多种过渡效果 为 BaseScene 添加视口适配功能,支持居中显示游戏内容 将按钮组件扩展支持切换模式,优化 UI 系统文档 更新示例项目使用新过渡系统和视口适配 新增 TransitionScene 基类及多种过渡效果实现: - TransitionFadeScene 淡入淡出过渡 - TransitionSlideScene 滑动过渡 - TransitionScaleScene 缩放过渡 - TransitionFlipScene 翻页过渡 - TransitionBoxScene 方块过渡 BaseScene 提供视口适配功能: - 计算居中视口参数 - 处理窗口大小变化 - 支持作为 TransitionScene 子场景渲染 UI 系统改进: - Button 组件新增切换模式支持 - 合并 ToggleImageButton 功能到 Button - 更新文档和示例使用切换按钮 示例项目更新: - flappy_bird 使用新过渡系统 - push_box 实现完整场景结构和视口适配 - 更新场景切换 API 使用方式 --- Extra2D/include/extra2d/app/application.h | 2 +- Extra2D/include/extra2d/extra2d.h | 7 +- Extra2D/include/extra2d/scene/scene.h | 7 +- Extra2D/include/extra2d/scene/scene_manager.h | 31 +- Extra2D/include/extra2d/scene/transition.h | 137 ---- .../extra2d/scene/transition_box_scene.h | 34 + .../extra2d/scene/transition_fade_scene.h | 54 ++ .../extra2d/scene/transition_flip_scene.h | 37 + .../extra2d/scene/transition_scale_scene.h | 29 + .../include/extra2d/scene/transition_scene.h | 127 ++++ .../extra2d/scene/transition_slide_scene.h | 35 + Extra2D/include/extra2d/ui/button.h | 130 ++-- Extra2D/src/app/application.cpp | 4 +- Extra2D/src/scene/scene_manager.cpp | 310 ++++---- Extra2D/src/scene/transition.cpp | 396 ---------- Extra2D/src/scene/transition_box_scene.cpp | 72 ++ Extra2D/src/scene/transition_fade_scene.cpp | 88 +++ Extra2D/src/scene/transition_flip_scene.cpp | 91 +++ Extra2D/src/scene/transition_scale_scene.cpp | 71 ++ Extra2D/src/scene/transition_scene.cpp | 81 +++ Extra2D/src/scene/transition_slide_scene.cpp | 121 ++++ Extra2D/src/ui/button.cpp | 313 ++++---- docs/API_Tutorial/02_Scene_System.md | 77 +- docs/API_Tutorial/07_UI_System.md | 75 +- examples/flappy_bird/BaseScene.cpp | 54 +- examples/flappy_bird/BaseScene.h | 6 + examples/flappy_bird/SplashScene.cpp | 2 +- examples/push_box/BaseScene.cpp | 138 ++++ examples/push_box/BaseScene.h | 57 ++ examples/push_box/PlayScene.cpp | 678 +++++++++--------- examples/push_box/PlayScene.h | 57 +- examples/push_box/StartScene.cpp | 341 ++++----- examples/push_box/StartScene.h | 44 +- examples/push_box/SuccessScene.cpp | 28 +- examples/push_box/SuccessScene.h | 22 +- 35 files changed, 2275 insertions(+), 1481 deletions(-) delete mode 100644 Extra2D/include/extra2d/scene/transition.h create mode 100644 Extra2D/include/extra2d/scene/transition_box_scene.h create mode 100644 Extra2D/include/extra2d/scene/transition_fade_scene.h create mode 100644 Extra2D/include/extra2d/scene/transition_flip_scene.h create mode 100644 Extra2D/include/extra2d/scene/transition_scale_scene.h create mode 100644 Extra2D/include/extra2d/scene/transition_scene.h create mode 100644 Extra2D/include/extra2d/scene/transition_slide_scene.h delete mode 100644 Extra2D/src/scene/transition.cpp create mode 100644 Extra2D/src/scene/transition_box_scene.cpp create mode 100644 Extra2D/src/scene/transition_fade_scene.cpp create mode 100644 Extra2D/src/scene/transition_flip_scene.cpp create mode 100644 Extra2D/src/scene/transition_scale_scene.cpp create mode 100644 Extra2D/src/scene/transition_scene.cpp create mode 100644 Extra2D/src/scene/transition_slide_scene.cpp create mode 100644 examples/push_box/BaseScene.cpp create mode 100644 examples/push_box/BaseScene.h diff --git a/Extra2D/include/extra2d/app/application.h b/Extra2D/include/extra2d/app/application.h index ac23673..e4b48b6 100644 --- a/Extra2D/include/extra2d/app/application.h +++ b/Extra2D/include/extra2d/app/application.h @@ -90,7 +90,7 @@ public: // 便捷方法 // ------------------------------------------------------------------------ void enterScene(Ptr scene); - void enterScene(Ptr scene, Ptr transition); + void enterScene(Ptr scene, Ptr transitionScene); float deltaTime() const { return deltaTime_; } float totalTime() const { return totalTime_; } diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index f8fe2b8..293647e 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -29,7 +29,12 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include // Animation #include diff --git a/Extra2D/include/extra2d/scene/scene.h b/Extra2D/include/extra2d/scene/scene.h index 053ce36..9d0a3d6 100644 --- a/Extra2D/include/extra2d/scene/scene.h +++ b/Extra2D/include/extra2d/scene/scene.h @@ -56,7 +56,7 @@ public: // 渲染和更新 // ------------------------------------------------------------------------ void renderScene(RenderBackend &renderer); - void renderContent(RenderBackend &renderer); + virtual void renderContent(RenderBackend &renderer); void updateScene(float dt); void collectRenderCommands(std::vector &commands, int parentZOrder = 0) override; @@ -92,7 +92,12 @@ protected: void onEnter() override; void onExit() override; + // 过渡场景生命周期回调(供 TransitionScene 使用) + virtual void onExitTransitionDidStart() {} + virtual void onEnterTransitionDidFinish() {} + friend class SceneManager; + friend class TransitionScene; private: Color backgroundColor_ = Colors::Black; diff --git a/Extra2D/include/extra2d/scene/scene_manager.h b/Extra2D/include/extra2d/scene/scene_manager.h index bbe19d8..0b67928 100644 --- a/Extra2D/include/extra2d/scene/scene_manager.h +++ b/Extra2D/include/extra2d/scene/scene_manager.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -12,21 +13,7 @@ namespace extra2d { // 前向声明 struct RenderCommand; -class Transition; - -// ============================================================================ -// 场景切换特效类型 -// ============================================================================ -enum class TransitionType { - None, - Fade, - SlideLeft, - SlideRight, - SlideUp, - SlideDown, - Scale, - Flip -}; +class TransitionScene; // ============================================================================ // 场景管理器 - 管理场景的生命周期和切换 @@ -116,27 +103,27 @@ public: // 场景切换(供 Application 使用) void enterScene(Ptr scene); - void enterScene(Ptr scene, Ptr transition); + void enterScene(Ptr scene, Ptr transitionScene); private: void doSceneSwitch(); void startTransition(Ptr from, Ptr to, TransitionType type, float duration, Function stackAction); - void updateTransition(float dt); void finishTransition(); void dispatchPointerEvents(Scene &scene); + // 创建过渡场景 + Ptr createTransitionScene(TransitionType type, + float duration, + Ptr inScene); + std::stack> sceneStack_; std::unordered_map> namedScenes_; // Transition state bool isTransitioning_ = false; TransitionType currentTransition_ = TransitionType::None; - float transitionDuration_ = 0.0f; - float transitionElapsed_ = 0.0f; - Ptr outgoingScene_; - Ptr incomingScene_; - Ptr activeTransition_; + Ptr activeTransitionScene_; Function transitionStackAction_; TransitionCallback transitionCallback_; diff --git a/Extra2D/include/extra2d/scene/transition.h b/Extra2D/include/extra2d/scene/transition.h deleted file mode 100644 index 0c2353f..0000000 --- a/Extra2D/include/extra2d/scene/transition.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 过渡方向 -// ============================================================================ -enum class TransitionDirection { Left, Right, Up, Down }; - -// ============================================================================ -// 过渡效果基类 -// ============================================================================ -class Transition : public std::enable_shared_from_this { -public: - using FinishCallback = std::function; - - Transition(float duration); - virtual ~Transition() = default; - - // 开始过渡 - void start(Ptr from, Ptr to); - - // 更新过渡进度 - void update(float dt); - - // 渲染过渡效果 - virtual void render(RenderBackend &renderer); - - // 是否完成 - bool isFinished() const { return isFinished_; } - - // 获取进度 [0, 1] - float getProgress() const { return progress_; } - - // 获取淡入淡出进度 (0->1 for fade in, 1->0 for fade out) - float getFadeInAlpha() const; - float getFadeOutAlpha() const; - - // 设置完成回调 - void setFinishCallback(FinishCallback callback) { - finishCallback_ = callback; - } - - // 获取源场景和目标场景 - Ptr getOutgoingScene() const { return outgoingScene_; } - Ptr getIncomingScene() const { return incomingScene_; } - -protected: - // 子类实现具体的渲染效果 - virtual void onRenderTransition(RenderBackend &renderer, float progress) = 0; - - // 过渡完成时调用 - virtual void onFinish(); - - float duration_; - float elapsed_; - float progress_; - bool isFinished_; - bool isStarted_; - - Ptr outgoingScene_; - Ptr incomingScene_; - FinishCallback finishCallback_; -}; - -// ============================================================================ -// 淡入淡出过渡 -// ============================================================================ -class FadeTransition : public Transition { -public: - FadeTransition(float duration); - -protected: - void onRenderTransition(RenderBackend &renderer, float progress) override; -}; - -// ============================================================================ -// 滑动过渡 -// ============================================================================ -class SlideTransition : public Transition { -public: - SlideTransition(float duration, TransitionDirection direction); - -protected: - void onRenderTransition(RenderBackend &renderer, float progress) override; - -private: - TransitionDirection direction_; -}; - -// ============================================================================ -// 缩放过渡 -// ============================================================================ -class ScaleTransition : public Transition { -public: - ScaleTransition(float duration); - -protected: - void onRenderTransition(RenderBackend &renderer, float progress) override; -}; - -// ============================================================================ -// 翻页过渡 -// ============================================================================ -class FlipTransition : public Transition { -public: - enum class Axis { Horizontal, Vertical }; - - FlipTransition(float duration, Axis axis = Axis::Horizontal); - -protected: - void onRenderTransition(RenderBackend &renderer, float progress) override; - -private: - Axis axis_; -}; - -// ============================================================================ -// 马赛克/方块过渡 -// ============================================================================ -class BoxTransition : public Transition { -public: - BoxTransition(float duration, int divisions = 8); - -protected: - void onRenderTransition(RenderBackend &renderer, float progress) override; - -private: - int divisions_; -}; - -} // namespace extra2d 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..095724c --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition_fade_scene.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#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/include/extra2d/ui/button.h b/Extra2D/include/extra2d/ui/button.h index afea3c9..4e1fa1c 100644 --- a/Extra2D/include/extra2d/ui/button.h +++ b/Extra2D/include/extra2d/ui/button.h @@ -95,6 +95,8 @@ public: // 边框设置 // ------------------------------------------------------------------------ void setBorder(const Color &color, float width); + float getBorderWidth() const { return borderWidth_; } + Color getBorderColor() const { return borderColor_; } // ------------------------------------------------------------------------ // 图片背景设置 @@ -102,6 +104,22 @@ public: void setBackgroundImage(Ptr normal, Ptr hover = nullptr, Ptr pressed = nullptr); void setBackgroundImage(Ptr texture, const Rect &rect); + + /** + * @brief 为切换按钮的两种状态设置图片背景 + * @param offNormal 关闭状态的普通图片 + * @param onNormal 开启状态的普通图片 + * @param offHover 关闭状态的悬停图片(可选) + * @param onHover 开启状态的悬停图片(可选) + * @param offPressed 关闭状态的按下图片(可选) + * @param onPressed 开启状态的按下图片(可选) + */ + void setStateBackgroundImage(Ptr offNormal, Ptr onNormal, + Ptr offHover = nullptr, + Ptr onHover = nullptr, + Ptr offPressed = nullptr, + Ptr onPressed = nullptr); + void setBackgroundImageScaleMode(ImageScaleMode mode); void setCustomSize(const Vec2 &size); void setCustomSize(float width, float height); @@ -131,6 +149,63 @@ public: // ------------------------------------------------------------------------ void setOnClick(Function callback); + // ------------------------------------------------------------------------ + // 切换模式支持(Toggle Button) + // ------------------------------------------------------------------------ + + /** + * @brief 设置是否为切换模式 + * @param enabled true 表示启用切换模式,点击时自动切换 on/off 状态 + */ + void setToggleMode(bool enabled); + + /** + * @brief 获取当前是否为切换模式 + * @return true 表示处于切换模式 + */ + bool isToggleMode() const { return toggleMode_; } + + /** + * @brief 设置当前状态(仅切换模式有效) + * @param on true 表示开启状态,false 表示关闭状态 + */ + void setOn(bool on); + + /** + * @brief 获取当前状态 + * @return true 表示开启状态,false 表示关闭状态 + */ + bool isOn() const { return isOn_; } + + /** + * @brief 切换当前状态 + */ + void toggle(); + + /** + * @brief 设置状态改变回调 + * @param callback 状态改变时调用的回调函数,参数为新状态 + */ + void setOnStateChange(Function callback); + + // ------------------------------------------------------------------------ + // 状态文字设置(用于切换按钮) + // ------------------------------------------------------------------------ + + /** + * @brief 为两种状态设置不同的文字 + * @param textOff 关闭状态显示的文字 + * @param textOn 开启状态显示的文字 + */ + void setStateText(const std::string &textOff, const std::string &textOn); + + /** + * @brief 为两种状态设置不同的文字颜色 + * @param colorOff 关闭状态的文字颜色 + * @param colorOn 开启状态的文字颜色 + */ + void setStateTextColor(const Color &colorOff, const Color &colorOn); + Rect getBoundingBox() const override; protected: @@ -170,6 +245,12 @@ private: bool useImageBackground_ = false; bool useTextureRect_ = false; + // 切换按钮状态图片 + Ptr imgOffNormal_, imgOnNormal_; + Ptr imgOffHover_, imgOnHover_; + Ptr imgOffPressed_, imgOnPressed_; + bool useStateImages_ = false; + // 边框 Color borderColor_ = Color(0.6f, 0.6f, 0.6f, 1.0f); float borderWidth_ = 1.0f; @@ -188,48 +269,10 @@ private: bool hovered_ = false; bool pressed_ = false; - Function onClick_; -}; - -// ============================================================================ -// 切换按钮 - 点击切换两种状态(支持图片和文字) -// ============================================================================ -class ToggleImageButton : public Button { -public: - ToggleImageButton(); - ~ToggleImageButton() override = default; - - static Ptr create(); - - // 设置两种状态的图片 - void setStateImages(Ptr stateOffNormal, Ptr stateOnNormal, - Ptr stateOffHover = nullptr, - Ptr stateOnHover = nullptr, - Ptr stateOffPressed = nullptr, - Ptr stateOnPressed = nullptr); - - // 设置两种状态的文字 - void setStateText(const std::string &textOff, const std::string &textOn); - - // 设置两种状态的文字颜色 - void setStateTextColor(const Color &colorOff, const Color &colorOn); - - // 获取/设置当前状态 - bool isOn() const { return isOn_; } - void setOn(bool on); - void toggle(); - - // 设置状态改变回调 - void setOnStateChange(Function callback); - -protected: - void onDrawWidget(RenderBackend &renderer) override; - -private: - // 状态图片 - Ptr imgOffNormal_, imgOnNormal_; - Ptr imgOffHover_, imgOnHover_; - Ptr imgOffPressed_, imgOnPressed_; + // 切换模式相关 + bool toggleMode_ = false; + bool isOn_ = false; + Function onStateChange_; // 状态文字 std::string textOff_, textOn_; @@ -240,8 +283,7 @@ private: Color textColorOn_ = Colors::White; bool useStateTextColor_ = false; - bool isOn_ = false; - Function onStateChange_; + Function onClick_; }; } // namespace extra2d diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index d46598d..7ce037a 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -364,11 +364,11 @@ Camera &Application::camera() { return *camera_; } void Application::enterScene(Ptr scene) { enterScene(scene, nullptr); } void Application::enterScene(Ptr scene, - Ptr transition) { + Ptr transitionScene) { if (sceneManager_ && scene) { scene->setViewportSize(static_cast(window_->getWidth()), static_cast(window_->getHeight())); - sceneManager_->enterScene(scene, transition); + sceneManager_->enterScene(scene, transitionScene); } } diff --git a/Extra2D/src/scene/scene_manager.cpp b/Extra2D/src/scene/scene_manager.cpp index 8d7b1c8..8a8f82c 100644 --- a/Extra2D/src/scene/scene_manager.cpp +++ b/Extra2D/src/scene/scene_manager.cpp @@ -4,57 +4,16 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include #include namespace extra2d { -// ============================================================================ -// Transition 工厂映射 - 使用函数指针数组替代 switch -// ============================================================================ -using TransitionFactory = Ptr (*)(float); - -static Ptr createFadeTransition(float duration) { - return makePtr(duration); -} - -static Ptr createSlideLeftTransition(float duration) { - return makePtr(duration, TransitionDirection::Left); -} - -static Ptr createSlideRightTransition(float duration) { - return makePtr(duration, TransitionDirection::Right); -} - -static Ptr createSlideUpTransition(float duration) { - return makePtr(duration, TransitionDirection::Up); -} - -static Ptr createSlideDownTransition(float duration) { - return makePtr(duration, TransitionDirection::Down); -} - -static Ptr createScaleTransition(float duration) { - return makePtr(duration); -} - -static Ptr createFlipTransition(float duration) { - return makePtr(duration); -} - -// 工厂函数指针数组,索引对应 TransitionType 枚举值 -static constexpr TransitionFactory TRANSITION_FACTORIES[] = { - createFadeTransition, // TransitionType::Fade = 0 - createSlideLeftTransition, // TransitionType::SlideLeft = 1 - createSlideRightTransition,// TransitionType::SlideRight = 2 - createSlideUpTransition, // TransitionType::SlideUp = 3 - createSlideDownTransition, // TransitionType::SlideDown = 4 - createScaleTransition, // TransitionType::Scale = 5 - createFlipTransition // TransitionType::Flip = 6 -}; - -static constexpr size_t TRANSITION_FACTORY_COUNT = sizeof(TRANSITION_FACTORIES) / sizeof(TRANSITION_FACTORIES[0]); - namespace { Node *hitTestTopmost(const Ptr &node, const Vec2 &worldPos) { @@ -150,17 +109,22 @@ void SceneManager::enterScene(Ptr scene) { } void SceneManager::enterScene(Ptr scene, - Ptr transition) { + Ptr transitionScene) { if (!scene || isTransitioning_) { return; } - if (!transition) { + // 如果没有过渡场景,使用无过渡切换 + if (!transitionScene) { enterScene(scene); return; } auto current = getCurrentScene(); + if (!current) { + enterScene(scene); + return; + } // 在过渡开始前,发送 UIHoverExit 给当前悬停的节点,重置按钮状态 if (hoverTarget_) { @@ -173,32 +137,39 @@ void SceneManager::enterScene(Ptr scene, captureTarget_ = nullptr; hasLastPointerWorld_ = false; - transition->start(current, scene); - outgoingScene_ = current; - incomingScene_ = scene; - activeTransition_ = transition; - currentTransition_ = TransitionType::None; + // 设置过渡场景 + transitionScene->setOutScene(current); + transitionScene->setFinishCallback([this]() { finishTransition(); }); + + // 暂停当前场景 + current->pause(); + + // 推入过渡场景(作为中介场景) + transitionScene->onEnter(); + transitionScene->onAttachToScene(transitionScene.get()); + sceneStack_.push(transitionScene); + isTransitioning_ = true; - transitionStackAction_ = [this]() { + activeTransitionScene_ = transitionScene; + transitionStackAction_ = [this, transitionScene]() { // 退出旧场景 - auto outgoing = outgoingScene_; - if (!sceneStack_.empty() && outgoing && sceneStack_.top() == outgoing) { - outgoing->onExit(); - outgoing->onDetachFromScene(); - sceneStack_.pop(); - } - // 推入新场景并调用 onEnter - auto incoming = incomingScene_; - if (incoming) { - sceneStack_.push(incoming); - if (!incoming->isRunning()) { - incoming->onEnter(); - incoming->onAttachToScene(incoming.get()); + 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); } }; - // 注意:不在此处调用新场景的 onEnter,由 transitionStackAction_ - // 在过渡完成后调用 } void SceneManager::replaceScene(Ptr scene, TransitionType transition, @@ -216,19 +187,26 @@ void SceneManager::replaceScene(Ptr scene, TransitionType transition, startTransition(oldScene, scene, transition, duration, [this]() { // 过渡完成后,退出旧场景并从堆栈中移除 - auto outgoing = outgoingScene_; - auto incoming = incomingScene_; - if (!sceneStack_.empty() && outgoing && sceneStack_.top() == outgoing) { - outgoing->onExit(); - outgoing->onDetachFromScene(); - sceneStack_.pop(); + if (!sceneStack_.empty() && activeTransitionScene_) { + // 弹出过渡场景 + if (sceneStack_.top().get() == activeTransitionScene_.get()) { + sceneStack_.pop(); + } + + // 退出旧场景 + auto outScene = activeTransitionScene_->getOutScene(); + if (outScene) { + outScene->onExit(); + outScene->onDetachFromScene(); + } } - // 将新场景推入堆栈并调用 onEnter - if (incoming) { - sceneStack_.push(incoming); - if (!incoming->isRunning()) { - incoming->onEnter(); - incoming->onAttachToScene(incoming.get()); + + // 将新场景推入堆栈 + if (activeTransitionScene_) { + auto inScene = activeTransitionScene_->getInScene(); + if (inScene) { + inScene->onAttachToScene(inScene.get()); + sceneStack_.push(inScene); } } }); @@ -267,12 +245,19 @@ void SceneManager::pushScene(Ptr scene, TransitionType transition, auto currentScene = sceneStack_.top(); startTransition(currentScene, scene, transition, duration, [this]() { - // 过渡完成后,将新场景推入堆栈并调用 onEnter - if (incomingScene_) { - sceneStack_.push(incomingScene_); - if (!incomingScene_->isRunning()) { - incomingScene_->onEnter(); - incomingScene_->onAttachToScene(incomingScene_.get()); + // 过渡完成后,将新场景推入堆栈 + 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); } } }); @@ -304,20 +289,26 @@ void SceneManager::popScene(TransitionType transition, float duration) { startTransition(current, previous, transition, duration, [this]() { // 过渡完成后,退出当前场景并从堆栈中移除 - auto outgoing = outgoingScene_; - if (!sceneStack_.empty() && outgoing && sceneStack_.top() == outgoing) { - outgoing->onExit(); - outgoing->onDetachFromScene(); - sceneStack_.pop(); - } - // 恢复前一个场景 - auto incoming = incomingScene_; - if (!sceneStack_.empty() && incoming && sceneStack_.top() == incoming) { - if (!incoming->isRunning()) { - incoming->onEnter(); - incoming->onAttachToScene(incoming.get()); + 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(); } - incoming->resume(); } }); } @@ -349,18 +340,15 @@ void SceneManager::popToRootScene(TransitionType transition, float duration) { startTransition(current, root, transition, duration, [this, root]() { // 退出所有场景直到根场景 - while (!sceneStack_.empty() && sceneStack_.top() != root) { + while (!sceneStack_.empty() && sceneStack_.top().get() != root.get()) { auto scene = sceneStack_.top(); scene->onExit(); scene->onDetachFromScene(); sceneStack_.pop(); } + // 恢复根场景 - if (!sceneStack_.empty() && sceneStack_.top() == root) { - if (!root->isRunning()) { - root->onEnter(); - root->onAttachToScene(root.get()); - } + if (!sceneStack_.empty() && sceneStack_.top().get() == root.get()) { root->resume(); } }); @@ -409,12 +397,9 @@ void SceneManager::popToScene(const std::string &name, scene->onDetachFromScene(); sceneStack_.pop(); } + // 恢复目标场景 if (!sceneStack_.empty() && sceneStack_.top() == target) { - if (!target->isRunning()) { - target->onEnter(); - target->onAttachToScene(target.get()); - } target->resume(); } }); @@ -479,7 +464,7 @@ bool SceneManager::hasScene(const std::string &name) const { void SceneManager::update(float dt) { if (isTransitioning_) { - updateTransition(dt); + // 过渡场景在栈顶,正常更新即可 hoverTarget_ = nullptr; captureTarget_ = nullptr; hasLastPointerWorld_ = false; @@ -496,13 +481,7 @@ void SceneManager::update(float dt) { void SceneManager::render(RenderBackend &renderer) { Color clearColor = Colors::Black; - if (isTransitioning_) { - if (outgoingScene_) { - clearColor = outgoingScene_->getBackgroundColor(); - } else if (incomingScene_) { - clearColor = incomingScene_->getBackgroundColor(); - } - } else if (!sceneStack_.empty()) { + if (!sceneStack_.empty()) { clearColor = sceneStack_.top()->getBackgroundColor(); } @@ -510,10 +489,7 @@ void SceneManager::render(RenderBackend &renderer) { clearColor.r, clearColor.g, clearColor.b); renderer.beginFrame(clearColor); - if (isTransitioning_ && activeTransition_) { - E2D_LOG_TRACE("SceneManager::render - rendering transition"); - activeTransition_->render(renderer); - } else if (!sceneStack_.empty()) { + if (!sceneStack_.empty()) { E2D_LOG_TRACE("SceneManager::render - rendering scene content"); sceneStack_.top()->renderContent(renderer); } else { @@ -525,13 +501,7 @@ void SceneManager::render(RenderBackend &renderer) { } void SceneManager::collectRenderCommands(std::vector &commands) { - if (isTransitioning_ && outgoingScene_) { - // During transition, collect commands from both scenes - outgoingScene_->collectRenderCommands(commands, 0); - if (incomingScene_) { - incomingScene_->collectRenderCommands(commands, 0); - } - } else if (!sceneStack_.empty()) { + if (!sceneStack_.empty()) { sceneStack_.top()->collectRenderCommands(commands, 0); } } @@ -555,18 +525,14 @@ void SceneManager::startTransition(Ptr from, Ptr to, return; } - // 使用工厂映射替代 switch - Ptr transition; - size_t typeIndex = static_cast(type); - if (typeIndex < TRANSITION_FACTORY_COUNT) { - transition = TRANSITION_FACTORIES[typeIndex](duration); - } else { - // 默认使用 Fade 过渡 - transition = TRANSITION_FACTORIES[0](duration); + // 创建过渡场景 + auto transitionScene = createTransitionScene(type, duration, to); + if (!transitionScene) { + // 回退到无过渡切换 + replaceScene(to); + return; } - transition->start(from, to); - // 在过渡开始前,发送 UIHoverExit 给当前悬停的节点,重置按钮状态 if (hoverTarget_) { Event evt; @@ -578,29 +544,55 @@ void SceneManager::startTransition(Ptr from, Ptr to, captureTarget_ = nullptr; hasLastPointerWorld_ = false; + // 设置过渡场景 + transitionScene->setOutScene(from); + transitionScene->setFinishCallback([this]() { finishTransition(); }); + + // 暂停当前场景 + from->pause(); + + // 推入过渡场景(作为中介场景) + transitionScene->onEnter(); + transitionScene->onAttachToScene(transitionScene.get()); + sceneStack_.push(transitionScene); + isTransitioning_ = true; currentTransition_ = type; - transitionDuration_ = duration; - transitionElapsed_ = 0.0f; - outgoingScene_ = from; - incomingScene_ = to; - activeTransition_ = transition; + activeTransitionScene_ = transitionScene; transitionStackAction_ = std::move(stackAction); - - // 注意:不在此处调用新场景的 onEnter,由 transitionStackAction_ - // 在过渡完成后调用 } -void SceneManager::updateTransition(float dt) { - transitionElapsed_ += dt; +Ptr SceneManager::createTransitionScene(TransitionType type, + float duration, + Ptr inScene) { + if (!inScene) { + return nullptr; + } - if (activeTransition_) { - activeTransition_->update(dt); - if (activeTransition_->isFinished()) { - finishTransition(); - } - } else { - finishTransition(); + 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); } } @@ -625,9 +617,7 @@ void SceneManager::finishTransition() { dispatchToNode(lastHoverTarget, evt); } - outgoingScene_.reset(); - incomingScene_.reset(); - activeTransition_.reset(); + activeTransitionScene_.reset(); transitionStackAction_ = nullptr; if (transitionCallback_) { diff --git a/Extra2D/src/scene/transition.cpp b/Extra2D/src/scene/transition.cpp deleted file mode 100644 index 531602a..0000000 --- a/Extra2D/src/scene/transition.cpp +++ /dev/null @@ -1,396 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 缓动函数 -// ============================================================================ -static float easeInOutQuad(float t) { - return t < 0.5f ? 2.0f * t * t : -1.0f + (4.0f - 2.0f * t) * t; -} - -static float easeOutQuad(float t) { return t * (2.0f - t); } - -// ============================================================================ -// Transition 基类 -// ============================================================================ -Transition::Transition(float duration) - : duration_(duration), elapsed_(0.0f), progress_(0.0f), isFinished_(false), - isStarted_(false) {} - -void Transition::start(Ptr from, Ptr to) { - outgoingScene_ = from; - incomingScene_ = to; - elapsed_ = 0.0f; - progress_ = 0.0f; - isFinished_ = false; - isStarted_ = true; -} - -void Transition::update(float dt) { - if (!isStarted_ || isFinished_) { - return; - } - - elapsed_ += dt; - progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f; - - if (progress_ >= 1.0f) { - onFinish(); - } -} - -void Transition::render(RenderBackend &renderer) { - if (!isStarted_ || isFinished_) { - return; - } - - onRenderTransition(renderer, easeInOutQuad(progress_)); -} - -float Transition::getFadeInAlpha() const { return easeOutQuad(progress_); } - -float Transition::getFadeOutAlpha() const { - return 1.0f - easeOutQuad(progress_); -} - -void Transition::onFinish() { - isFinished_ = true; - if (finishCallback_) { - finishCallback_(); - } -} - -// ============================================================================ -// FadeTransition - 淡入淡出 -// ============================================================================ -FadeTransition::FadeTransition(float duration) : Transition(duration) {} - -void FadeTransition::onRenderTransition(RenderBackend &renderer, - float progress) { - float screenWidth = 800.0f; - float screenHeight = 600.0f; - - if (outgoingScene_) { - Size viewportSize = outgoingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } else if (incomingScene_) { - Size viewportSize = incomingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } - - glm::mat4 overlayVP = - glm::ortho(0.0f, screenWidth, screenHeight, 0.0f, -1.0f, 1.0f); - - if (progress < 0.5f) { - if (outgoingScene_) { - outgoingScene_->renderContent(renderer); - } - float a = std::clamp(progress * 2.0f, 0.0f, 1.0f); - renderer.setViewProjection(overlayVP); - renderer.fillRect(Rect(0.0f, 0.0f, screenWidth, screenHeight), - Color(0.0f, 0.0f, 0.0f, a)); - } else { - if (incomingScene_) { - incomingScene_->renderContent(renderer); - } - float a = std::clamp((1.0f - progress) * 2.0f, 0.0f, 1.0f); - renderer.setViewProjection(overlayVP); - renderer.fillRect(Rect(0.0f, 0.0f, screenWidth, screenHeight), - Color(0.0f, 0.0f, 0.0f, a)); - } -} - -// ============================================================================ -// SlideTransition - 滑动 -// ============================================================================ -SlideTransition::SlideTransition(float duration, TransitionDirection direction) - : Transition(duration), direction_(direction) {} - -void SlideTransition::onRenderTransition(RenderBackend &renderer, - float progress) { - // 获取视口尺寸 - float screenWidth = 800.0f; - float screenHeight = 600.0f; - - if (outgoingScene_) { - Size viewportSize = outgoingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } else if (incomingScene_) { - Size viewportSize = incomingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } - - // 渲染源场景(滑出) - if (outgoingScene_) { - float offsetX = 0.0f; - float offsetY = 0.0f; - - switch (direction_) { - case TransitionDirection::Left: - offsetX = -screenWidth * progress; - break; - case TransitionDirection::Right: - offsetX = screenWidth * progress; - break; - case TransitionDirection::Up: - offsetY = -screenHeight * progress; - break; - case TransitionDirection::Down: - offsetY = screenHeight * progress; - break; - } - - // 保存原始相机位置 - Camera *camera = outgoingScene_->getActiveCamera(); - Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); - - // 应用偏移 - if (camera) { - camera->setPosition(originalPos.x + offsetX, originalPos.y + offsetY); - } - - // 渲染场景 - outgoingScene_->renderContent(renderer); - - // 恢复相机位置 - if (camera) { - camera->setPosition(originalPos); - } - } - - // 渲染目标场景(滑入) - if (incomingScene_) { - float offsetX = 0.0f; - float offsetY = 0.0f; - - switch (direction_) { - case TransitionDirection::Left: - offsetX = screenWidth * (1.0f - progress); - break; - case TransitionDirection::Right: - offsetX = -screenWidth * (1.0f - progress); - break; - case TransitionDirection::Up: - offsetY = screenHeight * (1.0f - progress); - break; - case TransitionDirection::Down: - offsetY = -screenHeight * (1.0f - progress); - break; - } - - // 保存原始相机位置 - Camera *camera = incomingScene_->getActiveCamera(); - Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); - - // 应用偏移 - if (camera) { - camera->setPosition(originalPos.x + offsetX, originalPos.y + offsetY); - } - - // 渲染场景 - incomingScene_->renderContent(renderer); - - // 恢复相机位置 - if (camera) { - camera->setPosition(originalPos); - } - } -} - -// ============================================================================ -// ScaleTransition - 缩放 -// ============================================================================ -ScaleTransition::ScaleTransition(float duration) : Transition(duration) {} - -void ScaleTransition::onRenderTransition(RenderBackend &renderer, - float progress) { - // 源场景:缩小消失 - if (outgoingScene_) { - float scale = std::max(0.01f, 1.0f - progress); - - // 保存原始相机状态 - Camera *camera = outgoingScene_->getActiveCamera(); - float originalZoom = camera ? camera->getZoom() : 1.0f; - Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); - - // 应用缩放(通过调整相机 zoom 实现) - if (camera) { - camera->setZoom(originalZoom * scale); - } - - // 渲染场景 - outgoingScene_->renderContent(renderer); - - // 恢复相机状态 - if (camera) { - camera->setZoom(originalZoom); - camera->setPosition(originalPos); - } - } - - // 目标场景:放大出现 - if (incomingScene_) { - float scale = std::max(0.01f, progress); - - // 保存原始相机状态 - Camera *camera = incomingScene_->getActiveCamera(); - float originalZoom = camera ? camera->getZoom() : 1.0f; - Vec2 originalPos = camera ? camera->getPosition() : Vec2::Zero(); - - // 应用缩放 - if (camera) { - camera->setZoom(originalZoom * scale); - } - - // 渲染场景 - incomingScene_->renderContent(renderer); - - // 恢复相机状态 - if (camera) { - camera->setZoom(originalZoom); - camera->setPosition(originalPos); - } - } -} - -// ============================================================================ -// FlipTransition - 翻页 -// ============================================================================ -FlipTransition::FlipTransition(float duration, Axis axis) - : Transition(duration), axis_(axis) {} - -void FlipTransition::onRenderTransition(RenderBackend &renderer, - float progress) { - float angle = progress * PI_F; // 180度翻转 - - if (progress < 0.5f) { - // 前半段:翻转源场景 - if (outgoingScene_) { - float currentAngle = angle; - - // 保存原始相机状态 - Camera *camera = outgoingScene_->getActiveCamera(); - float originalRotation = camera ? camera->getRotation() : 0.0f; - - // 应用旋转(水平翻转绕Y轴,垂直翻转绕X轴) - if (axis_ == Axis::Horizontal) { - // 水平轴翻转 - 模拟绕X轴旋转 - if (camera) { - camera->setRotation(originalRotation + currentAngle * RAD_TO_DEG); - } - } else { - // 垂直轴翻转 - 模拟绕Y轴旋转 - if (camera) { - camera->setRotation(originalRotation - currentAngle * RAD_TO_DEG); - } - } - - // 渲染场景 - outgoingScene_->renderContent(renderer); - - // 恢复相机状态 - if (camera) { - camera->setRotation(originalRotation); - } - } - } else { - // 后半段:翻转目标场景 - if (incomingScene_) { - float currentAngle = angle - PI_F; - - // 保存原始相机状态 - Camera *camera = incomingScene_->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); - } - } - - // 渲染场景 - incomingScene_->renderContent(renderer); - - // 恢复相机状态 - if (camera) { - camera->setRotation(originalRotation); - } - } - } -} - -// ============================================================================ -// BoxTransition - 方块过渡 -// ============================================================================ -BoxTransition::BoxTransition(float duration, int divisions) - : Transition(duration), divisions_(divisions) {} - -void BoxTransition::onRenderTransition(RenderBackend &renderer, - float progress) { - float screenWidth = 800.0f; - float screenHeight = 600.0f; - - if (incomingScene_) { - Size viewportSize = incomingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } else if (outgoingScene_) { - Size viewportSize = outgoingScene_->getViewportSize(); - if (viewportSize.width > 0 && viewportSize.height > 0) { - screenWidth = viewportSize.width; - screenHeight = viewportSize.height; - } - } - - if (incomingScene_) { - incomingScene_->renderContent(renderer); - } else if (outgoingScene_) { - outgoingScene_->renderContent(renderer); - } else { - return; - } - - int div = std::max(1, divisions_); - int total = div * div; - int visible = std::clamp(static_cast(total * progress), 0, total); - - float cellW = screenWidth / static_cast(div); - float cellH = screenHeight / static_cast(div); - glm::mat4 overlayVP = - glm::ortho(0.0f, screenWidth, screenHeight, 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); - } -} - -} // 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..220a451 --- /dev/null +++ b/Extra2D/src/scene/transition_box_scene.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +TransitionBoxScene::TransitionBoxScene(float duration, Ptr inScene, + int divisions) + : TransitionScene(duration, inScene), divisions_(divisions) {} + +Ptr TransitionBoxScene::create(float duration, + Ptr inScene, + int divisions) { + return makePtr(duration, inScene, divisions); +} + +void TransitionBoxScene::onTransitionStart() { + // 方块过渡不需要特殊的初始化 +} + +void TransitionBoxScene::renderContent(RenderBackend &renderer) { + // 获取窗口大小 + auto &app = Application::instance(); + float windowWidth = static_cast(app.window().getWidth()); + float windowHeight = static_cast(app.window().getHeight()); + + // 更新进度 + 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..45c6789 --- /dev/null +++ b/Extra2D/src/scene/transition_fade_scene.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +TransitionFadeScene::TransitionFadeScene(float duration, Ptr inScene, + const Color &color) + : TransitionScene(duration, inScene), maskColor_(color) {} + +Ptr TransitionFadeScene::create(float duration, + Ptr inScene, + const Color &color) { + return makePtr(duration, inScene, color); +} + +void TransitionFadeScene::onTransitionStart() { + E2D_LOG_DEBUG("TransitionFadeScene::onTransitionStart - 启动淡入淡出过渡"); + + // 使用一个定时器来更新进度 + // 由于我们没有直接的动作系统集成到 Scene,使用简单的 update 逻辑 + // 实际进度更新由 SceneManager 的 update 驱动 +} + +void TransitionFadeScene::renderContent(RenderBackend &renderer) { + // 获取窗口大小 + auto &app = Application::instance(); + float windowWidth = static_cast(app.window().getWidth()); + float windowHeight = static_cast(app.window().getHeight()); + + // 计算当前进度 + elapsed_ += 1.0f / 60.0f; // 假设 60fps,实际应该由 update 传递 dt + 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; // 0 -> 1 + } else { + // 后半段:从不透明到透明 + maskAlpha = (1.0f - progress_) * 2.0f; // 1 -> 0 + } + + // 设置视口为整个窗口 + 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(); + } +} + +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..b63075f --- /dev/null +++ b/Extra2D/src/scene/transition_flip_scene.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +namespace extra2d { + +TransitionFlipScene::TransitionFlipScene(float duration, Ptr inScene, + Axis axis) + : TransitionScene(duration, inScene), axis_(axis) {} + +Ptr TransitionFlipScene::create(float duration, + Ptr inScene, + Axis axis) { + return makePtr(duration, inScene, axis); +} + +void TransitionFlipScene::onTransitionStart() { + // 翻页过渡不需要特殊的初始化 +} + +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; // 180度翻转 + + if (progress_ < 0.5f) { + // 前半段:翻转源场景 + if (outScene_) { + float currentAngle = angle; + + Camera *camera = outScene_->getActiveCamera(); + float originalRotation = camera ? camera->getRotation() : 0.0f; + + if (axis_ == Axis::Horizontal) { + // 水平轴翻转 - 模拟绕X轴旋转 + if (camera) { + camera->setRotation(originalRotation + currentAngle * RAD_TO_DEG); + } + } else { + // 垂直轴翻转 - 模拟绕Y轴旋转 + 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..235b533 --- /dev/null +++ b/Extra2D/src/scene/transition_scale_scene.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +namespace extra2d { + +TransitionScaleScene::TransitionScaleScene(float duration, Ptr inScene) + : TransitionScene(duration, inScene) {} + +Ptr TransitionScaleScene::create(float duration, + Ptr inScene) { + return makePtr(duration, inScene); +} + +void TransitionScaleScene::onTransitionStart() { + // 缩放过渡不需要特殊的初始化 +} + +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..592d891 --- /dev/null +++ b/Extra2D/src/scene/transition_scene.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +namespace extra2d { + +TransitionScene::TransitionScene(float duration, Ptr inScene) + : duration_(duration), inScene_(inScene) {} + +void TransitionScene::onEnter() { + // 调用基类的 onEnter + Scene::onEnter(); + + // 调用退出场景的 onExitTransitionDidStart + if (outScene_) { + outScene_->onExitTransitionDidStart(); + } + + // 调用进入场景的 onEnter + if (inScene_) { + inScene_->onEnter(); + inScene_->onAttachToScene(inScene_.get()); + } + + // 启动过渡 + onTransitionStart(); +} + +void TransitionScene::onExit() { + // 调用退出场景的 onExit + if (outScene_) { + outScene_->onExit(); + outScene_->onDetachFromScene(); + } + + // 调用进入场景的 onEnterTransitionDidFinish + if (inScene_) { + inScene_->onEnterTransitionDidFinish(); + } + + // 调用基类的 onExit + Scene::onExit(); +} + +void TransitionScene::finish() { + if (isFinished_) { + return; + } + + isFinished_ = true; + + E2D_LOG_DEBUG("TransitionScene::finish - 过渡完成,切换到目标场景"); + + // 调用完成回调,通知 SceneManager 进行场景切换 + if (finishCallback_) { + finishCallback_(); + } +} + +void TransitionScene::renderContent(RenderBackend &renderer) { + // 在 TransitionScene 上渲染新旧两个子场景 + // 子类可以重写此方法来控制渲染顺序和效果 + + // 默认先渲染退出场景,再渲染进入场景(在上方) + drawOutScene(renderer); + drawInScene(renderer); +} + +void TransitionScene::drawOutScene(RenderBackend &renderer) { + if (outScene_) { + outScene_->renderContent(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..b5b3f0a --- /dev/null +++ b/Extra2D/src/scene/transition_slide_scene.cpp @@ -0,0 +1,121 @@ +#include +#include +#include + +namespace extra2d { + +TransitionSlideScene::TransitionSlideScene(float duration, Ptr inScene, + TransitionDirection direction) + : TransitionScene(duration, inScene), direction_(direction) {} + +Ptr TransitionSlideScene::create( + float duration, Ptr inScene, TransitionDirection direction) { + return makePtr(duration, inScene, direction); +} + +void TransitionSlideScene::onTransitionStart() { + // 滑动过渡不需要特殊的初始化 +} + +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->setPosition(originalPos.x + offsetX, originalPos.y + offsetY); + } + + outScene_->renderContent(renderer); + + if (camera) { + camera->setPosition(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->setPosition(originalPos.x + offsetX, originalPos.y + offsetY); + } + + inScene_->renderContent(renderer); + + if (camera) { + camera->setPosition(originalPos); + } + } + + // 检查是否完成 + if (progress_ >= 1.0f && !isFinished_) { + finish(); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/ui/button.cpp b/Extra2D/src/ui/button.cpp index a135930..9d2ee1e 100644 --- a/Extra2D/src/ui/button.cpp +++ b/Extra2D/src/ui/button.cpp @@ -1,9 +1,9 @@ #include #include #include +#include #include #include -#include namespace extra2d { @@ -15,7 +15,7 @@ Button::Button() { // 按钮默认锚点为左上角,这样setPosition(0, 0)会在左上角显示 setAnchor(0.0f, 0.0f); setSpatialIndexed(false); - + auto &dispatcher = getEventDispatcher(); dispatcher.addListener(EventType::UIHoverEnter, [this](Event &) { hovered_ = true; @@ -37,24 +37,24 @@ Button::Button() { dispatcher.addListener(EventType::UIReleased, [this](Event &) { pressed_ = false; }); dispatcher.addListener(EventType::UIClicked, [this](Event &) { - if (onClick_) + if (toggleMode_) { + toggle(); + } + if (onClick_) { onClick_(); + } }); } -Button::Button(const std::string &text) : Button() { - text_ = text; -} +Button::Button(const std::string &text) : Button() { text_ = text; } // ------------------------------------------------------------------------ // 静态创建方法 // ------------------------------------------------------------------------ -Ptr