本文へスキップ
Unity基礎

Unityのポーズメニューを作る|timeScaleで停止と再開を切り替える

Unity 6とuGUIでポーズ・再開ボタンを実装します。UIの配置、停止前のtimeScaleの保存、解除漏れへの対処、停止中も動く処理の違いをコードと図で確認します。

公開日: 2026-10-24#Unity#uGUI#CSharp#ポーズ#初心者
ガイドキャラクター
Unity 6とuGUIでポーズ・再開ボタンを実装します。UIの配置、停止前のtimeScaleの保存、解除漏れへの対処、停止中も動く処理の違いをコードと図で確認します。
学習者キャラクター
準備するものと、動作確認で見る結果を先に確認してから進めます。
Unityのポーズメニューを作る|timeScaleで停止と再開を切り替えるの手順と判断条件を示す説明図
仕組みを説明する模式図です。エディタのスクリーンショットではありません。小さい画面では横にスクロールできます。設定値と手順は本文で確認してください。

この記事で作るもの

ゲーム画面の「ポーズ」を押すとメニューを開き、「再開」を押すとゲームを続けられる仕組みを作ります。対象はUnity 6・uGUIを使う、1人用のローカルゲームです。キーボード入力を直接読まず、画面のボタンで操作します。

Time.timeScaleはゲーム内の時間倍率です。通常の1から0にすると、時間に合わせて動く処理を止められます。再開時には停止前の値へ戻します。最初から0.5で動いていたゲームを、再開時に勝手に1にしないためです。

1. ボタンとパネルを配置する

まず、最初のUIボタンの手順を参考に、CanvasとEventSystemを用意します。Canvasには有効なGraphic Raycasterが必要です。UIがクリックできる状態になってから次へ進みます。Graphic Raycasterの公式資料

Hierarchyを次の構成にします。PauseControllerはPausePanelの外に置き、常に有効にしておきます。

Main Camera
Cube                     ← 回転を確認する3Dオブジェクト
PauseController          ← ButtonPauseMenu.cs
Canvas                   ← Screen Space - Overlay
  PauseButton            ← 表示文字「ポーズ」
  PausePanel             ← 全画面に広げた半透明のImage
    ResumeButton         ← 表示文字「再開」
EventSystem
  1. PauseButtonを画面右上に配置します。サイズは幅160・高さ48程度にします。
  2. PausePanelのRectTransformを全方向ストレッチにし、上下左右の余白を0にします。Imageの色は半透明の黒にし、Raycast Targetをオンにします。
  3. ResumeButtonをPausePanelの中央に配置します。Canvas内ではパネルをポーズボタンより後に置き、手前に表示します。
  4. 下のコードをButtonPauseMenu.csとして保存し、PauseControllerへ付けます。
  5. InspectorのPause Button、Resume Button、Pause Panelへ対応するオブジェクトをドラッグします。

ボタンのOn Click()は空のままにします。コード内でクリックを登録するため、Inspectorでも同じ処理を登録すると二重に呼ばれます。EventSystemの入力モジュールは、プロジェクトで使用する入力方式に合わせてください。

2. 停止と再開のコードを書く

using UnityEngine;
using UnityEngine.UI;

public sealed class ButtonPauseMenu : MonoBehaviour
{
    [SerializeField] private Button pauseButton;
    [SerializeField] private Button resumeButton;
    [SerializeField] private GameObject pausePanel;

    private bool isPaused;
    private float previousTimeScale = 1f;

    private void OnEnable()
    {
        if (pauseButton == null || resumeButton == null || pausePanel == null)
        {
            Debug.LogError("ポーズUIの参照を3つ設定してください。", this);
            enabled = false;
            return;
        }

        pausePanel.SetActive(false);
        pauseButton.interactable = true;
        pauseButton.onClick.AddListener(Pause);
        resumeButton.onClick.AddListener(Resume);
    }

    private void OnDisable()
    {
        if (pauseButton != null) pauseButton.onClick.RemoveListener(Pause);
        if (resumeButton != null) resumeButton.onClick.RemoveListener(Resume);
        Resume();
    }

    public void Pause()
    {
        if (!isActiveAndEnabled || isPaused) return;

        previousTimeScale = Time.timeScale;
        isPaused = true;
        Time.timeScale = 0f;
        pauseButton.interactable = false;
        pausePanel.SetActive(true);
    }

    public void Resume()
    {
        if (isPaused)
        {
            Time.timeScale = previousTimeScale;
            isPaused = false;
        }

        if (pausePanel != null) pausePanel.SetActive(false);
        if (pauseButton != null) pauseButton.interactable = true;
    }
}

isPausedで二重停止を防ぎます。停止済みの状態で再びpreviousTimeScaleに0を保存してしまうと、再開しても動かなくなるからです。OnDisable()でも再開処理を呼び、ポーズ中に管理オブジェクトを無効にした場合の解除漏れを防ぎます。

参照の確認とクリック登録はOnEnable()で行います。参照が足りない場合はエラーを出して自分を無効にし、登録処理へ進みません。参照を直して再有効化したときにも、同じ確認が働きます。OnEnableの公式資料

この教材では、時間倍率を変更する管理役をこのコンポーネント1つに限定します。別のポーズ機能やスローモーション機能も同時に時間倍率を変更する場合は、共通の管理役へまとめてから組み合わせてください。

3. 止まるオブジェクトを用意する

空の3DシーンならCubeを作り、Main Cameraから見える位置に置きます。次のコードを別ファイルPauseDemoSpinner.csに保存し、Cubeへ付けます。

using UnityEngine;

public sealed class PauseDemoSpinner : MonoBehaviour
{
    private void Update()
    {
        transform.Rotate(0f, 90f * Time.deltaTime, 0f);
    }
}

この回転はTime.deltaTimeを掛けているため、ポーズ中は回転量が0になります。Update()自体の呼び出しを止めているわけではありません。

4. 通常・ポーズ・再開を確認する

操作期待する結果
Playを開始するCubeが回転し、ポーズパネルは非表示
「ポーズ」を押すCubeが止まり、パネルと再開ボタンが表示される
「再開」を押すパネルが閉じ、Cubeが再び回転する
停止と再開を数回繰り返す毎回再開でき、クリックの登録が増えない
ポーズ中にPauseControllerを無効にする保存した時間倍率へ戻る
PauseControllerを再び有効にするパネルは閉じたまま、ポーズボタンを再び使える

timeScaleだけでは止まらないもの

timeScale = 0ではFixedUpdate()やWaitForSecondsの待機が進まなくなります。一方、Time.unscaledDeltaTimeで進める処理は時間倍率の影響を受けません。停止中にも動くメニュー演出には、非スケール時間を使う方法があります。Time.timeScaleの公式資料、Time.unscaledDeltaTimeの公式資料

このコードはBGMの停止や、プレイヤー入力の一括無効化までは行いません。移動コードが毎フレーム固定距離を加算している場合や、時間と無関係に攻撃を発生させる場合は、別途ポーズ状態を確認する必要があります。オンラインゲーム全体の停止にも使えません。

うまく動かないとき

  • 再開ボタンが押せない:CanvasのGraphic Raycaster、EventSystem、入力モジュール、UIの重なりを確認します。Panelの子にResumeButtonがあることも確認します。
  • 参照未設定のエラーで止まった:3つの参照を登録し、ButtonPauseMenuコンポーネントを有効にし直します。編集時に直した場合はPlayもやり直します。
  • Play開始直後に管理役まで消える:PauseControllerをPausePanelの子にしていないか確認します。
  • Cubeが止まらない:回転コードにTime.deltaTimeが掛かっているか確認します。
  • 再開しても動かない:Consoleのエラーと、別のスクリプトによるTime.timeScaleの変更を確認します。

UIの受付状態をさらに学ぶなら、購入ボタンの連打防止へ進めます。

学習者キャラクター
今回の内容で覚えておくポイントを確認したいです。
ガイドキャラクター
Unity 6とuGUIでポーズ・再開ボタンを実装します。UIの配置、停止前のtimeScaleの保存、解除漏れへの対処、停止中も動く処理の違いをコードと図で確認します。
この記事のまとめを案内するガイドキャラクター

この記事のまとめ

Unity 6とuGUIでポーズ・再開ボタンを実装します。UIの配置、停止前のtimeScaleの保存、解除漏れへの対処、停止中も動く処理の違いをコードと図で確認します。