市販ゲームであればStartボタンを押した時にゲームの中断が出来たりします。
そこで、今回はUnityでゲームの中断が出来るようにスクリプトを組んでみようと思います。
まずは中断した時に「中断中ですよ」というのがわかるUIを作成してみます。
↑はポーズ中か解りやすくする為、時間経過の表示を行っていますが今回の記事ではやりません。
時間の計測については
を参照してください。
中断中だとわかる為のUIを作成する
ヒエラルキー上で右クリック→UI→Panelを選びます。
Panelを選ぶとCanvasも作られます。Panelの子要素にUI→Textでテキストを作成します。
上のような階層が出来上がりました。(CanvasはPauseUIという名前に変更してあります)
Textのインスペクタでは表示する文字とテキストのサイズ、BestFitにチェックを入れておきます。
Alignmentも中央の真中表示にします。
Panelの色やTextの色等を変更してご自分の好きなようにカスタマイズしてください。
出来たらAssetsフォルダにPauseUIをドラッグ&ドロップしてプレハブにします。
このプレハブをスクリプトから出現させる事にします。
プレハブ化が出来たらシーン上のPauseUIは削除してかまいません。
ゲームを中断させるスクリプトを作成
では次に中断をスクリプトで実装してみます。
PauseScriptを作成しMainCamera等のゲームシーンで常にアクティブで存在しているゲームオブジェクトに取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using UnityEngine; using System.Collections; public class PauseScript : MonoBehaviour { [SerializeField] // ポーズした時に表示するUIのプレハブ private GameObject pauseUIPrefab; // ポーズUIのインスタンス private GameObject pauseUIInstance; // Update is called once per frame void Update () { if (Input.GetKeyDown ("q")) { if (pauseUIInstance == null) { pauseUIInstance = GameObject.Instantiate (pauseUIPrefab) as GameObject; Time.timeScale = 0f; } else { Destroy (pauseUIInstance); Time.timeScale = 1f; } } } } |
UpdateメソッドでQキーが押された時にUIのプレハブがインスタンス化されていなければインスタンス化を行います。
Time.timeScaleを0にし、ゲームの流れを止めます。
すでにUIがインスタンス化されていればUIのインスタンスを削除します。
時間が中断していた場合は再開させるのでTime.timeScaleを1にします。1は通常の時間の流れになります。
これでゲームの中断が出来るようになりました。
今回のポーズ機能のサンプルのヒエラルキーは、
↑のようになります。
MainCameraにPauseScriptを取り付け、プレハブ化したPauseUIを設定します。
ステータス画面を表示して武器を切り替えたい場合等にTime.timeScaleの値を0にすれば一時停止しながら装備を変えることが出来ます。
リアルな感じを出したい場合は装備切り替え画面になっていても時間は流したままで、敵の攻撃を受けるというのもいいかもしれません。
上が実際の画像です。
あらかじめPauseUIをヒエラルキーに置いておく方法
PauseUIをプレハブからインスタンス化するのではなく、あらかじめヒエラルキー上に非表示の状態で置いておき、それをオン・オフするというやり方もあります。
↑のようにPauseUIをヒエラルキー上に配置しておき、PauseUIのインスペクタでチェックをを外し非表示にしておきます。
MainCameraにPauseScript2を作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | using UnityEngine; using System.Collections; public class PauseScript2 : MonoBehaviour { // ポーズした時に表示するUI [SerializeField] private GameObject pauseUI; // Update is called once per frame void Update () { if (Input.GetKeyDown ("q")) { // ポーズUIのアクティブ、非アクティブを切り替え pauseUI.SetActive (!pauseUI.activeSelf); // ポーズUIが表示されてる時は停止 if(pauseUI.activeSelf) { Time.timeScale = 0f; // ポーズUIが表示されてなければ通常通り進行 } else { Time.timeScale = 1f; } } } } |
PauseScript2にPauseUIゲームオブジェクトを設定します。
こちらの方がインスタンス化するよりわかりやすいかもしれません。
Time.timeScaleを0にするだけじゃダメ
すべてが簡単にいきそうですが、Time.timeScaleを0にした場合FixedUpdateは呼び出されなくなりますが、Updateメソッドは呼び出されます。
つまり、FixedUpdateを使った移動やアニメーションなどの再生は止まるんですが、
Updateメソッド内で行っているキーボードの受付やOnAnimatorIKなどはそのまま呼び出されています。
その為Updateメソッド内の処理を行いたくない場合は条件文を追加し、
1 2 3 4 5 6 7 8 9 10 | void Update () { if (Mathf.Approximately(Time.timeScale, 0f)) { return; } ・ ・ ・ } |
という風にUpdateメソッドの最初でTime.timeScaleが0だった時はその後の処理を何もしないようにしておきます。
全然気づかずやっていたら中断中にキャラクターがブレていたので気づきました・・・・(^_^;)
よく考えたら当たり前ですよね・・、中断させているスクリプトでQが押されたかどうかでストップしているのに、Updateまで止まってたらどうやって再開させてたんだって話に・・・・・(+_+)
ボタン操作で中断させる
この項目は2018/10/06に追記した内容です。
キーボードでTime.timeScaleを操作しましたが、RPG等でメニューのアイテムボタンを押した時等にもTime.timeScaleを0にし、アイテムメニューを終えたら1に戻したい場合もあると思います。
そこで簡単なサンプルを作ってみたので参考にしてみてください。
まずはヒエラルキー上で右クリック→UI→Canvasを選択し、名前をMenuUIとします。
MenuUIの子要素にUI→Panelを2つ作成し、名前をMenuPanelとItemPanelとします。
MenuPanelの子要素にUI→Buttonを2つ作成し、名前をItemButtonにし子要素のTextに「アイテムメニュー」という文字列を入れます。
もう一つをReStartButtonとし子要素のTextに「ゲーム再開」という文字列を入れます。
ItemPanelとReStartButtonは最初は表示しない為、インスペクタの名前の横のチェックを外しておきます。
作ったUIは
のような感じになりました。
次にMenuUIゲームオブジェクトに新しくPauseScript3というスクリプトを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class PauseScript3 : MonoBehaviour { // アイテムメニューを開くボタン [SerializeField] private GameObject itemButton; // ゲーム再開ボタン [SerializeField] private GameObject reStartButton; // アイテムメニューパネル [SerializeField] private GameObject itemPanel; public void StopGame() { Time.timeScale = 0f; itemButton.SetActive(false); reStartButton.SetActive(true); itemPanel.SetActive(true); } public void ReStartGame() { itemPanel.SetActive(false); reStartButton.SetActive(false); itemButton.SetActive(true); Time.timeScale = 1f; } } |
MenuUIののインスペクタでPauseScript3のitemButtonにはItemButtonゲームオブジェクト、reStartButtonにはReStartButtonゲームオブジェクト、itemPanelにはItemPanelゲームオブジェクトをそれぞれ設定します。
StopGameメソッドはItemButtonボタンを押した時に実行させ、ReStartButtonメソッドはReStartButtonボタンを押した時に実行させるようにします。
ItemButtonとReStartButtonのOnClickにMenuUIに取り付けたPauseScript3のメソッドを指定し、ボタンを押した時に実行するようにします。
内部の処理は今までやってきた事と同じで、後はUIのパネルやボタンの表示と非表示をしているだけです。
今回はMenuPanelに「アイテムボタン」と「ゲーム再開ボタン」を切り替えて表示していますが、アイテムメニュー内に「ゲーム再開ボタン」を作ってPauseScript3のReStartGameメソッドを実行するようにしても出来ます。
ボタン操作で中断するサンプルの実行
機能が出来たので実行してみましょう。
上のようになりました。
アイテムメニューを開いている時は時間が進んでおらず、「ゲーム再開ボタン」を押したら再び時間が進んでいます。