【Unity】RPGを作るチュートリアルその64 マップを管理するクラスを作成

【Unity】RPGを作るチュートリアルその64 マップを管理するクラスを作成

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

第63回ではマップの管理の仕組みを考えつつ、マップを制御するクラスから作成しました。

今回はマップの管理クラスを作成して、エンカウントの動作を確認していきましょう。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回はマップの管理の仕組みを考えつつ、マップを制御するクラスから作成しました。

 

マップを管理するクラス

前回は個別のマップを制御するクラスを作成しました。今回はそれらの制御クラスを管理して、シーン内でのマップ表示を管理するクラスを作成していきます。

このクラスでは、AddressablesからのPrefabのロードも行なっていきます。Prefab名の仕様として機械的にロードできるように、名前を 「Map_0000_TestMap」 のようにしています。「Map_」の部分が接頭辞で、「0000」の部分はマップIDを4桁で表したもの、その後ろは人が分かるようにマップの説明にしています。ロードしたPrefabを特定する際に、IDを4桁表記に直して文字列の先頭一致を使う予定です。

前回作成したテスト用マップを使って、エンカウントを発生させるところまで進めたいと思います。

 

マップを管理するクラスの作成

シーン内のマップを管理するクラスを作成します。必要な機能としては、

  • ゲーム開始時に読み込むマップIDを保持するフィールド
  • マップの親オブジェクトを保持するフィールド
  • エンカウントを管理するクラスへの参照を保持するフィールド
  • 読み込んだマップの制御クラスやPrefabを保持するリスト
  • マップ用Prefabをロードするメソッド
  • ゲーム開始時のマップを表示するメソッド
  • マップIDからPrefabを返すメソッド
  • 指定したマップIDのマップを表示するメソッド
  • 全てのマップを非表示にするメソッド
  • マップIDからマップの制御クラスを返すメソッド
  • マップ用ゲームオブジェクトをインスタンス化するメソッド
  • その他デバッグ用の機能

です。

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

スクリプトファイルの作成
スクリプトファイルの作成

 

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

 

「_gameStartMapId」のフィールドはゲーム開始時に読み込むマップのIDです。ShowStartMap()ではこのIDを使って、マップ表示用のメソッドを呼び出しています。Start()のタイミングでLoadMapPrefab()のメソッドを呼び出して、ロード処理が終わった後にShowStartMap()を呼ぶようにすることで、ロードしていないPrefabを探そうとするのを防いでいます。ゲーム全体の起動シーケンスを用意するまではこの実装で進めていきます。

「_mapParent」はマップ用のゲームオブジェクトを配置するTransformへの参照で、シーン内の「Grid」のゲームオブジェクトが対象です。「_encounterManager」はエンカウント管理のクラスへの参照で、エンカウント定義をセットする目的でマップ制御用のクラスに参照を渡しています。

「_debugTargetMapId」はデバッグ用の設定で、Inspectorウィンドウ上で任意のマップを表示するためにIDを設定できるようにしています。「_debugLoadTargetMap」のフラグをTrueにすることでそのマップを表示するようにしています。「BattleTester」で戦闘機能を呼び出す仕組みと同じような実装ですね。

「_currentMapController」は現在表示中のマップの制御クラス、「_mapControllers」はインスタンス化したマップの制御クラスのリストです。一度インスタンス化した後は、表示されていないマップはシーン内では非表示になっているため、再度のインスタンス化をせずにこちらのリストから探して表示するようにします。

「_mapPrefabs」はAddressablesにてロード済みのPrefabです。マップ数が多くなってくると、ずっとPrefabを保持しているのではなく、画面切り替えのタイミングでロードする方が良いかもしれません。今回はマップ数が少ないのでリストとして持っておきます。

Update()からはデバッグ用機能のShowDebugMap()を呼んでいます。上で少し触れたデバッグ用の機能で、Inspectorウィンドウのデバッグ用フラグを監視しています。

GetMapPrefabById()は指定したマップIDのPrefabを取得するメソッドで、「Map_0000_TestMap」といった命名規則から、対象のマップIDのPrefabを特定しています。ToString(“D4”)のように、引数でフォーマットを指定することができて、この場合は4桁で0埋めした文字列に変換しています。

ShowMap()は指定したIDのマップを表示するメソッドで、まずGetTargetMap()のメソッドを使ってマップ制御クラスのリストから対応するマップIDのものを取得します。ここで見つからない場合はまだインスタンス化されていないと判断し、InstantiateMap()のメソッドでインスタンス化します。インスタンス化後はマップ制御クラスのリストに追加するようにします。対応するIDの制御クラスを使って、マップの表示と、エンカウント定義のセットを行なっています。

 

キャラクターのステータスをセットアップするクラスを作成

戦闘機能の確認時には、「BattleTester」のクラスでキャラクターのステータス情報をセットアップしていました。エンカウントの機能を確認するにあたっては「BattleTester」の処理を通らないので、別でキャラクターのステータス情報をセットアップするようにしたいと思います。

他の確認時にも共通して使えるように、シーン内のゲームオブジェクトとして個別に作成し、セットアップを行います。ゲーム全体の起動シーケンスができるまでは、こちらを使っていきましょう。

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

スクリプトファイルの作成
スクリプトファイルの作成

 

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

フィールドの「_partyCharacters」では、ゲーム実行時にパーティにいるメンバーを定義できるようにしています。といってもチュートリアルでは1人なのでデフォルト値で「1」を入れています。このリストについてはInspectorウィンドウから値を設定できるようにしているため、内容を拡張していく際にもとっかかりになるようにしています。

その他の項目は「BattleTester」とほとんど同じですが、レベルは1で固定でセットアップしています。セーブとロードの機能を導入したら、また改修していきましょう。

セットアップの処理はゲーム開始から5フレーム目に行うようにしています。これは魔法やアイテムのデータなどのロード処理が終わっていることを見越して少し待つようにしています。開始から5フレーム以内にエンカウント処理が走るとデータがなくてエラーが出てきますが、テスト時にはそこまで厳密な制御はせずに進めます。そのため、ゲーム実行ボタンをクリックしたら、5フレーム(60FPSで83msくらい)待ってから操作するようにしましょう(格ゲーかな?)

 

スプライト表示のクラスを修正

先行してエンカウントの動作確認を行なっていたところ、恥ずかしながら既存のクラスに修正ポイントが見つかりました。そのクラスはスプライト表示の「BattleSpriteController」のクラスで、敵キャラクターの画像の位置を設定する処理が間違っていて、このまま実行すると以下の画像のようにカメラに追従しないようになっていました。

スライムは見た
スライムは見た

 

そのため、「BattleSpriteController」のクラスで以下のように修正を行いました。

SetSpritePosition()のメソッドにおいて、var enemyPosOffset」の次の行でも「_backgroundRenderer」の位置を設定していたので、「_enenyRenderer.transform.position」に代入するように修正しました。

 

スクリプトのアタッチ

スクリプトを保存したら、ゲームオブジェクトの作成してスクリプトをアタッチしましょう。

 

MapManagerのアタッチ

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

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

 

作成した「MapManager」を選択し、Inspectorウィンドウから「MapManager」のスクリプトをアタッチします。「Game Start Map Id」はテスト用マップの [0] に設定し、「Map Parent」は [Grid] のゲームオブジェクトを、「Encounter Manager」は [EncounterManager] のゲームオブジェクトをそれぞれアサインします。

スクリプトのアタッチ
スクリプトのアタッチ

 

CharacterStatusSetterのアタッチ

Hierarchyウィンドウから同じく「Managers」のゲームオブジェクトの下に子オブジェクトを作成します。名前は [CharacterStatusSetter] にしました。

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

 

作成した「CharacterStatusSetter」を選択し、Inspectorウィンドウから「CharacterStatusSetter」のスクリプトをアタッチします。

スクリプトのアタッチ
スクリプトのアタッチ

 

動作確認

スクリプトをアタッチしたら、ゲームを開始して動作を確認します。今回はエンカウントから戦闘を開始するため、「BattleTester」の「Execute Battle」のチェックボックスがTrueになっている場合はFalseにしてから確認します。

また、マップ表示の機能も確認したいので、「Grid」のゲームオブジェクトの下にあるマップは全て非表示にしてからゲームを実行します。

シーン内のマップを非表示に
シーン内のマップを非表示に

 

ゲームを実行すると、まず最初のマップが表示されます。画面上にマップが表示されることと、コンソールにデバッグ用のメッセージが表示されることを確認します。

マップIDとマップ名の確認
マップIDとマップ名の確認

 

マップ内を歩き回って、スライムとエンカウントすることを確認します。このテストマップではエンカウント定義としてフィールド用のものをアサインしています。フィールド用の定義ではスライムのみ出現するので、スライムだけが出てくればOKです。戦闘に勝利して、再び歩けることが確認できればOKです。

もんすたあ さぷらいずど ゆう
もんすたあ さぷらいずど ゆう

 

今回のブランチ

 

まとめ

今回はマップの管理クラスを作成して、エンカウントの動作を確認しました。これでマップ上を歩いてエンカウントして、戦闘の動作を確認するところまで進みました。

次回はゲームで使うマップを作成していきます。マップ用画像の作成作業もあるので、ちょっと先になるかもしれません。

 

     

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

CTA-IMAGE

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


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


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