【Unity】RPGを作るチュートリアルその78 メニュー画面のセーブ機能を実装

【Unity】RPGを作るチュートリアルその78 メニュー画面のセーブ機能を実装

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

第77回ではメニューから呼び出すためのセーブ機能について、ロード機能とセットで作成しました。

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

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回はメニューから呼び出すためのセーブ機能について、ロード機能とセットで作成しました。

 

メニュー画面の実装方針

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

 

メニュー画面では、

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

の項目を作成します。メニュー画面からは前回作成したセーブとロード機能のうち、セーブ機能を呼び出していきます。ゲーム内に存在するセーブ枠の情報も画面に表示するので、その機能も実装しましょう。

 

セーブ画面の実装

この画面に関しては、

  • セーブ枠のUIを制御するクラス
  • セーブ画面のUIを制御するクラス
  • セーブ画面のウィンドウを制御するクラス

を作成します。基本方針としてはデータに存在している情報を画面にセットしていく流れになります。

 

セーブ枠のUIを制御するクラスの作成

セーブ枠の名前やレベルなどのUIを制御するクラスから作成していきます。Projectウィンドウから「Assets/Scripts/Menu」のフォルダを開き、MonoBehaviourのスクリプトファイルを作成します。名前は [MenuSaveSlotController] にしました。

セーブ枠の制御クラス
セーブ枠の制御クラス

 

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

セーブ枠の内部にあるテキストへの参照をそれぞれフィールドとして保持します。主にテキストに値をセットするメソッドが中心で、セーブ枠のデータがない場合には各テキストをクリアするメソッドも用意しています。

 

セーブ画面のUIを制御するクラスの作成

次にセーブ画面のUIを制御するクラスを作成していきます。先ほど作成したセーブ枠の制御クラスへの参照を使って、必要な情報を画面内にセットしていく流れになります。

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

UIを制御するクラス
UIを制御するクラス

 

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

画面上部のセーブ画面の説明テキストへの参照と、各セーブ枠の制御クラスへの参照をフィールドとして保持するようにしています。セーブ枠は3つで固定なので、参照も1, 2, 3と若干力技ですが番号をつけて用意しています。

SetDescription()のメソッドでは説明テキストをセットできるようにしています。セーブファイル選択する点、セーブが完了した点などを画面上に表示していきたいと思います。

SetUpControllerList()のメソッドではセーブ枠の制御クラスをリストに追加しています。目的としては各制御クラスに対して一括で操作できるようにしています。今回は直接それぞれの参照をフィールドとして用意していますが、Prefabをインスタンス化する場合などではインスタンス化のタイミングで追加すると良いかと思います。

SetSlotInfo()ではセーブ枠のデータがある場合の処理、SetSlotInfoAsEmpty()ではデータがない場合の処理を行います。それぞれセーブ枠の制御クラスに対してテキストをセットする処理を呼び出していますが、データがない場合は名前テキスト以外を画面に表示しないようにしています。また、名前欄に「NO DATA」と表示して、データがないことを画面上に表示しています。

GetSlotController()のメソッドでは対応するセーブ枠のIDの制御クラスを返すようにしています。フィールドとして対応するものを用意しているので、IDで分岐させて返すようになっています。Prefabを使って動的にセーブ枠を増やしていく場合は、セーブ枠の制御クラスにIDを持たせて、ListのFindメソッドを使って対応するIDのものを返す形にするのが良さそうです。

 

セーブ画面のウィンドウを制御するクラスの作成

続いてセーブ画面のウィンドウを制御するクラスを作成していきます。画面が表示されるタイミングで読み込み済みのセーブファイルから情報を取得し、画面に表示する処理を呼び出していきます。また、キー入力を検知して、選択したファイルにデータを保存する処理も行います。

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

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

 

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

フィールドとしては、他のメニュー項目の制御クラスと同様に、「MenuManager」や対応するUIの制御クラスへの参照を用意しています。また、セーブファイルを読み書きするために「SaveDataManager」への参照を、マップ名を取得するために「MapManager」への参照もそれぞれ追加しています。

セーブファイルがない時の名前表示や、セーブの説明テキスト、セーブ後の表示テキストなどもここで定義しています。

画面を表示するタイミング、セーブが完了したタイミングでSetUpSlotInfo()が呼ばれ、セーブファイルの情報を画面上にセットします。マップIDからマップ名を取得する処理については、のちほど「MapManager」に追加したいと思います。

キー入力の検知では、上キー、下キーでカーソルを移動させ、決定ボタンを押すとSave()のメソッドでセーブ用の処理を呼び出します。セーブの処理が完了した後は、画面上の説明テキストにセーブが完了した旨を表示し、PostSaveDelay()のコルーチンにより2秒後に画面を閉じる処理が呼ばれます。キャンセルボタンを押した時にはメニューのトップでの同フレームの入力検知を防ぐために1フレーム後に画面を閉じます。

 

既存のクラスの変更

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

  • MenuManager
  • MapManager
  • SaveDataManager

 

MenuManagerの変更

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

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

 

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

 

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

 

スクリプトの末尾にOnSaveCanceled()のメソッドを追加します。画面に応じてキャンセルボタンで戻ってきた時の違いを実装できるようにしてありますが、区別しなくても良かったかも……? と思い始めました。

 

MapManagerの変更

次にマップ名取得の処理を追加するため、「MapManager」を変更していきます。

スクリプトの末尾にGetMapNameFromId()のメソッドを追加します。このメソッド内では、インスタンス化したマップからではなく、マップのPrefabからGetComponentしてマップ名を取得しています。これは、タイミングによってはまだインスタンス化されていないマップのIDが指定される可能性もあるため、リソースとしてゲーム開始時にロード済みのPrefabを使っています。

 

SaveDataManagerの変更

「SaveDataManager」では個別のセーブ枠のデータを返すメソッドを追加します。このメソッドはウィンドウの制御クラスの「MenuSaveWindowController」にてSetUpSlotInfo()のメソッドから呼び出すようにしています。

以下のGetSaveSlot()のメソッドはスクリプトの末尾に追加しました。

 

スクリプトのアタッチ

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

 

セーブ枠の制御クラスのアタッチ

Hierarchyウィンドウから「SaveFile1Background」のゲームオブジェクトを選択し、Inspectorウィンドウから「MenuSaveSlotController」のスクリプトファイルをアタッチします。カーソルやファイル名などのゲームオブジェクトもアサインしておきます。

セーブ枠の制御クラス
セーブ枠の制御クラス

 

「Overrides」のプルダウンから [Apply All] のボタンをクリックして上記の作業をPrefabに反映します。これにより、「SaveFile2Background」や「SaveFile3Background」にもスクリプトのアタッチが反映されます。

Prefabへの反映
Prefabへの反映

 

セーブ画面のUIの制御クラスのアタッチ

Hierarchyウィンドウから「SaveMenuParent」のゲームオブジェクトを選択し、Inspectorウィンドウから「MenuSaveUIController」のスクリプトファイルをアタッチします。説明文テキスト、セーブ枠の制御クラスもアサインしましょう。

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

 

セーブ画面のウィンドウの制御クラスのアタッチ

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

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

 

作成した「MenuSaveWindowController」のゲームオブジェクトを選択し、同名のスクリプトファイルをアタッチします。UIの制御クラスに加えて、「SaveDataManager」や「MapManager」への参照もアサインしておきます。

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

 

メニュー画面の管理クラスへのアサイン

Hierarchyウィンドウから「MenuManager」のゲームオブジェクトを選択し、Inspectorウィンドウから先ほど作成した「MenuSaveWindowController」のゲームオブジェクトをアサインします。

セーブ画面の制御クラスをアサイン
セーブ画面の制御クラスをアサイン

 

動作確認

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

  • MenuBackground
  • SaveMenuParent

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

ゲームを実行し、メニューを開いて「セーブ」を選択し、セーブ画面が表示されることを確認します。前回セーブ機能のテストをしている場合は、その時の情報が表示されることを確認します。といっても画面上で違いが分かる部分はレベルの値になるので、数字の違いを確認すると良いかと思います。レベルの値を変更する場合は、ゲームの実行前に「CharacterStatusSetter」のゲームオブジェクトにて、「Player Level」の値を変更します。

カーソルの移動、決定ボタンでのセーブ機能の動作についても確認しましょう。

セーブ機能の確認
セーブ機能の確認

 

前回作成したセーブの機能をメニュー画面から呼び出せるようになったので、ゲームの進行状況について保存できるようになりました。こうなるとゲームっぽさも増してくる気がします。

 

今回のブランチ

 

まとめ

今回はメニュー画面のセーブの機能について、セーブ機能の呼び出し動作を作成しました。セーブとロードに関しては前回作成済みなので、画面の制御と呼び出し部分の対応がメインで重い実装がなくて良かったです(安堵)

次回はメニュー画面のゲーム終了の機能について作成していきます。実装の作業も少ないので、UIと機能を一括でやってしまいましょう。

     

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

CTA-IMAGE

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


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


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