CanvasGroupとは?

UIを作っていると、パネルやダイアログの表示・非表示を切り替えたり、フェードさせたい場面がよくあります。

そのとき、パネル内のButtonImageを一つずつ操作するのは手間がかかりますし、管理も複雑になりがちです。

CanvasGroupは、こうした課題を解決するためのコンポーネントです。

GameObjectにアタッチするだけで、そのオブジェクトと子オブジェクトすべてのUI要素に対して、透明度・操作可否・レイキャストのブロックをまとめて制御できます。

たとえばalphaを0にするだけでパネル全体を透明にしたり、interactablefalseにするだけで全ボタンを一括で無効化したりできます。

個別に制御するコードを書く必要がなくなるので、UIまわりの実装がシンプルになります。

プロパティの解説

CanvasGroupには4つのプロパティがあります。

alpha

グループ全体の透明度を0〜1の範囲で設定します。

0で完全に透明、1で完全に不透明です。

この値は子要素のアルファ値と乗算で合成されます。

たとえばCanvasGroupalphaが0.5で、子のImageのアルファも0.5の場合、実際の表示は0.25になります。

interactable

trueの場合、グループ内のUI要素は通常どおり操作できます。

falseにすると、グループ内のすべてのSelectableコンポーネント(ButtonSliderToggleなど)が操作不能になります。

ナビゲーションも無効化されるため、キーボードやゲームパッドでの操作もできなくなります。

blocksRaycasts

trueの場合、Graphic Raycasterのレイキャストをブロックします。

falseにすると、グループ内のUI要素をクリックやタッチがすり抜けるようになります。

パネルを非表示にする際はalphaを0にするだけでなく、blocksRaycastsfalseにしておかないと、見えないUIが背後のタッチを奪ってしまいます。これは意外とハマりやすいポイントです。

ignoreParentGroups

通常、子のCanvasGroupは親のCanvasGroupの影響を受けます。

ignoreParentGroupstrueにすると、親のCanvasGroupalphainteractableなどの設定を無視できます。

たとえば、パネル全体をフェードアウトさせたいけれど、特定の子要素だけは常に表示しておきたい場合などに使えます。

基本的な使い方

パネルの表示・非表示をCanvasGroupで切り替える例です。

alphainteractableblocksRaycastsをまとめて操作することで、表示状態を制御します。

using UnityEngine;

public class PanelController : MonoBehaviour
{
    [SerializeField] private CanvasGroup _canvasGroup;

    // パネルを表示する
    public void ShowPanel()
    {
        _canvasGroup.alpha = 1f;
        _canvasGroup.interactable = true;
        _canvasGroup.blocksRaycasts = true;
    }

    // パネルを非表示にする
    public void HidePanel()
    {
        _canvasGroup.alpha = 0f;
        _canvasGroup.interactable = false;
        _canvasGroup.blocksRaycasts = false;
    }
}

ShowPanelで3つのプロパティをすべて有効にし、HidePanelですべて無効にしています。

HidePanelのときblocksRaycastsfalseにしているのがポイントです。

これを忘れると、透明なパネルが背後のUIへのタッチをブロックしてしまいます。

実践的な使い方

コルーチンによるフェードイン・フェードアウト

alphaを徐々に変化させれば、パネルのフェードイン・フェードアウトも実現できます。

using System.Collections;
using UnityEngine;

public class PanelFader : MonoBehaviour
{
    [SerializeField] private CanvasGroup _canvasGroup;
    [SerializeField] private float _fadeDuration = 0.3f;

    public void FadeIn()
    {
        StartCoroutine(Fade(0f, 1f));
    }

    public void FadeOut()
    {
        StartCoroutine(Fade(1f, 0f));
    }

    private IEnumerator Fade(float from, float to)
    {
        var elapsed = 0f;

        // フェード開始時に操作を設定する
        _canvasGroup.interactable = false;
        _canvasGroup.blocksRaycasts = true;

        while (elapsed < _fadeDuration)
        {
            elapsed += Time.deltaTime;
            _canvasGroup.alpha = Mathf.Lerp(from, to, elapsed / _fadeDuration);
            yield return null;
        }

        _canvasGroup.alpha = to;

        // フェード完了後に操作状態を反映する
        var isVisible = to > 0f;
        _canvasGroup.interactable = isVisible;
        _canvasGroup.blocksRaycasts = isVisible;
    }
}

FadeメソッドでMathf.Lerpを使い、alphaを徐々に変化させています。

フェード中はinteractablefalseにして誤操作を防ぎ、完了後に表示状態に応じて操作可否を切り替えています。

ダイアログ表示中に背後のUIを無効化する

ダイアログを表示しているとき、背後のUIをタッチできないようにしたい場面は多いです。

背後のパネルにもCanvasGroupを付けておけば、簡単に実現できます。

using UnityEngine;

public class DialogManager : MonoBehaviour
{
    [SerializeField] private CanvasGroup _mainUICanvasGroup;
    [SerializeField] private CanvasGroup _dialogCanvasGroup;

    // ダイアログを表示し、背後のUIを無効化する
    public void OpenDialog()
    {
        // 背後のUIを操作不能にする
        _mainUICanvasGroup.interactable = false;
        _mainUICanvasGroup.blocksRaycasts = false;

        // ダイアログを表示する
        _dialogCanvasGroup.alpha = 1f;
        _dialogCanvasGroup.interactable = true;
        _dialogCanvasGroup.blocksRaycasts = true;
    }

    // ダイアログを閉じて、背後のUIを復帰させる
    public void CloseDialog()
    {
        // ダイアログを非表示にする
        _dialogCanvasGroup.alpha = 0f;
        _dialogCanvasGroup.interactable = false;
        _dialogCanvasGroup.blocksRaycasts = false;

        // 背後のUIを復帰させる
        _mainUICanvasGroup.interactable = true;
        _mainUICanvasGroup.blocksRaycasts = true;
    }
}

背後のUIはinteractableblocksRaycastsfalseにして操作を無効化しています。

alphaは変えていないので、背後のUIは見えたまま操作だけができなくなります。

SetActive(false) との使い分け

UIを非表示にする方法として、GameObject.SetActive(false)を使う方法もあります。

それぞれの違いを把握しておくと、場面に応じた判断がしやすくなります。

SetActive(false) の特徴

  • GameObjectが非アクティブになるため、描画もコンポーネントの処理も完全に停止する
  • 実行中のコルーチンが中断され、再アクティブ化しても再開されない
  • OnEnableOnDisableが呼ばれるため、副作用が発生する場合がある
  • 非アクティブなGameObjectFind系メソッドで検索できなくなる

CanvasGroup の特徴

  • GameObjectはアクティブのままなので、コルーチンやUpdateは動き続ける
  • alphaを0にしても描画コストは発生する(後述の注意点を参照)
  • 表示・非表示の切り替えが滑らかにできる(フェード処理など)
  • interactableblocksRaycastsで操作だけを無効化できる

使い分けの指針

フェードなどの演出を伴う場合や、非表示中もコルーチンを動かしたい場合はCanvasGroupが向いています。

一方、完全に不要なUIを非表示にして描画コストを削減したい場合はSetActive(false)が適切です。

両者を組み合わせることも有効です。フェード完了後にSetActive(false)で非アクティブにすれば、演出と描画コスト削減の両立ができます。

注意点

alpha=0でもレイキャストをブロックする

alphaを0にしてもUIが透明になるだけで、blocksRaycaststrueのままだとレイキャストはブロックされ続けます。

非表示にしたいならblocksRaycastsfalseにすること。ここを忘れると、「なぜかボタンが押せない」という原因不明のバグに繋がります。

alpha=0でも描画コストが発生する

alphaを0にしてもGameObjectはアクティブなままなので、描画パスの処理は行われます。

長時間非表示にするUIの場合はSetActive(false)を併用して、描画コストを抑えることを検討してください。

パフォーマンスへの配慮

CanvasGroupのプロパティを変更すると、所属するCanvasのリビルドが発生する場合があります。

フェード処理など毎フレームalphaを更新するケースでは、対象のUIを別のCanvas(Sub Canvas)に分離しておくと、リビルドの影響範囲を限定できます。

まとめ

CanvasGroupは、透明度・操作可否・レイキャストをグループ単位で一括制御できるコンポーネントです。

フェードやダイアログの背後の無効化など、UIでよくある実装パターンとも相性が良く、SetActive(false)とは違った使い道があります。

ただしalphaを0にしてもblocksRaycastsや描画コストは残ります。

この挙動を知らないと原因のわかりにくいバグに繋がるので、頭の片隅に入れておいてください。