【Unity】RPGを作るチュートリアルその79 メニュー画面のゲーム終了機能を実装

【Unity】RPGを作るチュートリアルその79 メニュー画面のゲーム終了機能を実装

シンプルなRPGをUnityで作るチュートリアルシリーズの79回目です。

第78回ではメニュー画面のセーブの機能について、セーブ機能の呼び出し動作を作成しました。

今回はメニュー画面のゲーム終了機能について、UIと動作部分の両方を実装していきましょう。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

シンプルなRPGを作る上でどんな作業が必要か、どんな順番で作っていくと良さそうか、別ページで検討しました。基本的にこの流れに沿って進めていきます。

 

チュートリアルの一覧

このシリーズ全体の一覧は以下のページにまとめています。

 

前回の内容

前回はメニュー画面のセーブの機能について、セーブ機能の呼び出し動作を作成しました。

 

メニュー画面の実装方針

メニューの表示を確認
メニューの表示を確認

 

メニュー画面では、

  • アイテムの使用
  • 魔法の使用
  • 装備の選択
  • ステータスの確認
  • セーブ
  • ゲームの終了 ◀︎いまここ
  • メニューを閉じる

の項目を作成します。ゲームを終了する機能については、メニュー画面から呼び出すケース、タイトル画面から呼び出すケースがありますが、今回のチュートリアルではメニュー画面から直接終了できるようにします。ゲーム機でゲームを動かしている場合には本体側から終了できることも多いのですが、WindowsなどPCでゲームを動かしている場合には、ゲーム内で終了する方法を提供しておくとユーザ側としても安心できます。ショートカットキーとして Alt + F4で終了できるとはいえ、知らないとどうしようもないこともあるため、分かりやすい位置に終了のメニューがあると良いかと思います。

また、実装方針の中で「メニューを閉じる」を入れていましたが、メニューを閉じる機能については既に実装されているので、今回の作業でメニュー画面については完了になりそうです。必要な作業が発生したら都度変更を加えていきたいと思います。

 

メニューのゲーム終了画面の作成

ゲーム終了画面……といっても、メッセージウィンドウを使って「ゲームを終了しますか?」と表示して、はい、いいえの選択肢で選択するようにしたいと思います。はい、いいえの選択肢はイベントでも使えるので、メッセージ表示のクラスを改修して機能を追加しておきたいと思います。

イメージとしては、はい、いいえの選択肢のウィンドウもメッセージウィンドウと同じように「Canvas」の下に配置して、必要に応じて表示するようにします。選択肢の表示中はメッセージの改ページをしないように専用のメソッドを用意したいと思います。

 

UIの作成

まずは選択肢のUIから作成していきましょう。Hierarchyウィンドウから「Canvas」の下にある「MessageWindowParent」を複製して、名前を [OptionWindowParent] に変更します。

メッセージウィンドウを複製
メッセージウィンドウを複製

 

作成した「OptionWindowParent」ではInspectorウィンドウから「MessageUIController」のコンポーネントを外します。

コンポーネントを削除
コンポーネントを削除

 

「Rect Transform」のコンポーネントでは、アンカーを画面右上になるようにします。位置とサイズは「Pos X」を [-32] に、「Pos Y」を [-32] に、「Width」を [156] に、「Height」を [120] にします。

Rect Transformの設定
Rect Transformの設定

 

Hierarchyウィンドウで、はい、いいえそれぞれの選択肢について親オブジェクトを作成します。まずは「はい」の親オブジェクトを作成し、それを複製する形にしましょう。「OptionWindowParent」の子オブジェクトとして空のゲームオブジェクトを作成します。名前は [YesParent] にしました。続いて、メニューのトップ画面からいずれかの項目のカーソルとテキストを複製して、「YesParent」の子オブジェクトにします。それぞれ名前は [CursorItem][OptionText] にしました。

選択肢の親オブジェクト
選択肢の親オブジェクト

 

作成した「YesParent」では、アンカーを左上にして、「Height」を [64] にします。

アンカーとサイズの設定
アンカーとサイズの設定

 

次に「CursorItem」を選択し、アンカーを左中段に再設定して、「Pos X」を [24] にします。複製して移動させた時に位置がずれているかと思いますので、選択肢のウィンドウに合わせて配置します。

カーソルの位置
カーソルの位置

 

続いて「OptionText」を選択し、アンカーを左中段に、「Pos X」を [52] に、「Width」を [128] に、「Height」を [48] にします。テキストの内容は「はい」を記載します。

テキストの位置とサイズ
テキストの位置とサイズ

 

設定を行なったら「YesParent」をPrefabにしましょう。Projectウィンドウから「Assets/Prefabs」のフォルダを開き、「YesParent」をドラッグ&ドロップしてPrefabを作成します。作成したPrefabの名前は [OptionItem] にしました。

選択肢のPrefab
選択肢のPrefab

 

Prefabを作成後、「YesParent」を複製し、名前を [NoParent] に変更します。

ゲームオブジェクトの複製
ゲームオブジェクトの複製

 

作成した「NoParent」では、「Pos Y」を [-48] にします。テキストの内容は「いいえ」を記載します。

位置の設定
位置の設定

 

ここまでの設定を行うと以下のように表示されるかと思います。

選択肢ウィンドウは右上に
選択肢ウィンドウは右上に

 

メニューのゲーム終了画面の動作を作成

次にゲーム終了画面の動作を作成していきます。作成したいのは、

  • 選択肢ウィンドウの選択結果のコールバック用インタフェース
  • 選択肢の項目を制御するクラス
  • 選択肢ウィンドウのUIを制御するクラス
  • 選択肢ウィンドウを制御するクラス
  • ゲーム終了画面を制御するクラス

です。これに合わせて、メッセージウィンドウの制御クラスやメニュー画面の制御クラスも変更していきます。

 

選択肢ウィンドウの選択結果のコールバック用インタフェースの作成

選択肢ウィンドウで、はい、いいえのどちらを選んだのか通知するためのインタフェースを作成します。選択されたことを通知するコールバックメソッドの引数として、選択肢のIDを渡すようにします。今回は仕様として固定で0がはい、1がいいえにします。選択肢が増える場合はそれに伴って渡す数値を変えていくと良いかと思います。

Projectウィンドウから「Assets/Scripts/Message」のフォルダを開き、空のスクリプトファイルを作成します。名前は [IOptionCallback] にしました。

コールバック用のメソッド
コールバック用のメソッド

 

作成した「IOptionCallback」の中身は以下のように記載しました。

コールバックする際には、選択された引数を元に、呼び出し元で判断するようにしていきます。

 

選択肢の項目を制御するクラスの作成

個別の選択肢でカーソルの表示を切り替えるため、項目を制御するためのクラスを作成します。選択肢ウィンドウのUIから項目制御のクラスをリストで参照させることで、項目数が増えても対応できるようにしたいと思います。今回のチュートリアルだと、お店の処理で選択肢が3ついりそうなので、それを踏まえて実装していきます。

Projectウィンドウから「Assets/Scripts/Message」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [OptionItemController] にしました。

選択肢の制御クラス
選択肢の制御クラス

 

作成した「OptionItemController」の中身は以下のように記載しました。

今回はテキストの文字を固定で入れてUIを作成するため、カーソルへの参照をアサインするようにしています。もし選択肢の文字列を変更する場合は、テキストへの参照も入れると良いかもしれません。その場合は、UIを制御するクラスにて選択肢ウィンドウの幅も自動で設定できるようにしておくとさらに良いと思います。

 

選択肢ウィンドウのUIを制御するクラス

選択肢ウィンドウで個別の選択肢のカーソル表示を切り替えるため、UIを制御するクラスを作成します。このクラスでリストとして選択肢の制御クラスを参照し、対応するUIを操作していくイメージです。

Projectウィンドウから「Assets/Scripts/Message」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [OptionUIController] にしました。

UIの制御クラス
UIの制御クラス

 

作成した「OptionUIController」の中身は以下のように記載しました。

このクラスからは選択肢ウィンドウ内に存在する選択肢をリストとして参照します。リストに入れた順番でインデックスを判断するため、上から順番に入れるようにします。カーソルの移動時にリストのカウントを参照したいので、プロパティとして「OptionCount」を作成しています。

ShowCursor()のメソッドでは引数のインデックスに応じてカーソルを表示するようにしています。

 

選択肢ウィンドウを制御するクラス

選択肢ウィンドウでキー入力を検知したり、選択結果をコールバック先に通知したりするクラスです。UIのリストの要素数を取得して、選択されている選択肢のIDを判断していきたいと思います。

Projectウィンドウから「Assets/Scripts/Message」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [OptionWindowController] にしました。

ウィンドウの制御クラス
ウィンドウの制御クラス

 

作成した「OptionWindowController」の中身は以下のように記載しました。

フィールドとしては選択肢ウィンドウのUIを制御するクラスへの参照を保持し、SetUpController()のメソッドで渡されたコールバック先も保持するようにします。

SetUpController()のメソッドではコールバック先の登録のほかに、初期状態で選択されている選択肢を指定できるようにします。呼び出し元で指定しなければ先頭の選択肢が選ばれます。ゲーム終了画面ではデフォルトで「いいえ」が選択されている状態にします。

Update()からはキー入力を確認するCheckKeyInput()を呼んでいます。選択肢ウィンドウはメニュー画面からは独立しているため、このウィンドウ内のフラグだけを使って選択できるかどうかを判断します。上下のキー入力によってカーソルを移動させる部分については他の選択ウィンドウと同様です。

決定ボタンが押された際には、コールバック先に選択されたインデックスを渡して選択肢ウィンドウを非表示にします。キャンセルボタンが押された場合には、提示された選択肢とは区別できるように「-1」を渡すようにします。基本的には「いいえ」と同じ動作になるようにコールバック先で分岐させることになることが多いかと思いますが、場合によっては選択しない場合の分岐もあると便利なのでこのようにしています。

 

ゲーム終了画面を制御するクラス

メニュー画面にて、ゲーム終了画面を制御するクラスを作成しましょう。この画面では固有のUIは使わず、選択肢ウィンドウ、メッセージウィンドウを使って確認を行なっていきます。

Projectウィンドウから「Assets/Scripts/Menu」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [MenuQuitGameWindowController] にしました。

ゲーム終了ウィンドウの制御クラス
ゲーム終了ウィンドウの制御クラス

 

作成した「MenuQuitGameWindowController」の中身は以下のように記載しました。

フィールドとして「MenuManager」への参照を保持する点は他のメニューと同様で、画面のUIの制御クラスへの参照に変わってメッセージウィンドウ、選択肢ウィンドウへの参照を保持するようにしています。

ゲーム終了画面を表示する際にはShowWindow()のメソッドが呼ばれ、そこからメッセージウィンドウに確認のメッセージを表示します。ShowMessageWindow()では汎用のメッセージ表示用メソッドを呼び出します。このメソッドは後ほど追加しますが、メッセージ表示からコールバックまでの時間を指定できるようにしています。ここでは待ち時間を0にしてメッセージの表示が完了したタイミングでOnFinishedShowMessage()の中のShowOptionWindow()のメソッドを呼んでいます。このメソッドで選択肢ウィンドウを表示するようにしています。

選択肢ウィンドウのコールバックでOnSelectedOption()が呼ばれた際、「いいえ」を選択したり、キャンセルボタンを押した場合はメニューのトップ画面に戻るようにしています。「はい」を選択した場合は、エディタならプレイモードの終了、ビルドで実行している場合はアプリケーションを終了するようにしています。Unityのエディタで実行している場合はApplication.Quit()で終了できないので、エディタで確認する場合に備えてプラットフォームごとに分岐させるようにしています。

 

既存のクラスの変更

作成したクラスに合わせて、既存のクラスも変更を加えていきます。変更したいクラスは以下の通りです。

  • MenuManager
  • MapMessageWindowController

 

MenuManagerの変更

「MenuManager」では今回作成した「MenuQuitGameWindowController」への参照用フィールドを追加し、メニュー選択時の分岐を追加していきます。

まずはフィールドの追加です。既存の「_menuSaveWindowController」の下にフィールドを追加しました。

 

HandleMenu()のメソッドでは、セーブ画面の分岐にて、この後追加するShowQuitMenu()を呼び出すようにします。

 

既存のShowSaveMenu()のメソッドの下にShowQuitMenu()を追加します。

 

スクリプトの末尾にOnQuitCanceled()のメソッドを追加します。

 

MapMessageWindowControllerの変更

「MapMessageWindowController」ではクラスの末尾に汎用のメッセージ表示用メソッドを追加します。他のメソッドはこのクラス内で情報をまとめてメッセージを生成していますが、汎用のメソッドでは引数で直接メッセージを渡せるようにします。また、メッセージ表示後にコールバックを呼び出すまでの時間も引数で指定できるようにしています。

 

スクリプトのアタッチ

スクリプトファイルを保存したら、ゲームオブジェクトにアタッチします。

 

選択肢ウィンドウ関連のアタッチ

各選択肢を制御するクラスからアタッチしていきます。

 

選択肢の制御クラス

Hierarchyウィンドウにて「YesParent」を選択し、Inspectorウィンドウから「OptionItemController」のスクリプトをアタッチします。カーソルのゲームオブジェクトもアサインしておきましょう。

項目の制御クラスをアタッチ
項目の制御クラスをアタッチ

 

「Override」のプルダウンから [Apply All] を選択して設定を反映させます。これにより「NoParent」の方にも反映されます。

設定の反映
設定の反映

 

選択肢ウィンドウのUIの制御クラス

続いてHierarchyウィンドウから「OptionWindowParent」を選択し、「OptionUIController」のスクリプトをアタッチします。「Option Items」のリストには、「YesParent」と「NoParent」をアサインします。

UIの制御クラスをアタッチ
UIの制御クラスをアタッチ

 

選択肢ウィンドウの制御クラス

次に、Hierarchyウィンドウから「Managers」を選択し、子オブジェクトとして空のゲームオブジェクトを作成します。名前は [OptionWindowController] にしました。

ゲームオブジェクトの作成
ゲームオブジェクトの作成

 

作成した「OptionWindowController」を選択し、Inspectorウィンドウから同名のスクリプトをアタッチします。先ほどアタッチしたUIの制御クラスについてもアサインしておきましょう。

ウィンドウの制御クラス
ウィンドウの制御クラス

 

メニュー関連のアタッチ

ゲーム終了メニューのスクリプトについてもアタッチしていきます。Hierarchyウィンドウから「MenuManager」のゲームオブジェクトを選択し、子オブジェクトを作成します。名前は [MenuQuitGameWindowController] にしました。

ゲームオブジェクトの作成
ゲームオブジェクトの作成

 

Inspectorウィンドウから同名のスクリプトをアタッチし、メッセージウィンドウ、選択肢ウィンドウへの参照もアサインします。

終了メニューのスクリプト
終了メニューのスクリプト

 

続いて「MenuManager」のゲームオブジェクトを選択し、「MenuQuitGameWindowController」への参照をアサインします。

終了メニューのアサイン
終了メニューのアサイン

 

動作確認

スクリプトをアタッチしたら動作確認に入ります。

  • MenuBackground
  • MessageWindowParent
  • OptionWindowParent

についてはそれぞれ非表示にしておきます。

ゲームを実行し、メニューを開いて「ゲームの終了」を選択し、ゲーム終了の確認画面が表示されることを確認します。選択肢ウィンドウではデフォルトで「いいえ」が選択されていて、「いいえ」で決定キーを押すか、キャンセルボタンを押すとメニューのトップ画面に戻ることを確認しましょう。また、「はい」を選択した場合は、Unityエディタのプレイモードが終了することも確認します。実機での動作はビルドした時に確認しましょう。

ゲーム終了の確認画面
ゲーム終了の確認画面

 

今回のブランチ

 

まとめ

今回はメニュー画面のゲーム終了の機能について作成しました。これでメニュー画面に関しては一通り実装できたので、次回からはNPCとの会話やお店、場所移動など、イベント関連の実装に入ります。メニューは作るUIの多さの面で山でしたが、イベントに関してはフラグとも関連するので機能面での大きな山になっています(白目)

次回はイベント機能に関して基本方針を考えていきましょう。

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!