【Unity】RPGを作るチュートリアルその41 ステータス表示のUIを制御するクラス

【Unity】RPGを作るチュートリアルその41 ステータス表示のUIを制御するクラス

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

第40回では戦闘開始時に背景を表示したり、敵キャラクターを表示するために、スプライトを制御するクラスを作成しました。

今回はUI単位のウィンドウ制御と、UI自体の制御を行うクラスを作っていきます。まずはステータス表示の部分からやっていきましょう。また、UIの制御に関してはインタフェースも作っておきたいと思います。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回は戦闘開始時に背景を表示したり、敵キャラクターを表示するために、スプライトを制御するクラスを作成しました。

 

UIを制御するクラス

戦闘画面のUIとして、5つのグループを作成しました。

  • ステータス表示のUI
  • 敵キャラクターの名前表示のUI
  • コマンドのUI
  • 選択ウィンドウのUI
  • メッセージ表示のUI

これらのUIに関しては、ひとつのまとまりをウィンドウとみなして、そのウィンドウ自体を制御するクラスと、ウィンドウ内のUIを制御するクラスの2つをセットにして実装していきます。

「BattleManager」や他のクラスから各ウィンドウに対して何か処理を依頼する場合はウィンドウ制御のクラスを通します。ウィンドウ制御のクラスはそれを受けて、UIを制御するクラスに処理を依頼します。UI制御のクラスはほとんど値をセットするだけ、必要な計算はウィンドウ制御のクラス、といったように分けて実装していきたいと思います。

また、ウィンドウを制御するクラスについては、それぞれまとめて管理するクラスも作っておきます。UIを制御するクラスに関しては、ウィンドウを制御するクラスを通して操作することから、まとめて管理するクラスは必要なさそうです。

表示する、非表示にする、といった共通の動作に関しては、インタフェースを作って実装するようにします。

 

今回作りたいもの

上記を踏まえて、今回は以下のクラスを作っていきましょう。

  • ウィンドウを制御するクラスのインタフェース
  • UIを制御するクラスのインタフェース
  • ステータス表示のUIを制御するクラス
  • ステータス表示のウィンドウを制御するクラス
  • ウィンドウを制御するクラスをまとめて管理するクラス

盛りだくさんですね。処理内容としてはそんなに多くないはず……! と信じてそれぞれ作っていきましょう。

 

ウィンドウを制御するクラスのインタフェースの作成

ウィンドウを制御するクラスで共通して必要そうな機能としては、ウィンドウ内の要素の表示/非表示の切り替えです。あとは「BattleManager」から初期化の呼び出しを行う際にも共通して呼び出せると良さそうです。一覧にすると、

  • 初期化処理のメソッド
  • ウィンドウの表示メソッド
  • ウィンドウの非表示メソッド

を入れていく感じになります。

インタフェース用のクラスを作るにあたって、まずはUIやウィンドウを配置するためのフォルダから作成していきます。Projectウィンドウから「Assets/Scripts/Battle」のフォルダに移動し、新しくフォルダを作成します。名前は [UI] にしました。

フォルダの作成
フォルダの作成

 

作成した「UI」のフォルダに移動し、空のスクリプトファイルを作成します。名前は [IBattleWindowController] にしました。

インタフェース用のスクリプト
インタフェース用のスクリプト

 

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

「class」での宣言ではなく「interface」での宣言を行います。このインタフェースを実装するクラスでは、記載の3つのメソッドを実装するようにしましょう。インタフェースを見れば、そのメソッドが存在することが分かるので、ウィンドウを制御するクラスにこのインタフェースを実装すると、内部ではこのメソッドがあるな、とコンパイラが理解できます。そうすると、例えばリストの型でインタフェースを指定すると、foreachなどでループさせた際に「ShowWindow()」を呼ぶ、なんてことができます。これはウィンドウの管理クラスで実装する予定なので、そのときに実例とともに説明しようと思います。

各クラス内での「ShowWindow()」の動作は、それぞれのクラスで必要な動作を記載しますが、クラス外から見たときにそのメソッドがあることが分かる、という点が大切です。

 

UIを制御するクラスのインタフェース

UIを制御するクラスで実装するインタフェースについても作成していきましょう。こちらのインタフェースで実装したいのは、

  • 表示用メソッド
  • 非表示用メソッド

の2つです。

同じく「UI」のフォルダにて、空のスクリプトファイルを作成します。名前は [IBattleUIController] にしました。

こちらもインタフェース
こちらもインタフェース

 

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

こちらはUIを制御するクラスに実装して、表示用のメソッドと非表示用のメソッドがあることが他のクラスから分かる状態にしておきます。こちらも同様に、内部での具体的な処理内容は、このインタフェースを実装したクラス内で記載していきます。

 

ステータス表示のUIを制御するクラス

作成したインタフェースを実装して、ステータス表示のUIを制御するクラスを作成していきます。このクラスでは、ステータス表示に関連するテキストへの参照フィールドと、各テキストに値をセットするためのメソッドを作っていきます。

同じく「UI」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [StatusUIController] にしました。

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

 

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

ステータス表示のウィンドウで作成したTextMesh Proへの参照フィールドを用意して、そこに各値をセットしていくイメージです。

冒頭の宣言部では、MonoBehaviourのクラスを継承した後に、カンマ区切りで「IBattleUIController」のインタフェースを実装しています。インタフェースに含まれるメソッドとしてShow()とHide()の処理もこのクラス内に作成してあります。このクラスはステータス表示のウィンドウの親オブジェクトにアタッチする想定で、ShowではSetActive()をtrueに、HideではSetActive()をfalseにしています。

 

ステータス表示のウィンドウを制御するクラス

続いてステータス表示のウィンドウを制御するクラスも作成していきます。ウィンドウの制御クラス側では、現在のステータスを確認して必要な値を取得して、それをUI制御のクラスに渡していく流れになります。

同じく「UI」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [StatusWindowController] にしました。

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

 

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

ウィンドウの制御クラスでは、UIを制御するクラスへの参照を持たせるようにします。

ウィンドウ用のインタフェースを実装しているので、SetUpController()、ShowWindow()、HideWindow()のメソッドをそれぞれ実装しています。このうち、このクラスではSetUpController()の中で特にやることはないので、メソッドだけ用意する形になっています。

外部からはUpdateAllCharacterStatus()のメソッドを呼んで、パーティ内のキャラクターの現在のステータス情報をセットするようにします。その中で呼ばれるSetCharacterStatus()のメソッドで、UI制御のクラスに値を渡して画面上の数値をセットしていく流れになっています。

 

ウィンドウを制御するクラスをまとめて管理するクラス

外部から任意のウィンドウの制御クラスを取得したい場合に備えて、ウィンドウを制御するクラスをまとめて管理するクラスを用意したいと思います。このまとめクラスは「BattleManager」から参照させるようにします。必要に応じてそこから取得するようにしていきます。

先ほど作成したインタフェースのファイルと同じく「UI」のフォルダにて、MonoBehaviourのスクリプトファイルを作成します。名前は [BattleWindowManager] にしました。

ウィンドウ管理のクラス
ウィンドウ管理のクラス

 

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

現時点ではステータスウィンドウの制御クラスのみ作ってあるので、だいぶスッキリした形になっています。後々追加していきましょう。

ウィンドウを制御するクラスに関しては、「IBattleWindowController」のインタフェースを使ってリストとして保持します。インタフェースを実装していれば、クラス内にインタフェースで定義したメソッドがあることが分かるので、例えばSetUpWindowControllers()のメソッド内では、インタフェース側で記載したSetUpController()を使って処理を行うことができます。

HideAllWindow()でも同様に、インタフェースのメソッドを使って処理を呼ぶ形にしています。

 

既存のクラスの修正

新しく作ったクラスと繋ぎ合わせるため、既存のクラスも修正していきます。対象は、

  • BattleManager
  • BattleStarter

です。

 

BattleManagerの修正

「BattleManager」では今回作成した「BattleWindowManager」への参照用フィールドと、それを返すメソッド、また、初期化時に各ウィンドウをセットアップするメソッドを呼ぶようにします。

追加したのは

  • 16-20行目の「BattleWindowManager」への参照用フィールド
  • 75行目のStartBattle()内のセットアップ用メソッド
  • 79-85行目の参照を返すメソッド

です。Inspectorウィンドウでは「BattleManager」で参照をアサインするようにして、他のクラスからは「BattleManager」で参照を取得するようにしたいと思います。また、戦闘の開始処理の中で各ウィンドウのコントローラもセットアップするようにしましょう。

 

BattleStarterの修正

「BattleStarter」では今回作成したウィンドウ制御の管理クラスを使って、UIを全部非表示にしたり、ステータス表示のUIのセットアップする処理を入れていきます。メソッド自体は以前作成してあるので、その中身を記載していく形になります。

HideAllUI()の中身と、ShowStatus()の中身を記載しました。ShowSprites()に関しては以前記載してあるままです。

HideAllUI()では、「BattleWindowManager」のHideAllWindow()を呼んでいます。現時点で作成済みなのはステータス表示のウィンドウの制御クラスだけなので、非表示の対象はこのクラスだけになります。戦闘開始前は戦闘関連のUIは全て非表示になっている想定のため、後ほど各ウィンドウは非表示の状態に切り替えましょう。

ShowStatus()ではキャラクターのステータスを取得して、「BattleWindowManager」経由で「StatusWindowController」の中にあるSetCharacterStatus()のメソッドを呼んでいます。また、必要な値のセットが終わったらウィンドウを表示するようにしています。

 

スクリプトのアタッチ

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

 

ステータス画面のUI

まずはステータス画面のUIを制御するスクリプトからです。Hierarchyウィンドウから「StatusParent」のゲームオブジェクトを選択します。

StatusParentのゲームオブジェクト
StatusParentのゲームオブジェクト

 

Inspectorウィンドウから、作成した [StatusUIController] のスクリプトをアタッチして、フィールドに参照をアサインします。ゲームオブジェクト名とだいたい同じ名前でフィールドを用意しているので、同じ名前を選択するようにしましょう。

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

 

ウィンドウの管理クラスとステータス表示ウィンドウの制御クラス

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

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

 

先に「StatusWindowController」の方からスクリプトをアタッチしていきます。ゲームオブジェクトを選択した状態でInspectorウィンドウから [StatusWindowController] のスクリプトをアタッチします。「Ui Controller」のフィールドには、先ほどアタッチした「StatusParent」のものをアサインしましょう。

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

 

続いてHierarchyウィンドウから「BattleWindowManager」のゲームオブジェクトを選択します。Inspectorウィンドウから [BattleWindowManager] のスクリプトをアタッチしましょう。「Status Window Controller」のフィールドには「StatusWindowController」のものをアサインします。

管理用スクリプトのアタッチ
管理用スクリプトのアタッチ

 

戦闘全体を管理するクラス

戦闘全体を管理するクラスでも参照用フィールドを追加したので、こちらもアサインしておきます。Hierarchyウィンドウから「BattleManager」のゲームオブジェクトを選択し、Inspectorウィンドウから「Battle Window Manager」のフィールドに先ほど作成した「BattleWindowManager」をアサインします。

ウィンドウ管理クラスへの参照
ウィンドウ管理クラスへの参照

 

動作確認

ここまでの設定を終えたら動作確認をしてみます。その準備として、戦闘関連のUIの親オブジェクトを非表示にしておきましょう。スクリプト内でも一応非表示にするようにしていますが、フィールド移動中に表示されない状態を想定しておきます。

ゲームオブジェクトの非表示
ゲームオブジェクトの非表示

 

ゲームを実行して、画像のようにステータス表示のUIでキャラクターのHPやMPが表示されればOKです。もし参照系のエラーが出ている場合は、今回作成したスクリプトをアタッチしたゲームオブジェクトを順番にご確認ください。

ステータスが表示された
ステータスが表示された

 

Inspectorウィンドウから「BattleTester」にて、主人公のレベルを変更するとその値が表示されることも確認しておくとグッドです。

ここまでの確認が済んだら今回は完了です。今回は長くなってしまいましたね。お疲れ様でした。

 

今回のブランチ

 

まとめ

今回はUI単位のウィンドウ制御と、UI自体の制御を行うクラスのうち、ステータス表示の機能に関して作成しました。また、各ウィンドウを管理するためのクラスも実装しました。

次回は敵キャラクターの名前を表示するウィンドウに関連した機能を作っていきましょう。

     

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

CTA-IMAGE

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


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


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