Unityを触り始めた頃にありがちなのが、メニューの開閉やポーズ処理、UIの表示切替を全部ひとつの巨大なUpdate関数に書いてしまうパターンです。
例えばこんな感じですね:
// 悪い例(なんでもかんでも1つのスクリプトに詰め込む)
void Update()
{
// プレイヤー移動
// 攻撃処理
// カメラ制御
// メニュー表示切替
// ポーズ処理
// ...
}
これを続けてしまうと、
- どこを触れば何が変わるのか分かりにくい
- 別シーンや別プロジェクトで再利用しづらい
- ちょっとした仕様変更でバグを生みやすい
という「神クラス(Godクラス)」まっしぐらになってしまいます。
そこで今回は、「メニュー(親オブジェクト)の表示/非表示だけ」を担当する小さなコンポーネントとして、VisibilityToggle を作っていきましょう。
特定のキー入力で、親(メニュー画面など)の表示/非表示を切り替える、シンプルだけど汎用性の高いコンポーネントです。
【Unity】一発キー入力でUIをON/OFF!「VisibilityToggle」コンポーネント
ここでは Unity6(C#)で動作する、コピペで使える完全版コードを掲載します。
新しいInput Systemを使わず、UnityEngine.Input の Input.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キーでポーズメニュー(親オブジェクト)を表示/非表示する」ケースを想定して説明します。
-
メニュー用の親オブジェクトを用意する
- ヒエラルキーで
Canvasを作成し、その中に「PauseMenu」などのUIをまとめた空の親オブジェクトを作成します。 - 例:
Canvas
└─ PauseMenu(Panel や Text, Button などを子に持つ) - この
PauseMenuが「表示/非表示を切り替えたい親」です。
- ヒエラルキーで
-
VisibilityToggle コンポーネントをアタッチする
- ヒエラルキーで
PauseMenuオブジェクトを選択します。 Add ComponentからVisibilityToggleを追加します。- インスペクターで以下を設定します:
- Toggle Key:
Escape(デフォルトのままでOK) - Target Root:空欄なら自動で
PauseMenuが入るので、そのままでOK - Start Visible:
false(ゲーム開始時は非表示にしたい場合)
- Toggle Key:
- ヒエラルキーで
-
初期状態を確認する
- シーンを再生すると、
Start Visibleがfalseの場合、PauseMenuが非表示の状態でスタートします。 - ゲーム中に
Escapeキーを押すと、PauseMenuが表示され、もう一度押すと非表示になります。
- シーンを再生すると、
-
別の用途にも流用してみる
例えば次のようなオブジェクトにも同じコンポーネントをそのまま使えます:- ミニマップのON/OFF
–MiniMapRootという親オブジェクトを作成し、そこにミニマップUIをまとめる。
–VisibilityToggleをアタッチし、Toggle KeyをKeyCode.Mに変更。 - デバッグ用オーバーレイのON/OFF
–DebugOverlayオブジェクトを用意し、FPS表示やログをまとめる。
–VisibilityToggleをアタッチし、Toggle KeyをKeyCode.F1に設定。 - 敵AIの「視界コーン」表示のON/OFF
– 敵キャラの子に「VisionCone」オブジェクト(メッシュやSprite)を置いておく。
– そのVisionConeにVisibilityToggleをアタッチし、KeyCode.Vなどでデバッグ表示を切り替える。
- ミニマップのON/OFF
メリットと応用
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 から導入してみてください。
