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

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

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

第75回ではメニュー画面のステータスの機能について、ゲーム内の値を表示する機能を作成しました。

今回はメニュー画面のセーブの機能について、方針を考えつつUIを作成していきます。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回はメニュー画面のステータスの機能について、ゲーム内の値を表示する機能を作成しました。

 

メニュー画面の実装方針

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

 

メニュー画面では、

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

の項目を作成します。ゲームの進行状態をセーブする機能については、ゲーム起動時のタイトル画面でのロードとも対応していくので、それも想定しつつ進めていきたいと思います。

 

セーブの機能に関して

セーブ機能については、最初の方針としてはドラクエのように特定の位置にあるセーブポイントを想定していましたが、メニューとしてセーブ機能を追加したので、ロマサガのようにどこでもセーブできるようにしたいと思います。

セーブファイルに関してはJSONで保存するようにします。キャラクターの各ステータス、経験値、レベル、フラグの情報、セーブした場所、といった内容を保存していきます。フラグの情報についてはまだ管理用のクラス等を作っていないので、イベント機能の作成時に実装していきます。保存した内容を使ってロード時に同じ状況を再現できるかも確かめておきたいと思います。

今回は特に暗号化の機能は入れたりしませんが、リリースするゲームの場合はセーブファイルを暗号化しておくのも大切です。知識のある人ならセーブファイルを書き換えて色々と悪さできてしまうので、多少なりともめんどくさいと思わせることで防ぐ目的です。暗号化してあっても分かる人なら復号できちゃうのが辛いところですが、何もしないよりはマシかと思います。ネットワークを使って遊ぶゲームなら特に大切で、そもそもローカルにデータを保存しないなども対策になります。

JSONでの読み書きでは今回はUnityのJSONUtilityを使用します。同じJSONを使うケースでも、オープンソースの「MessagePack for C#」を使うとより速く、容量も少なくできるかと思います。

有料アセットを使えるなら「Easy Save」を使うのが多分一番簡単だと思います。「Easy Save」なら暗号化もやってくれるので暗号化処理を実装しなくても済むのが楽です。

 

セーブ機能の実装方針

実装方針を考えていくと、

  • JSONUtilityを使ってデータを保存する
  • セーブはメニュー画面から行う
  • タイトル画面でロードできるようにする
  • セーブとロードの機能を担当するクラスを作成する

といった方向性で考えたいと思います。

 

JSONUtilityを使ってデータを保存する

UnityではJSONを扱うためのクラスとしてJSONUtilityがあるので、それを使ってメモリ上のデータをJSONファイルにしたり、JSONファイルの内容をメモリ上に読み込んだりします。JSONUtilityを使う場合は「System.Serializable」の属性をつける必要があり、辞書を保存できないなどの制約はいくつかあるものの、お手軽に実装するには便利です。

今回はCharacterStatusManagerなどをそのまま保存できるようにはしていないので、データをやり取りするための保存用のクラスを用意して対応したいと思います。

 

 

 

セーブはメニュー画面から行う

上とちょっと重複する部分もありますが、ドラクエのような固定セーブポイント形式ではなく、ロマサガのようなどこでもセーブ形式でいきます。フラグの保存をミスったりすると詰みかねないので、フラグ関係のテストは頑張りましょう。

モダンなゲームであれば、メニュー画面からロードもできると便利なのですが、今回はセーブのみにしておきます。セーブできる枠数については3つ作れるようにします。現代のゲームだとセーブする枠数を少なくする必要もそんなにないような気もしますが、3つくらいなら枠の表示切り替えのための機能を作らなくて良さそうだというメタ的な理由もあります。

現代のゲームでも、オートセーブで定期的に状態を保存するゲームだとセーブ枠が少ないこともあります。例えばゼルダの伝説のティアーズオブザキングダムでは、手動セーブできる枠が1つ、オートセーブの枠が5つとなっていました。この部分はゲームの遊び方とも照らし合わせて決めておくと良いかと思います。ティアーズオブザキングダムの場合だと、オープンワールドのゲームでじっくりと世界を遊び尽くすことが重視されていることから、枠が少ないのではと思います。

RPGの場合は、特定の場面から再開できることで、ストーリーの理解を助けたり、選択肢の分岐でどうなるかを確認したりするケースもあるので、セーブ枠は増やしても良いかもしれません。データを出力する際のファイルの容量についても確認しておくと決めやすくなりそうです。特にコンシューマ向けやスマホ向けではあまり容量が大きくならないようにするのが鉄則です。

 

タイトル画面でロードできるようにする

ゲームを起動するとタイトル画面が表示されることを想定しています。タイトル画面では、初めから、続きから、ゲームの終了、から選択できるようにしておきます。続きからを選択した際にはセーブしたデータをロードしていきますが、セーブ前と同じ状況が再現できるように保存する情報を確認しておきましょう。

例えば経験値、パーティの所持金、所持アイテムの種類と所持数、現在のHPやMP、といった部分は動的に変化するので、しっかりと確認しておくのがグッド。

 

セーブとロードの機能を担当するクラスを作成する

メニュー画面とは別に、セーブとロードの機能を担当するクラスを作成します。セーブの担当クラス、ロードの担当クラスとして分けて実装していきましょう。MonoBehaviourのクラスとして、シーン内の「Managers」の下に配置予定です。

メニュー画面からはこちらの機能を呼び出す形で実装したいと思います。こうすることで、メニュー画面以外から(イベントなどから)も呼び出すことができるようになります。

 

保存対象のデータのピックアップ

今回のチュートリアルで保存対象として考えているデータは以下のとおりです。

  • CharacterStatusManagerで保持する一時データ
    • パーティ内メンバーのリスト
    • キャラクターステータスのリスト
    • パーティの所持金
    • パーティの所持アイテムのリスト
  • マップ情報
    • マップID
    • マップ上の位置(Vector2Int)
  • フラグ情報
    • フラグ名と値のリスト

ひとまずゲームを中断、再開できるための情報を保存していきます。他にも、コンフィグ画面で設定した音量を保存したり、オプション設定を保存したりすることも考えられるので、必要に応じて追加すると良いかと思います。

上記のデータをセーブ枠ごとに保持するようにします。

 

メニューのセーブ画面の作成

ここからメニューのセーブ画面のUIを作成していきます。画面内に表示する情報としては、

  • 「セーブするファイルを選択してください」の説明文
  • セーブファイルごとの背景画像
    • カーソル
    • 名前
    • レベル
    • マップ名

があれば良いかと思います。これを基としてUIを作成していきます。

Hierarchyウィンドウから「MenuScreen」の子オブジェクトとして空のゲームオブジェクトを作成します。名前は [SaveMenuParent] にしました。また、ステータス画面の親オブジェクトの名前が他のメニュー項目と合っていなかったので、「StatusParent」から [StatusMenuParent] に変更しました。

セーブ画面の親オブジェクト
セーブ画面の親オブジェクト

 

作成した「SaveMenuParent」を選択し、Inspectorウィンドウからアンカーの設定を行い、画面全体に広げるようにします。

アンカーの設定
アンカーの設定

 

セーブ画面では「セーブするファイルを選択してください」と表示するための説明文を作成したいと思います。「EquipmentInformationParent」の下にある、背景画像とテキストの組み合わせである「ItemDescriptionBackground」のゲームオブジェクトを複製して「SaveMenuParent」の子オブジェクトにします。名前は「ItemDescriptionBackground」を [SaveDescriptionBackground] に、「ItemDescriptionText」を [SaveDescriptionText] に変更しました。

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

 

Hierarchyウィンドウから「SaveDescriptionBackground」のゲームオブジェクトを選択し、位置やサイズを設定します。「Rect Transform」コンポーネントにて、「PosX」を [290] に、「Width」を [640] に、「Height」を [64] にします。

位置とサイズの設定
位置とサイズの設定

 

同様にHierarchyウィンドウから「SaveDescriptionText」のゲームオブジェクトを選択し、「Left」を [24] に、「Top」を [0] に、「Right」を [24] に、「Bottom」を [0] に変更します。テキストは中段揃えにするので、上下のオフセットは0にしておきます。

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

 

「TextMeshPro – Text(UI)」のコンポーネントでは「Alignment」でテキストが中段になるようにします。テキストの内容は「セーブするファイルを選択してください。」を入力しておきます。

テキストは中段に
テキストは中段に

 

セーブファイルの内容の表示項目を作成

続いて、セーブしたファイルについて、内容を表示する項目を作成します。セーブファイルは3つ表示し、それぞれの項目にて、

  • カーソル
  • 名前
  • レベル
  • マップ名

を表示するようにします。

背景画像を表示したいので、「SaveDescriptionBackground」のゲームオブジェクトを複製し、名前を [SaveFile1Background] に変更します。

セーブファイル用の親オブジェクト
セーブファイル用の親オブジェクト

 

「SaveFile1Background」の子オブジェクトの「SaveDescriptionText」を削除し、「EquipmentSlotWeapon」の下にある「CursorSelect」と「CategoryText」を複製して「SaveFile1Background」の子オブジェクトにします。「CategoryText」についてはファイル名を表示するゲームオブジェクトとして使うため、名前を [FileNameText] に変更しました。

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

 

Hierarchyウィンドウから「SaveFile1Background」のゲームオブジェクトを選択し、「Pos Y」を [-108] に、「Width」を [640] に、「Height」を [112] にします。

セーブファイルの親オブジェクト
セーブファイルの親オブジェクト

 

「CursorSelect」については、アンカーを左上の [left – top] に変更します。「Pos X」を [24] に、「Pos Y」を [-24] に変更しましょう。

カーソルの位置とサイズ
カーソルの位置とサイズ

 

「FileNameText」でも同様にアンカーを左上の [left – top] に変更します。「Pos X」を [52] に、「Pos Y」を [-18] に、「Width」は [200] に変更しましょう。テキスト側の設定でも左揃えの上段配置にして、カーソルと並ぶようにしたいと思います。

ファイル名のテキスト
ファイル名のテキスト

 

テキストには「ファイル1」を入力しておきます。ゲームの実行中は動的にファイルの数字を入れるようにしていきたいと思います。また、「Alignment」の項目は左揃えの上段を選択します。

テキストの設定
テキストの設定

 

キャラクター名のテキスト

次にキャラクター名のテキストを作成します。先ほど設定した「FileNameText」を複製して、名前を [CharacterNameText] に変更します。

キャラクター名のテキスト
キャラクター名のテキスト

 

「CharacterNameText」を選択し、位置とサイズを設定します。ファイル名テキストと同じ行で並ぶようにしたいので、「Pos X」を [200] に、「Width」を [256] に変更します。テキストには「アレン」を入力しておきます。こちらも動的に設定するようにしましょう。(といっても定義した名前がアレンなのでそのまま入ります)

キャラクターの名前のテキスト
キャラクターの名前のテキスト

 

レベルのテキスト

次にレベルを表示するテキストも作成します。名前のテキストと同様に「FileNameText」を複製しますが、「レベル」とラベルとして表示するテキストと、レベルの値を表示するテキストの2つを作成します。名前はそれぞれ [LevelTitleText][LevelValueText] に変更します。

レベルのラベルと値
レベルのラベルと値

 

「LevelTitleText」を選択し、位置を設定します。こちらもファイル名テキストと同じ行で並ぶようにしたいので、「Pos X」を [480] に変更します。テキストには「レベル」を入力しておきます。

レベルのテキスト
レベルのテキスト

 

「LevelValueText」を選択し、位置とサイズを設定します。「Pos X」を [536] に、「Width」を [48] に変更します。

レベルの値のテキスト
レベルの値のテキスト

 

レベルの値については数値を入れたいため、「Alignment」の項目で右揃えを選択します。

テキストの設定
テキストの設定

 

場所のテキスト

場所を表示するテキストも作成しましょう。「CharacterNameText」を複製し、名前を [PlaceText] に変更します。

場所表示のテキスト
場所表示のテキスト

 

「PlaceText」はキャラクターの名前テキストの下に表示するため、「Pos Y」を [-60] に、「Width」を [400] に変更します。

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

 

Prefabを作成

セーブファイルの内容表示の項目が作成できたので、これをPrefabにします。Projectウィンドウにて「Assets/Prefabs」のフォルダを開き、Hierarchyウィンドウから「SaveFile1Background」をドラッグ&ドロップします。作成したPrefabの名前は汎用的に使えるように [SaveFileBackground] にしました。Fileの後ろの1を取った形です。

Prefabの作成
Prefabの作成

 

Prefab内の各テキストに値をセットするために、Prefabの制御用スクリプトも後のチュートリアルで作成していきましょう。

 

セーブファイルの表示項目の複製

Hierarchyウィンドウからセーブファイルの表示項目の「SaveFile1Background」を複製して、合計3つになるようにします。名前はそれぞれ [SaveFile2Background][SaveFile3Background] にしました。Fileの後ろの数字を変えています。

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

 

複製したゲームオブジェクトで変更したいのは「Rect Transform」の「Pos Y」とテキストで、以下の表のように変更しましょう。

名前 「Rect Transform」の「Pos Y」 テキスト
SaveFile2Background -232 ファイル2
SaveFile3Background -356 ファイル3

 

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

作成したUI
作成したUI

 

ここまで作成できたら今回は完了です。

 

今回のブランチ

 

まとめ

今回はメニュー画面のセーブの機能について、方針を考えつつUIを作成しました。UIの作業はそう多くないのですが、セーブ機能についてはゲームの根幹部分にもなるので、考えることが多いですね。

次回はゲーム全体でのセーブとロードの機能を作成していきます。

 

     

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

CTA-IMAGE

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


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


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