Unity × DOTween × CanvasGroup で“実用的”なフェード付きシーン切り替え(拡張版)

— シングルトン化&非同期ロード対応まで

この記事でできること

  • 画面フェード(黒→表示/表示→黒)を 最小限のコード で実装
  • フェード中の誤入力をブロック
  • シングルトン化で全シーン共通のフェード管理
  • LoadSceneAsync でローディングにも対応(任意でインジケータ接続可)

なぜ CanvasGroup なのか

  • alpha を変えるだけで UI全体 を一括で透明化できる
  • blocksRaycasts を true にすれば、フェード中はタップ/クリックを遮断
  • DOTween の DOFade と相性がよく、1行でフェード を書ける

準備(FadePanel の作成)

  1. UI > Canvas を作る(Screen Space-Overlay 推奨)
  2. 子に UI > Image を追加し、全画面を覆う黒パネルにする
    • RectTransform をストレッチ(全方向アンカー)
    • Image.color は黒(#000、Alpha=1)
  3. 親(または同じオブジェクト)に CanvasGroup を追加
    • alpha = 1(最初は真っ黒)
    • interactable = true
    • blocksRaycasts = true
  4. この CanvasGroup を SceneFader にアサインする

実装:シングルトン+即戦力コード

どのシーンにも 1個だけ 置く(最初のシーンに配置)。以降のシーンには置かないでOK(DontDestroyOnLoad)。

using UnityEngine;
using UnityEngine.SceneManagement;
using DG.Tweening;

public class SceneFader : MonoBehaviour
{
    public static SceneFader Instance { get; private set; }

    [SerializeField] private CanvasGroup fadeCanvasGroup;
    [SerializeField] private float fadeDuration = 1.0f;
    [SerializeField] private bool fadeInOnSceneStart = true;

    bool isFading;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);

        // DOTween 初期化
        DOTween.Init(recycleAllByDefault: false, useSafeMode: true);
    }

    void Start()
    {
        // 起動直後のフェードイン
        if (fadeInOnSceneStart)
        {
            fadeCanvasGroup.alpha = 1f;
            fadeCanvasGroup.blocksRaycasts = true;
            fadeCanvasGroup.DOFade(0f, fadeDuration)
                .OnComplete(() => fadeCanvasGroup.blocksRaycasts = false);
        }

        // シーンロード完了時に自動フェードインしたい場合は下記を有効化
        // SceneManager.sceneLoaded += (_, __) => FadeIn();
    }

    /// <summary>同期ロード版(軽量プロジェクト向け)</summary>
    public void FadeToScene(string sceneName)
    {
        if (isFading) return;
        isFading = true;

        fadeCanvasGroup.DOFade(1f, fadeDuration)
            .OnStart(() => fadeCanvasGroup.blocksRaycasts = true)
            .OnComplete(() =>
            {
                SceneManager.LoadScene(sceneName);
                FadeIn(); // ロード直後にフェードイン
            });
    }

    /// <summary>非同期ロード版(推奨)</summary>
    public void FadeToSceneAsync(string sceneName)
    {
        if (isFading) return;
        isFading = true;

        fadeCanvasGroup.DOFade(1f, fadeDuration)
            .OnStart(() => fadeCanvasGroup.blocksRaycasts = true)
            .OnComplete(() =>
            {
                // 非同期ロード → 完了後にフェードイン
                var op = SceneManager.LoadSceneAsync(sceneName);
                op.completed += _ => FadeIn();
            });
    }

    /// <summary>外部からフェードインだけ使いたいとき用</summary>
    public void FadeIn()
    {
        fadeCanvasGroup.alpha = 1f; // 黒から開始
        fadeCanvasGroup.DOFade(0f, fadeDuration)
            .OnComplete(() =>
            {
                fadeCanvasGroup.blocksRaycasts = false;
                isFading = false;
            });
    }
}

ここがポイント

  • 多重実行防止:isFading で二度押し・連打を無効化
  • 入力制御:フェード中は blocksRaycasts = true で誤操作を遮断
  • 即フェードイン:シーン切替直後に FadeIn() を呼んで自然な復帰
  • 非同期版推奨:LoadSceneAsync はローディングUIと組み合わせやすい

呼び出し例(ボタンから)

using UnityEngine;

public class TitleMenu : MonoBehaviour
{
    public void OnClickStart()
    {
        // 小規模なら同期版でもOK
        // SceneFader.Instance.FadeToScene("GameScene");

        // 実運用は非同期版推奨
        SceneFader.Instance.FadeToSceneAsync("GameScene");
    }
}

Button.onClick に OnClickStart を登録。


よくある落とし穴

  • フェードが見えない→ FadePanel の Image.color が黒・パネルが最前面・CanvasのSort順が適切か確認
  • クリックが貫通する→ フェード中に blocksRaycasts = true にしているか
  • 二重ロードされる→ isFading でガードしているか
  • シーンに複数の SceneFader→ 最初のシーンだけに置き、以降のシーンには置かない(Awake()で重複破棄)

拡張アイデア(必要に応じて)

  • ローディングスピナー連携:FadeToSceneAsync 内で op.progress をポーリング表示
  • 色・曲線のカスタム:DOFade に SetEase(Ease.InOutQuad) など
  • イベント通知:UnityEvent onFadeOutStarted / onFadeInCompleted を公開し、BGMのフェード等と同期

まとめ

  • CanvasGroup × DOTween で最短のフェード実装
  • シングルトン化で全シーン共通の入口を1本化
  • 非同期ロード対応でローディングUXも整う
  • フェード中は blocksRaycasts で安全に入力を遮断

訪問数 5 回, 今日の訪問数 5回

C#

Posted by hidepon