LoginSignup
9
5

More than 5 years have passed since last update.

Siv3D:SceneManagerのシーン遷移のフェード管理

Last updated at Posted at 2017-12-06

SceneManager

Siv3Dでゲーム制作をするとき、プロジェクトの大きさ次第では、HamFrameworkのham::SceneManagerを使う人は多いかと思います。

Main.cpp

using MyApp = ham::SceneManager<String>;

class GameMain : public MyApp::Scene
{
private:
public:
    void init()override;//OpenSiv3Dはコンストラクタ
    void update()override;
    void draw()const override;
};

その際、シーン遷移のフェード中の処理もオーバーライドできるのはご存知でしょうか?

Main.cpp

class GameMain : public MyApp::Scene
{
private:
public:
    void init()override;
    void update()override;
    void draw()const override;

    //フェード中の処理
    void updateFadeIn(double t)override;
    void updateFadeOut(double t)override;
    void drawFadeIn(double t)const override;
    void drawFadeOut(double t)const override;
};

この際にそれぞれのメソッドの引数double tに入ってくるのは
[0.0,1.0) (遷移開始~遷移終了)
になります

今回はこのうちのdrawFadeIndrawFadeOutを利用することでシーン遷移のフェード演出の変更をしてみるのですが、普段自分がやっている管理方法を紹介します。

FadeInとFadeOut関数の作成

Fade.hpp
#include<functional>

void FadeIn(std::function<void(double)> func, double t)
{
    func(1.0 - t);
}
auto FadeOut(std::function<void(double)> func, double t)
{
    func(t);
}

第一引数がdoubleである関数をうけとり、コールバックするFadeInFadeOutの関数を作ります。

FadeInとFadeOutは動きが逆になればよいだけなので、
FadeInでは1.0-t
FadeOutではt
渡しています。

templateにして複数引数への対応

Fade.hpp

template<class Func, class... Args>
void FadeIn(Func func, double t, Args&&...args)
{
    func(1.0 - t, std::forward<Args>(args)...);
}

template<class Func, class... Args>
void FadeOut(Func func, double t, Args&&...args)
{
    func(t, std::forward<Args>(args)...);
}

受け取る関数が他に引数を必要とする場合も想定するならばtemplate可変長引数を使うのも良いです。自分はそうしています。(融通が利くので)

フェード用の関数をいろいろ作ってみる

Fade.hpp
namespace Fade
{
    //フェードの種類
    void Default(double t);
    void SmoothCircle(double t);
    void Siv3DKun(double t,bool in);
}

今回は3種類のフェード関数を作ってみます。

Fade.cpp
#include"Fade.hpp"
#include<Siv3D.hpp>

namespace
{
    //微調整
    bool FadeBase(double& t)
    {
        if (t > 0.75)
        {
            Window::BaseClientRect().draw(ColorF(0.0));
            return false;
        }

        t *= (1 / 0.75);
        return true;
    }

    //マスク処理
    void StencilMask(std::function<void()> base, std::function<void()>drawFunc, StencilFunc stencilFunc, uint8 stencilValue = 1)
    {
        Graphics2D::SetStencilState(StencilState::Replace);
        Graphics2D::SetStencilValue(stencilValue);

        base();

        Graphics2D::SetStencilState(StencilState::Test(stencilFunc));

        drawFunc();

        Graphics2D::SetStencilState(StencilState::Default);

    };
}

namespace Fade
{
    //デフォルト
    void Default(double t)
    {
        if (!::FadeBase(t))
            return;
        Window::BaseClientRect().draw(ColorF(0.0, t));
    }

    //3次関数的に広がる円形マスク
    void SmoothCircle(double t)
    {
        if (!::FadeBase(t))
            return;

        static auto func = [=](double t)
        {
            return ((t - 0.3f)*(t - 0.3f)*(t - 0.3f) + 0.027) / 0.37f;
        };

        ::StencilMask(
            [t] { Circle(Window::BaseCenter(), Window::BaseWidth() * func(1.0 - t)).draw(); },
            [] { Window::BaseClientRect().draw(ColorF(0.0, 1)); },
            StencilFunc::NotEqual
        );
    }

    //Siv3Dくんマスク
    void Siv3DKun(double t, bool in)
    {
        static Texture tex(L"Example/siv3d-kun.png");
        if (!::FadeBase(t))
            return;

        ::StencilMask(
            [t, in]{
            tex.scale(4 * EaseIn(Easing::Quad, 1 - t))
                .rotate((in ? (1.0 - t) : t) * 10)
                .drawAt(Window::BaseCenter());
            },
            [t] { Window::BaseClientRect().draw(ColorF(0.0, t * 4.0)); },
            StencilFunc::NotEqual
        );
    }
}

こんな感じで自分オリジナルの関数を作っていきます。

実際に使ってみる

さっき作った関数を実際に使ってみましょう。

main.cpp
#include"Fade.hpp"

class GameMain : public MyApp::Scene
{
private:
public:
    void init()override;
    void update()override;
    void draw()const override;

    //フェード中の処理
    void drawFadeIn(double t)const override 
    {
        this->draw();
        FadeIn(Fade::Siv3DKun, t,true);//←ここを変えるだけで変更できる
    }
    void drawFadeOut(double t)const override 
    {
        this->draw();
        FadeOut(Fade::Siv3DKun, t,false);//←ここを変えるだけで変更できる
    }

};

例えばFadeIn(Fade::Siv3DKun, t,true)のところを、FadeIn(Fade::Default,t)等に変更するだけで簡単に演出を切り替え可能なので非常に便利です。
this->draw()を呼ばないとフェード中に元の描画がされないので呼び忘れないように注意が必要です。

参考

超おまけ

以下はちょっとレベルの上がった話なのでスルーするか、コピペして使うか、頑張って勉強してください。

FadeIn/Out関数の型制約

templateでFadeIn/Outの関数を作った場合、間違った引数を使ってエラーがでる場合があり危険です。
最低限の型制約をしておくと良いでしょう。

Fade.hpp
#include<type_traits>

//メタ関数作成
//std::is_invocable(C++17/VS2017なら使える)
template<class AlwaysVoid, class F, class... Args>
struct is_invocable_impl :std::false_type
{};
template<class F, class... Args>
struct is_invocable_impl<
    std::void_t<decltype(std::declval<F>()(std::declval<Args>()...))>, F, Args...
> :std::true_type
{};

template<class F, class... Args>
using is_invocable = is_invocable_impl<void, F, Args...>;

template<class Func, class... Args>
auto FadeIn(Func func, double t, Args&&...args)->std::enable_if_t <is_invocable<Func, double, Args...>::value>
{
    func(1.0 - t, std::forward<Args>(args)...);
}
template<class Func, class... Args>
auto FadeOut(Func func, double t, Args&&...args)->std::enable_if_t <is_invocable<Func, double, Args...>::value>
{
    func(t, std::forward<Args>(args)...);
}

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5