シーン遷移エフェクト
#OpenSiv3D #Scene #Effect
シーン遷移をしながら画面全体を覆うエフェクトを描画するサンプルです。
https://gyazo.com/56bbd3c7d417be1ec6a2dec9b1261673
https://scrapbox.io/files/68ecf1a2f7873dc4b06230aa.mp4
code:scene_transition.cpp
# include <Siv3D.hpp> // Siv3D v0.6.16
// シーン間の共有データ
struct GameData
{
// シーン遷移中に描画するエフェクト
Effect transitionEffect;
};
using App = SceneManager<String, GameData>;
// シーン遷移エフェクトの例
// 自由な内容で
struct ExampleTransitionEffect : public IEffect
{
// 画面の分割数(→白いラインの本数)
static constexpr int N = 32;
// 白いライン
Array<RectF> lines;
ExampleTransitionEffect()
{
// 白いラインを生成
for (int i = 0; i < N; ++i)
{
const double h = Scene::Height() / N;
lines.push_back(RectF{ 0, i * h, Scene::Width(), h });
}
lines.shuffle();
}
bool update(double t) override
{
// 白いライン
{
if (InRange(t, 0.0, 1.0))
{
for (const auto index, line : Indexed(lines))
{
const double startTime = 1.0 * index / N;
if (startTime < t)
{
const double alpha = Clamp((t - startTime) / 0.1, 0.0, 1.0);
const double scale = EaseInSine(alpha);
line.scaledAt(line.center(), scale, 1.0).draw(ColorF{ 1, alpha * 0.8 });
}
}
}
}
// 点滅する白背景とフェードするテキスト
{
double alpha = 1;
double flickerMin = 1;
double scale = 1;
if (InRange(t, 0.0, 0.7))
{
const double t01 = t / 0.7;
alpha = EaseOutQuad(t01);
flickerMin = alpha * 0.8;
scale = 1.8 - 0.8 * EaseOutCubic(t01);
}
else if (InRange(t, 0.7, 2.2))
{
const double t01 = (t - 0.7) / (2.2 - 0.7);
}
else
{
const double t01 = (t - 2.2) / (3.0 - 2.2);
alpha = 1.0 - EaseOutQuad(t01);
}
const double flicker = flickerMin + (1.0 - flickerMin) * Periodic::Square0_1(32ms);
Scene::Rect().draw(ColorF{ 1.0, alpha * flicker });
{
const Transformer2D _{ Mat3x2::Scale(scale, Scene::CenterF()) };
FontAsset(U"Transition")(U"シーン遷移中").drawAt(20, Scene::CenterF(), Palette::Steelblue.withA((alpha * flicker) * 255));
}
}
return t < 3.0;
}
};
// ISceneにシーン遷移用の関数を追加したもの
class CustomScene : public App::Scene
{
public:
using App::Scene::IScene;
protected:
// シーン遷移を開始
// sceneChangeTo: 遷移先のシーン名
// changeSceneTime: この関数が呼ばれてから changeScene() を行うまでの時間(秒)
// transitionEffect: シーン遷移時に描画するエフェクト。最低でも changeSceneTime 秒 以上持続することが望ましい
void startTransition(const String& sceneChangeTo, const Duration& changeSceneTime, std::unique_ptr<IEffect>&& transitionEffect)
{
if (isStartedTransition()) return;
sceneChangeTo_ = sceneChangeTo;
timerTransition_.restart(changeSceneTime);
getData().transitionEffect.add(std::move(transitionEffect));
}
// 指定した時間になったらシーン遷移をする
// 毎フレーム呼ぶことを想定
void updateTransition()
{
if (timerTransition_.reachedZero())
{
timerTransition_.reset();
changeScene(sceneChangeTo_, 0s);
}
}
// シーン遷移が開始され、まだ changeScene() が行われていない
bool isStartedTransition() const
{
return timerTransition_.isRunning();
}
private:
String sceneChangeTo_;
Timer timerTransition_{ 1s, StartImmediately::No };
};
class TitleScene : public CustomScene
{
public:
TitleScene(const InitData& init)
: CustomScene{ init }
{
}
void update() override
{
updateTransition();
if (KeyEnter.down())
{
startTransition(U"MainScene", 2.2s, std::make_unique<ExampleTransitionEffect>());
}
}
void draw() const override
{
Scene::Rect().draw(Palette::Turquoise);
FontAsset(U"Title")(U"タイトルシーン").drawAt(TextStyle::Outline(0.18, Palette::Turquoise.lerp(Palette::Black, 0.3)), 48, Scene::CenterF());
}
};
class MainScene : public CustomScene
{
public:
MainScene(const InitData& init)
: CustomScene{ init }
{
}
void update() override
{
}
void draw() const override
{
Scene::Rect().draw(Palette::Hotpink);
FontAsset(U"Title")(U"メインシーン").drawAt(TextStyle::Outline(0.18, Palette::Hotpink.lerp(Palette::Black, 0.3)), 48, Scene::CenterF());
}
};
void Main()
{
Scene::SetBackground(ColorF{ 0, 1 });
FontAsset::Register(U"Title", FontMethod::MSDF, 24, Typeface::Black);
FontAsset::Register(U"Transition", FontMethod::MSDF, 24, Typeface::Light);
App manager;
manager.add<TitleScene>(U"TitleScene");
manager.add<MainScene>(U"MainScene");
manager.init(U"TitleScene", 0s);
auto gameData = manager.get();
while (System::Update())
{
if (not manager.update())
{
break;
}
gameData->transitionEffect.update();
}
}