feat(scene): 添加多种场景过渡效果实现

实现缩放、方块、滑动、翻页和淡入淡出五种场景过渡效果
新增 TransitionScene 基类作为过渡场景的公共父类
扩展 SceneManager 支持带过渡效果的场景切换操作
This commit is contained in:
ChestnutYueyue 2026-02-15 18:00:32 +08:00
parent b55d279611
commit 6273f3235d
14 changed files with 1393 additions and 0 deletions

View File

@ -12,6 +12,8 @@
namespace extra2d { namespace extra2d {
struct RenderCommand; struct RenderCommand;
class TransitionScene;
enum class TransitionType;
/** /**
* @brief - * @brief -
@ -22,6 +24,9 @@ public:
static SceneManager &get(); static SceneManager &get();
// ------------------------------------------------------------------------
// 基本场景操作(无过渡效果)
// ------------------------------------------------------------------------
void runWithScene(Ptr<Scene> scene); void runWithScene(Ptr<Scene> scene);
void replaceScene(Ptr<Scene> scene); void replaceScene(Ptr<Scene> scene);
void pushScene(Ptr<Scene> scene); void pushScene(Ptr<Scene> scene);
@ -29,6 +34,61 @@ public:
void popToRootScene(); void popToRootScene();
void popToScene(const std::string &name); void popToScene(const std::string &name);
// ------------------------------------------------------------------------
// 带过渡效果的场景操作
// ------------------------------------------------------------------------
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration = 0.5f);
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void pushScene(Ptr<Scene> 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> scene, Ptr<TransitionScene> transitionScene);
// ------------------------------------------------------------------------
// 场景查询
// ------------------------------------------------------------------------
Ptr<Scene> getCurrentScene() const; Ptr<Scene> getCurrentScene() const;
Ptr<Scene> getPreviousScene() const; Ptr<Scene> getPreviousScene() const;
Ptr<Scene> getRootScene() const; Ptr<Scene> getRootScene() const;
@ -38,10 +98,16 @@ public:
bool isEmpty() const { return sceneStack_.empty(); } bool isEmpty() const { return sceneStack_.empty(); }
bool hasScene(const std::string &name) const; bool hasScene(const std::string &name) const;
// ------------------------------------------------------------------------
// 更新和渲染
// ------------------------------------------------------------------------
void update(float dt); void update(float dt);
void render(RenderBackend &renderer); void render(RenderBackend &renderer);
void collectRenderCommands(std::vector<RenderCommand> &commands); void collectRenderCommands(std::vector<RenderCommand> &commands);
// ------------------------------------------------------------------------
// 过渡状态
// ------------------------------------------------------------------------
bool isTransitioning() const { return isTransitioning_; } bool isTransitioning() const { return isTransitioning_; }
void setTransitionCallback(TransitionCallback callback) { void setTransitionCallback(TransitionCallback callback) {
transitionCallback_ = callback; transitionCallback_ = callback;
@ -59,6 +125,32 @@ public:
void enterScene(Ptr<Scene> scene); void enterScene(Ptr<Scene> scene);
private: private:
/**
* @brief
* @param from
* @param to
* @param type
* @param duration
* @param stackAction
*/
void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type,
float duration, std::function<void()> stackAction);
/**
* @brief
* @param type
* @param duration
* @param inScene
* @return
*/
Ptr<TransitionScene> createTransitionScene(TransitionType type, float duration,
Ptr<Scene> inScene);
/**
* @brief
*/
void finishTransition();
void doSceneSwitch(); void doSceneSwitch();
void dispatchPointerEvents(Scene &scene); void dispatchPointerEvents(Scene &scene);
@ -71,6 +163,9 @@ private:
Ptr<Scene> nextScene_; Ptr<Scene> nextScene_;
bool sendCleanupToScene_ = false; bool sendCleanupToScene_ = false;
Ptr<TransitionScene> activeTransitionScene_;
std::function<void()> transitionStackAction_;
Node *hoverTarget_ = nullptr; Node *hoverTarget_ = nullptr;
Node *captureTarget_ = nullptr; Node *captureTarget_ = nullptr;
Vec2 lastPointerWorld_ = Vec2::Zero(); Vec2 lastPointerWorld_ = Vec2::Zero();

View File

@ -0,0 +1,34 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 方块/马赛克过渡场景
// 实现原理:
// 1. 将屏幕分成多个方块
// 2. 方块逐个消失,显示新场景
// ============================================================================
class TransitionBoxScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param divisions 8 8x8
*/
TransitionBoxScene(float duration, Ptr<Scene> inScene, int divisions = 8);
static Ptr<TransitionBoxScene> create(float duration, Ptr<Scene> inScene,
int divisions = 8);
protected:
void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override;
private:
int divisions_;
};
} // namespace extra2d

View File

@ -0,0 +1,53 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 淡入淡出过渡场景
// 实现原理:
// 1. 创建一个纯色精灵作为遮罩层
// 2. 第一阶段:遮罩从透明淡入到不透明(黑屏),同时显示旧场景
// 3. 切换显示新场景
// 4. 第二阶段:遮罩从不透明淡出到透明,显示新场景
// ============================================================================
class TransitionFadeScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param color
*/
TransitionFadeScene(float duration, Ptr<Scene> inScene,
const Color &color = Colors::Black);
static Ptr<TransitionFadeScene> create(float duration, Ptr<Scene> 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

View File

@ -0,0 +1,37 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
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<Scene> inScene,
Axis axis = Axis::Horizontal);
static Ptr<TransitionFlipScene> create(float duration, Ptr<Scene> inScene,
Axis axis = Axis::Horizontal);
protected:
void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override;
private:
Axis axis_;
};
} // namespace extra2d

View File

@ -0,0 +1,29 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 缩放过渡场景
// 实现原理:
// 1. 旧场景缩小消失
// 2. 新场景放大出现
// ============================================================================
class TransitionScaleScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScaleScene(float duration, Ptr<Scene> inScene);
static Ptr<TransitionScaleScene> create(float duration, Ptr<Scene> inScene);
protected:
void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override;
};
} // namespace extra2d

View File

@ -0,0 +1,127 @@
#pragma once
#include <extra2d/scene/scene.h>
#include <functional>
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<void()>;
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScene(float duration, Ptr<Scene> inScene);
~TransitionScene() override = default;
// ------------------------------------------------------------------------
// 场景管理
// ------------------------------------------------------------------------
/**
* @brief
*/
Ptr<Scene> getInScene() const { return inScene_; }
/**
* @brief 退
*/
Ptr<Scene> getOutScene() const { return outScene_; }
/**
* @brief 退 SceneManager
*/
void setOutScene(Ptr<Scene> 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<Scene> inScene_; // 要进入的场景
Ptr<Scene> outScene_; // 要退出的场景
FinishCallback finishCallback_;
};
} // namespace extra2d

View File

@ -0,0 +1,35 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 滑动过渡场景
// 实现原理:
// 1. 旧场景向指定方向滑出
// 2. 新场景从相反方向滑入
// ============================================================================
class TransitionSlideScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param direction
*/
TransitionSlideScene(float duration, Ptr<Scene> inScene,
TransitionDirection direction);
static Ptr<TransitionSlideScene> create(float duration, Ptr<Scene> inScene,
TransitionDirection direction);
protected:
void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override;
private:
TransitionDirection direction_;
};
} // namespace extra2d

View File

@ -4,6 +4,12 @@
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/render_command.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/transition_box_scene.h>
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/scene/transition_scene.h>
#include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {
@ -115,6 +121,48 @@ void SceneManager::replaceScene(Ptr<Scene> scene) {
sceneStack_.push(scene); sceneStack_.push(scene);
} }
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void SceneManager::replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
auto oldScene = sceneStack_.top();
startTransition(oldScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
auto outScene = activeTransitionScene_->getOutScene();
if (outScene) {
outScene->onExit();
outScene->onDetachFromScene();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/** /**
* @brief * @brief
* @param scene * @param scene
@ -133,6 +181,67 @@ void SceneManager::enterScene(Ptr<Scene> scene) {
} }
} }
/**
* @brief 使
* @param scene
* @param transitionScene
*/
void SceneManager::enterScene(Ptr<Scene> scene,
Ptr<TransitionScene> transitionScene) {
if (!scene || isTransitioning_) {
return;
}
if (!transitionScene) {
enterScene(scene);
return;
}
auto current = getCurrentScene();
if (!current) {
enterScene(scene);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(current);
transitionScene->setFinishCallback([this]() { finishTransition(); });
current->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = [this, transitionScene]() {
auto outScene = transitionScene->getOutScene();
if (!sceneStack_.empty() && outScene) {
if (sceneStack_.top().get() == transitionScene.get()) {
sceneStack_.pop();
}
outScene->onExit();
outScene->onDetachFromScene();
}
auto inScene = transitionScene->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
};
}
/** /**
* @brief * @brief
* @param scene * @param scene
@ -153,6 +262,44 @@ void SceneManager::pushScene(Ptr<Scene> scene) {
sceneStack_.push(scene); sceneStack_.push(scene);
} }
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void SceneManager::pushScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
sceneStack_.top()->pause();
auto currentScene = sceneStack_.top();
startTransition(currentScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/** /**
* @brief * @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 * @brief
* *
@ -193,6 +375,33 @@ void SceneManager::popToRootScene() {
sceneStack_.top()->resume(); 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 * @brief
* @param name * @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 * @brief
* @return nullptr * @return nullptr
@ -381,6 +621,124 @@ void SceneManager::end() {
*/ */
void SceneManager::purgeCachedScenes() { namedScenes_.clear(); } void SceneManager::purgeCachedScenes() { namedScenes_.clear(); }
/**
* @brief
* @param from
* @param to
* @param type
* @param duration
* @param stackAction
*/
void SceneManager::startTransition(Ptr<Scene> from, Ptr<Scene> to,
TransitionType type, float duration,
std::function<void()> stackAction) {
if (!from || !to) {
return;
}
auto transitionScene = createTransitionScene(type, duration, to);
if (!transitionScene) {
replaceScene(to);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(from);
transitionScene->setFinishCallback([this]() { finishTransition(); });
from->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = std::move(stackAction);
}
/**
* @brief
* @param type
* @param duration
* @param inScene
* @return
*/
Ptr<TransitionScene> SceneManager::createTransitionScene(TransitionType type,
float duration,
Ptr<Scene> inScene) {
if (!inScene) {
return nullptr;
}
switch (type) {
case TransitionType::Fade:
return TransitionFadeScene::create(duration, inScene);
case TransitionType::SlideLeft:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Left);
case TransitionType::SlideRight:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Right);
case TransitionType::SlideUp:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Up);
case TransitionType::SlideDown:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Down);
case TransitionType::Scale:
return TransitionScaleScene::create(duration, inScene);
case TransitionType::Flip:
return TransitionFlipScene::create(duration, inScene);
case TransitionType::Box:
return TransitionBoxScene::create(duration, inScene);
default:
return TransitionFadeScene::create(duration, inScene);
}
}
/**
* @brief
*
*
*/
void SceneManager::finishTransition() {
Node *lastHoverTarget = hoverTarget_;
isTransitioning_ = false;
hoverTarget_ = nullptr;
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
if (transitionStackAction_) {
transitionStackAction_();
}
if (lastHoverTarget) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, lastHoverTarget};
dispatchToNode(lastHoverTarget, evt);
}
activeTransitionScene_.reset();
transitionStackAction_ = nullptr;
if (transitionCallback_) {
transitionCallback_();
transitionCallback_ = nullptr;
}
}
/** /**
* @brief * @brief
* @param scene * @param scene
@ -472,4 +830,7 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
lastPointerWorld_ = worldPos; lastPointerWorld_ = worldPos;
} }
void SceneManager::doSceneSwitch() {
}
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,87 @@
#include <extra2d/scene/transition_box_scene.h>
#include <extra2d/app/application.h>
#include <extra2d/core/color.h>
#include <extra2d/graphics/render_backend.h>
#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
* @param divisions
*/
TransitionBoxScene::TransitionBoxScene(float duration, Ptr<Scene> inScene,
int divisions)
: TransitionScene(duration, inScene), divisions_(divisions) {}
/**
* @brief
* @param duration
* @param inScene
* @param divisions
* @return
*/
Ptr<TransitionBoxScene> TransitionBoxScene::create(float duration,
Ptr<Scene> inScene,
int divisions) {
return makePtr<TransitionBoxScene>(duration, inScene, divisions);
}
/**
* @brief
*
*
*/
void TransitionBoxScene::onTransitionStart() {
}
/**
* @brief
* @param renderer
*
*
*/
void TransitionBoxScene::renderContent(RenderBackend &renderer) {
auto &app = Application::get();
float windowWidth = static_cast<float>(app.window().width());
float windowHeight = static_cast<float>(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<int>(windowWidth),
static_cast<int>(windowHeight));
int div = std::max(1, divisions_);
int total = div * div;
int visible = std::clamp(static_cast<int>(total * progress_), 0, total);
float cellW = windowWidth / static_cast<float>(div);
float cellH = windowHeight / static_cast<float>(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

View File

@ -0,0 +1,99 @@
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/app/application.h>
#include <extra2d/graphics/render_backend.h>
#include <extra2d/utils/logger.h>
#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
* @param color
*/
TransitionFadeScene::TransitionFadeScene(float duration, Ptr<Scene> inScene,
const Color &color)
: TransitionScene(duration, inScene), maskColor_(color) {}
/**
* @brief
* @param duration
* @param inScene
* @param color
* @return
*/
Ptr<TransitionFadeScene> TransitionFadeScene::create(float duration,
Ptr<Scene> inScene,
const Color &color) {
return makePtr<TransitionFadeScene>(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<float>(app.window().width());
float windowHeight = static_cast<float>(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<int>(windowWidth),
static_cast<int>(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

View File

@ -0,0 +1,107 @@
#include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/core/math_types.h>
#include <extra2d/graphics/camera.h>
#include <extra2d/graphics/render_backend.h>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
* @param axis
*/
TransitionFlipScene::TransitionFlipScene(float duration, Ptr<Scene> inScene,
Axis axis)
: TransitionScene(duration, inScene), axis_(axis) {}
/**
* @brief
* @param duration
* @param inScene
* @param axis
* @return
*/
Ptr<TransitionFlipScene> TransitionFlipScene::create(float duration,
Ptr<Scene> inScene,
Axis axis) {
return makePtr<TransitionFlipScene>(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

View File

@ -0,0 +1,87 @@
#include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/graphics/camera.h>
#include <extra2d/graphics/render_backend.h>
#include <algorithm>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScaleScene::TransitionScaleScene(float duration, Ptr<Scene> inScene)
: TransitionScene(duration, inScene) {}
/**
* @brief
* @param duration
* @param inScene
* @return
*/
Ptr<TransitionScaleScene> TransitionScaleScene::create(float duration,
Ptr<Scene> inScene) {
return makePtr<TransitionScaleScene>(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

View File

@ -0,0 +1,103 @@
#include <extra2d/scene/transition_scene.h>
#include <extra2d/graphics/render_backend.h>
#include <extra2d/utils/logger.h>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScene::TransitionScene(float duration, Ptr<Scene> 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

View File

@ -0,0 +1,139 @@
#include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/graphics/camera.h>
#include <extra2d/graphics/render_backend.h>
#include <algorithm>
namespace extra2d {
/**
* @brief
* @param duration
* @param inScene
* @param direction
*/
TransitionSlideScene::TransitionSlideScene(float duration, Ptr<Scene> inScene,
TransitionDirection direction)
: TransitionScene(duration, inScene), direction_(direction) {}
/**
* @brief
* @param duration
* @param inScene
* @param direction
* @return
*/
Ptr<TransitionSlideScene> TransitionSlideScene::create(
float duration, Ptr<Scene> inScene, TransitionDirection direction) {
return makePtr<TransitionSlideScene>(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