Unityを触り始めた頃にありがちなのが、メニューの開閉やポーズ処理、UIの表示切替を全部ひとつの巨大なUpdate関数に書いてしまうパターンです。

例えばこんな感じですね:

// 悪い例(なんでもかんでも1つのスクリプトに詰め込む)
void Update()
{
    // プレイヤー移動
    // 攻撃処理
    // カメラ制御
    // メニュー表示切替
    // ポーズ処理
    // ...
}

これを続けてしまうと、

  • どこを触れば何が変わるのか分かりにくい
  • 別シーンや別プロジェクトで再利用しづらい
  • ちょっとした仕様変更でバグを生みやすい

という「神クラス(Godクラス)」まっしぐらになってしまいます。

そこで今回は、「メニュー(親オブジェクト)の表示/非表示だけ」を担当する小さなコンポーネントとして、VisibilityToggle を作っていきましょう。
特定のキー入力で、親(メニュー画面など)の表示/非表示を切り替える、シンプルだけど汎用性の高いコンポーネントです。

【Unity】一発キー入力でUIをON/OFF!「VisibilityToggle」コンポーネント

ここでは Unity6(C#)で動作する、コピペで使える完全版コードを掲載します。
新しいInput Systemを使わず、UnityEngine.InputInput.GetKeyDown を利用した実装なので、導入も簡単です。

フルコード


using UnityEngine;

/// <summary>
/// 特定のキー入力で、対象オブジェクト(通常は親のメニューなど)の
/// 表示/非表示(SetActive)を切り替えるコンポーネント。
/// 
/// ・責務は「表示切替」に限定
/// ・キー入力の監視と SetActive の制御のみを担当
/// ・プレイヤー操作やゲームロジックとは完全に分離しておく
/// </summary>
public class VisibilityToggle : MonoBehaviour
{
    [Header("入力設定")]
    [SerializeField]
    private KeyCode toggleKey = KeyCode.Escape;
    // どのキーで表示切替するか。
    // 例:Escape, Tab, M, P など。インスペクターから変更可能。

    [Header("対象オブジェクト設定")]
    [SerializeField]
    private GameObject targetRoot;
    // 表示/非表示を切り替えたいオブジェクト。
    // メニューのルート、ポーズ画面のルート、設定ウィンドウのルートなどを指定する。
    // 未設定の場合は、このコンポーネントが付いている GameObject 自身を対象とする。

    [Header("初期状態")]
    [SerializeField]
    private bool startVisible = false;
    // シーン開始時に表示しておくかどうか。
    // false にすると、ゲーム開始時は非表示で、キー入力で表示されるようになる。

    [Header("入力受付の有効/無効")]
    [SerializeField]
    private bool enableInput = true;
    // 一時的に入力受付を止めたい場合(チュートリアル中など)は false にする。

    // 現在の表示状態をキャッシュしておく(任意だが、状態把握に便利)
    public bool IsVisible { get; private set; }

    private void Reset()
    {
        // コンポーネントをアタッチしたときに、targetRoot を自動設定しておく
        // (何も指定していない場合は、自身を対象にする)
        if (targetRoot == null)
        {
            targetRoot = gameObject;
        }
    }

    private void Awake()
    {
        // targetRoot が未設定なら、自身を対象にする
        if (targetRoot == null)
        {
            targetRoot = gameObject;
        }

        // シーン開始時の表示状態を反映
        ApplyVisibility(startVisible);
    }

    private void Update()
    {
        if (!enableInput)
        {
            // 入力受付が無効なら、何もしない
            return;
        }

        // 指定キーが押された瞬間にトグル
        if (Input.GetKeyDown(toggleKey))
        {
            Toggle();
        }
    }

    /// <summary>
    /// 表示状態を反転させる(表示中なら非表示、非表示なら表示)。
    /// </summary>
    public void Toggle()
    {
        ApplyVisibility(!IsVisible);
    }

    /// <summary>
    /// 明示的に表示状態を設定する。
    /// 外部スクリプトやUIボタンからも呼び出せるように public にしている。
    /// </summary>
    /// <param name="visible">true なら表示、false なら非表示</param>
    public void SetVisible(bool visible)
    {
        ApplyVisibility(visible);
    }

    /// <summary>
    /// 実際に GameObject の SetActive を行う内部処理。
    /// </summary>
    /// <param name="visible">true なら表示、false なら非表示</param>
    private void ApplyVisibility(bool visible)
    {
        if (targetRoot == null)
        {
            Debug.LogWarning(
                $"[VisibilityToggle] targetRoot が設定されていません。GameObject: {name}");
            return;
        }

        // 既にその状態なら何もしない(余計な再アクティブ化を避ける)
        if (targetRoot.activeSelf == visible)
        {
            IsVisible = visible;
            return;
        }

        targetRoot.SetActive(visible);
        IsVisible = visible;
    }

    /// <summary>
    /// 外部から一時的に入力受付を有効/無効にするための関数。
    /// 例:ゲーム中は true、カットシーン中は false にするなど。
    /// </summary>
    /// <param name="enabled">true で入力受付ON、false でOFF</param>
    public void SetInputEnabled(bool enabled)
    {
        enableInput = enabled;
    }
}

使い方の手順

ここからは、具体的な使用例として「Escapeキーでポーズメニュー(親オブジェクト)を表示/非表示する」ケースを想定して説明します。

  1. メニュー用の親オブジェクトを用意する
    • ヒエラルキーで Canvas を作成し、その中に「PauseMenu」などのUIをまとめた空の親オブジェクトを作成します。
    • 例:
      Canvas
      └─ PauseMenu(Panel や Text, Button などを子に持つ)
    • この PauseMenu が「表示/非表示を切り替えたい親」です。
  2. VisibilityToggle コンポーネントをアタッチする
    • ヒエラルキーで PauseMenu オブジェクトを選択します。
    • Add Component から VisibilityToggle を追加します。
    • インスペクターで以下を設定します:
      • Toggle KeyEscape(デフォルトのままでOK)
      • Target Root:空欄なら自動で PauseMenu が入るので、そのままでOK
      • Start Visiblefalse(ゲーム開始時は非表示にしたい場合)
  3. 初期状態を確認する
    • シーンを再生すると、Start Visiblefalse の場合、PauseMenu が非表示の状態でスタートします。
    • ゲーム中に Escape キーを押すと、PauseMenu が表示され、もう一度押すと非表示になります。
  4. 別の用途にも流用してみる
    例えば次のようなオブジェクトにも同じコンポーネントをそのまま使えます:
    • ミニマップのON/OFF
      MiniMapRoot という親オブジェクトを作成し、そこにミニマップUIをまとめる。
      VisibilityToggle をアタッチし、Toggle KeyKeyCode.M に変更。
    • デバッグ用オーバーレイのON/OFF
      DebugOverlay オブジェクトを用意し、FPS表示やログをまとめる。
      VisibilityToggle をアタッチし、Toggle KeyKeyCode.F1 に設定。
    • 敵AIの「視界コーン」表示のON/OFF
      – 敵キャラの子に「VisionCone」オブジェクト(メッシュやSprite)を置いておく。
      – その VisionConeVisibilityToggle をアタッチし、KeyCode.V などでデバッグ表示を切り替える。

メリットと応用

VisibilityToggle のように、「表示/非表示の切り替え」だけに責務を絞ったコンポーネントにしておくと、プレハブ管理やレベルデザインがかなり楽になります。

  • プレハブ化しやすい
    メニューやミニマップ、設定画面などを「UI一式 + VisibilityToggle」をセットにしてプレハブ化しておけば、別シーンにドラッグ&ドロップするだけで同じ挙動を再利用できます。
  • レベルデザイナーがノーコードで制御しやすい
    キーの種類や初期表示状態はインスペクターから変更できるので、プログラマーでなくても「このシーンではミニマップを最初から表示しておきたい」などの調整が簡単です。
  • 責務が分離されているので保守が楽
    「ポーズメニューの見た目を変えたい」「キー設定を変えたい」といった変更が、他のゲームロジックに影響しません。
    逆に、プレイヤー制御側のスクリプトをいじっても、メニューの表示切替ロジックを壊しにくくなります。

さらに、ちょっとした改造で応用範囲を広げられます。例えば、表示切替のたびにサウンドを鳴らしたい場合は、次のようなメソッドを追加できます。


[SerializeField]
private AudioSource toggleAudioSource;
// メニュー開閉時に鳴らすサウンド用 AudioSource。
// インスペクターで任意のSEを設定しておく。

/// <summary>
/// 表示状態が変化した直後に呼び出すフック。
/// サウンド再生やアニメーション再生などをここで行う。
/// </summary>
/// <param name="visible">true なら表示になった直後、false なら非表示になった直後</param>
private void OnVisibilityChanged(bool visible)
{
    // サウンド再生の例
    if (toggleAudioSource != null)
    {
        toggleAudioSource.Play();
    }

    // ここにアニメーション再生などを追加してもOK
}

そして、先ほどの ApplyVisibility の末尾で OnVisibilityChanged(IsVisible); を呼ぶようにすれば、
「表示切替 + 演出」をきれいに分離したまま、必要な演出だけを後付けできます。

このように、1コンポーネント1責務を意識して小さなスクリプトを積み重ねていくと、プロジェクト全体の見通しがどんどん良くなっていきます。
メニューやUIの表示制御に悩んでいたら、まずはこの VisibilityToggle から導入してみてください。