Unity × DOTween × CanvasGroup で“実用的”なフェード付きシーン切り替え(拡張版)
— シングルトン化&非同期ロード対応まで
目次
事前準備:DOTween の導入
本記事のコードは DOTween(無料版で可) を使用します。まだ導入していない場合は、Unity Asset Store から「DOTween (HOTween v2)」をインポートしてください。
インポート後、メニュー Tools > Demigiant > DOTween Utility Panel を開き、「Setup DOTween…」ボタンを押して初期設定を完了させてください。この操作で DOTween Settings アセットが生成され、初期化が自動化されます。
⚠️ DOTween Utility Panel でセットアップ済みであれば、コード内で
DOTween.Init()を呼ぶ必要はありません。
この記事でできること
- 画面フェード(黒→表示/表示→黒)を 最小限のコード で実装
- フェード中の誤入力をブロック
- シングルトン化で全シーン共通のフェード管理
- LoadSceneAsync でローディングにも対応(任意でインジケータ接続可)
なぜ CanvasGroup なのか
- alpha を変えるだけで UI全体 を一括で透明化できる
- blocksRaycasts を true にすれば、フェード中はタップ/クリックを遮断
- DOTween の DOFade と相性がよく、1行でフェード を書ける
準備(FadePanel の作成)
- UI > Canvas を作る(Screen Space-Overlay 推奨)
- 子に UI > Image を追加し、全画面を覆う黒パネルにする。RectTransform をストレッチ(全方向アンカー)、Image.color は黒(#000、Alpha=1)
- 親(または同じオブジェクト)に CanvasGroup を追加。alpha = 1(最初は真っ黒)、interactable = true、blocksRaycasts = true
- この 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.Init() はここでは呼ばない。
// DOTween Utility Panel(Tools > Demigiant > DOTween Utility Panel)で
// 「Setup DOTween...」を実行済みであれば自動初期化されます。
}
void Start()
{
if (fadeInOnSceneStart)
{
fadeCanvasGroup.alpha = 1f;
fadeCanvasGroup.blocksRaycasts = true;
fadeCanvasGroup.DOFade(0f, fadeDuration)
.OnComplete(() => fadeCanvasGroup.blocksRaycasts = false);
}
// ※ 下記を有効にすると FadeToSceneAsync 内の op.completed と二重になります。
// FadeToSceneAsync を使う場合は有効化しないでください。
// SceneManager.sceneLoaded += (_, __) => FadeIn();
}
/// <summary>
/// 同期ロード版(学習・小規模プロジェクト専用)
/// 本番環境では FadeToSceneAsync を推奨。
/// </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()で重複破棄)
- フェードインが2回走る→ Start() の SceneManager.sceneLoaded と FadeToSceneAsync の op.completed を同時に有効にしていないか確認
拡張アイデア(必要に応じて)
- ローディングスピナー連携:FadeToSceneAsync 内で op.progress をポーリング表示
- 色・曲線のカスタム:DOFade に SetEase(Ease.InOutQuad) など
- イベント通知:UnityEvent onFadeOutStarted / onFadeInCompleted を公開し、BGMのフェード等と同期
まとめ
- CanvasGroup × DOTween で最短のフェード実装
- シングルトン化で全シーン共通の入口を1本化
- 非同期ロード対応でローディングUXも整う
- フェード中は blocksRaycasts で安全に入力を遮断
- DOTween.Init() はコード内で呼ばず、DOTween Utility Panel でのセットアップに任せる
訪問数 53 回, 今日の訪問数 1回







ディスカッション
コメント一覧
まだ、コメントがありません