適当おじさんの適当ブログ

技術のことやゲーム開発のことやゲームのことなど自由に雑多に書き連ねます

シーン遷移時に暗転するようにしたときのメモ 〜DOTweenとUniRxを添えて〜

はじめに

DOTweenとUniRxをやっとこさ導入しました。 はじめは勉強の目的で、しばらく便利なアセットを使わず実装をしていました。そろそろいいかな?と思い、これらを導入しました。控えめに言って最高です。

シーン遷移時の暗転処理

今は勉強のために、DOTweenとUniRxでいろいろな処理をがむしゃらに書き換えているところです。シーン遷移時の暗転処理はその1つです。色々なところに適用してみることを目的としているので、あまり良くない使い方をしてしまっているかもしれません・・・。

public class CustomSceneManager : DontDestroySingletonMonoBehaviour<CustomSceneManager> {
    private const float FADE_DURATION = 0.5f;
    [SerializeField]
    private Image image;
    private CanvasGroup canvasGroup;

    protected override void Awake()
    {
        base.Awake();
        canvasGroup = image.GetComponent<CanvasGroup>();
    }

    public void LoadWithFade(string sceneName)
    {
        image.raycastTarget = true;
        var process = SceneManager.LoadSceneAsync (sceneName);
        process.allowSceneActivation = false;
        process.AsObservable().Where(p => p.isDone).Subscribe(p => 
        {
            canvasGroup.DOFade(0f, FADE_DURATION);
        });

        canvasGroup.DOFade(1f, FADE_DURATION).OnComplete(() =>
        {
            while(process.progress < 0.9f){ /* 何か処理があれば */ }
            process.allowSceneActivation = true;
            image.raycastTarget = false;
        });
    }
}

ポイント

DontDestroyOnLoad

DontDestroySingletonMonoBehaviourはその名の通り、Singleton かつ DontDestroyOnLoad なMonoBehaviourです。元のシーンで画面を真っ黒にして、ロードしたシーンで真っ黒解除という動きにしたかったので、シーンを跨いでも維持されるようにしました。

Imageオブジェクト

uGUIのCanvasに画面全体を覆う黒塗りのImageを作成しておき、ImageにCanvasGroupをアタッチしたものを作成しました。CanvasGroupの透明度を変更することでフェードイン・フェードアウトを表現するようにしてみました。

f:id:subarunari:20170505234517p:plain:w200

Imageの透明度ではなく、CanvasGroupの透明度を変更するようにした理由は、将来的に文字やプログレスバーを暗転後の画面に表示しようと考えているためです。 暗転の始まりと終わりのタイミングで、Image.raycastTargetのtrue/falseを切り替えています。演出中にあらゆるクリックを画面全体を覆うImageオブジェクトで雑にブロックしたかったからです。

DOTweenとUniRxを使ったところ

透明度を徐々に変更するところに DOTween.DOFade を使いました。DOFadeは、CanvasGroupだけでなく、SpriteRenderer, Image, AudioSourceなどにも用意されています。指定したalpha値まで指定した秒数をかけて変化させてくれるので非常に便利です。1行でシンプルに書ける点も好きです。

UniRxは画面をだんだん明るくしはじめるタイミングをコントロールするために利用しました。だんだん暗くする処理は、暗くしたいタイミングで処理を始めれば良いと思うのですが、だんだん明るくする処理は、シーンの読み込みが終わったタイミングで始めたいなぁと思いました。なので、allowSceneActivationtrueにしたタイミングではなく、AsyncOperation.isDonetrueになったタイミングで明るくしてみることにしました。変に作り込むのは避けたかったので、 以下のように、AsyncOperationを AsObservable() して、処理完了フラグが立ったタイミングで画面が明るくなるようにしてみました。果たしてこれで厳密にできているのだろうか・・・。

process.AsObservable().Where(p => p.isDone).Subscribe(p => 
{
    canvasGroup.DOFade(0f, FADE_DURATION);
});

参考

  • progress と isDone には、以下のような仕様があるので注意が必要です。私もこの仕様に気づかずにしばらくハマっておりました・・・ただ、よく考えてみると、allowSceneActivation = false としていてシーンの有効化をブロックしているのだから処理が完了しないのも当然ですよね。 answers.unity3d.com