【Unity】RPGを作るチュートリアルその110 ゲームの開始と終了の機能を実装

【Unity】RPGを作るチュートリアルその110 ゲームの開始と終了の機能を実装

シンプルなRPGをUnityで作るチュートリアルシリーズの110回目です。私本人としては終わりが見えてはいるのですが、まだもうちょっとだけ続きそうです。できればキリ良く128回で締めたいと思います(願望)

第109回ではタイトル画面の動作のうち、タイトルメニューの部分を実装しました。

今回はタイトル画面の動作のうち、「はじめから」と「ゲームの終了」の動作を実装したいと思います。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回はタイトル画面の動作のうち、タイトルメニューの部分を実装しました。

 

ゲームの開始と終了の動作

前回の作業でタイトル画面のメニューを選択できるようにしました。メニュー項目としては、

  • はじめから
  • つづきから
  • ゲームの終了

の3つで、このうち「はじめから」と「ゲームの終了」について今回の作業で実装したいと思います。

ゲームの開始を担当するクラスでは、シーンの切り替えを行う処理を入れていきます。最初から始める場合も、続きから始める場合も、ゲーム開始に関する情報を持つ「GameStartInfoHolder」にセットする情報が異なるだけで処理の流れは同じなので、このクラスで処理を行いたいと思います。

また、シーン遷移の後、「Game」シーン側でセットアップする動作も入れたいので、「Game」シーンを管理するクラスも作成します。マップ用のPrefabをロード後にマップ表示処理を行いたいので、「MapManager」に対して外部からロードできるようにメソッドをpublicにするのと、ロード完了を通知するためのインタフェースも追加したいと思います。

ゲームの終了を担当するクラスでは、画面をフェードアウトさせた後にゲームを終了する処理を呼びます。こちらは処理の内容は少ないのでシンプルに実装できるかと思います。

その他、変更が必要な部分については都度対応していきましょう。

 

ゲーム終了時の処理を制御するクラス

メニューを選択した後に、それぞれのメニューに応じた制御を行うためのクラスを作成していきます。まずはゲーム終了時の処理を制御するクラスから作成します。

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

ゲーム終了の処理を行うクラス
ゲーム終了の処理を行うクラス

 

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

タイトルメニューで「ゲームの終了」が選択された場合、QuitGame()のメソッドが呼び出されるようにします。このメソッド内では画面のフェードアウトの処理を呼び、それが終わったタイミングでゲームの終了処理を行います。

メニュー画面の「ゲームの終了」と同様に、エディタではプレイモードを終了し、実機ではアプリケーションを終了させます。

 

ゲーム開始時の処理を制御するクラス

続いてゲーム開始時の処理を制御するクラスも作成します。

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

ゲーム開始の処理を行うクラス
ゲーム開始の処理を行うクラス

 

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

フェードアウトの処理完了を検知したいので「IFadeCallback」のインタフェースを実装しています。

「CharacterStatusSetter」への参照用フィールドを実装し、シーン名の定義として「GameSceneName」のフィールドも実装しています。この名前は読み込むシーンに合わせるようにします。動的にシーンをロードする場合は、「Build Profiles」のウィンドウにてシーンを登録しておきましょう。今回のチュートリアルではシーン作成時に作業しました。

StartNewGame()のメソッドは新規にゲームを開始する時に呼ばれるメソッドで、タイトル画面のメニューで「はじめから」を選択した際に呼ばれます。「GameStartInfoHolder」の「isNewGame」にtrueをセットすることで新規に開始されたことが分かるようにします。

ContinueGame()のメソッドは「つづきから」を選択して、ロードするセーブファイルを選択した後に呼ばれます。「GameStartInfoHolder」の「isNewGame」にはfalseを、「loadedSlotId」にはロードするセーブ枠IDをセットすることで、シーンが切り替わった際にロードする対象のデータを特定できるようにします。

OnFinishedFade()はフェードアウトが終わった後の処理で、キャラクターのステータスの初期化、フラグの初期化を行なってからシーンをロードしています。シーンをロードする際にロード画面などを表示する場合は非同期でシーンをロードできるLoadSceneAsync()を使うと便利なのですが、今回は画面を真っ暗にしていること、「Game」シーンのサイズもそこまで大きくないことからシンプルにLoadScene()で進めたいと思います。

なお、現時点では「CharacterStatusSetter」に関してコンパイルエラーが表示されるので、後続の変更を実装していきましょう。

 

マップのPrefabロード完了を通知するインタフェース

「GameSceneManager」を追加した際に、このクラスから「MapManager」に対してPrefabのロードの処理を呼び出して、その完了を通知してもらうようにします。これはPrefabがロードされていない状態でマップを表示することを防ぐ目的です。

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

コールバック用のインタフェース
コールバック用のインタフェース

 

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

マップ用Prefabのロードが完了した時にOnFinishedLoad()のメソッドで通知を行います。

 

Gameシーンを管理するクラス

「Game」シーンを管理するクラスも作成していきます。このクラスでは、「Game」シーン側でゲームの開始の処理を行なっていきます。「Title」シーンを通ったかどうかで初期化に必要な処理も変わってくるので、その部分を吸収しつつ動作するようにしたいと思います。

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

ゲームシーンを管理するクラス
ゲームシーンを管理するクラス

 

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

マップ用のPrefabがロードされたことを検知したいので、「IMapLoadCallback」のインタフェースを実装しています。

「はじめから」を選択してゲームを始める場合は初期マップを、「つづきから」を選択してゲームを始める場合はセーブされたマップを表示するため、フィールドとしてそれぞれ「MapManager」と「SaveDataManager」への参照を保持するようにします。

Start()のタイミングで「MapManager」に対してマップ用Prefabのロード処理を呼び出して、完了したらコールバックを受け取ります。コールバック用のメソッドの中ではReadyData()の処理を呼び出しています。なお、現時点では「MapManager」のLoadMapPrefab()のメソッドは引数なしとなっていてコンパイルエラーが出るので、後ほど「MapManager」のクラスも変更していきます。

ReadyData()では「Title」シーンを通ったかどうかに応じて処理を分けます。通っている場合はそのままStartGame()のメソッドを呼んで開始のプロセスに進み、通っていない場合はSetUpProcess()のコルーチンを使ってリソース類のロード処理を行います。この部分の処理は「TitleManager」でやっているものを同様にこちらでも行うようにして、ゲームを開始できる状態にしています。

StartGame()のメソッドでは、「はじめから」を選択した場合はNewGame()のメソッドを呼び出して初期マップを表示し、「つづきから」を選択した場合はContinueGame()のメソッドを呼び出してロードしたデータをメモリ上にセットしつつ、セーブされたマップを表示します。

 

既存のクラスの変更

今回作成したクラスに合わせて、既存のクラスも変更していきます。変更したい対象のクラスは以下のものです。

  • CharacterStatusSetter
  • TitleMenuManager
  • MapManager

 

CharacterStatusSetterの変更

「CharacterStatusSetter」では、ステータスをセットするためのメソッドを外部から呼び出せるようにします。

元々はUpdate()の中でフレームカウントが5になったタイミングでSetPlayerStatus()などを呼び出していましたが、外部から呼び出すためのSetUpCharacterStatus()を追加しました。Update()は削除しています。これにより任意のきっかけで呼び出せるので、例えば「Game」シーンから開始した場合でも、それを検知して呼び出すこともできます。この辺りの処理は「つづきから」の実装の際に入れていきましょう。

 

TitleMenuManagerの変更

「TitleMenuManager」ではタイトルメニューを選択した時の動作として、今回追加したクラスの処理を呼ぶようにします。

フィールドとして「TitleStartController」と「TitleQuitGameController」への参照を保持するようにします。

HandleMenu()のメソッドでは、「はじめから」を選択した場合は「TitleStartController」のStartNewGame()を呼び出して、「ゲームの終了」を選択した場合は「TitleQuitGameController」のQuitGame()を呼び出すようにします。

OnSelectedSlotId()は「つづきから」を選んだ時に表示されるロード画面でのコールバックで、選択されたセーブ枠IDをContinueGame()に渡すようにします。

 

MapManagerの変更

「MapManager」ではマップ用Prefabをロードするタイミングを制御するため、コールバック用のインタフェースを使うようにします。

Start()からLoadMapPrefab()のメソッドを呼び出していた部分を削除しました。LoadMapPrefab()のメソッドでは、コールバック用のインタフェースを引数として渡すようにしていて、ロード処理後にコールバックのOnFinishedLoad()を呼び出すように変更しています。

ShowStartMap()ではpublicをつけて外部から呼び出せるようにしています。

 

 

スクリプトのアタッチ

作成したスクリプトをアタッチしていきます。まずは「Title」シーンで作業を行います。

Hierarchyウィンドウから「Managers」の子オブジェクトとして以下の2つのゲームオブジェクトを作成します。Inspectorウィンドウからはスクリプトファイルもアタッチしましょう。

ゲームオブジェクト名 アタッチするスクリプトファイル
TitleStartController TitleStartController
TitleQuitGameController TitleQuitGameController

 

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

 

「TitleStartController」のゲームオブジェクトでは、「CharacterStatusSetter」への参照もアサインしておきます。

参照のアサイン
参照のアサイン

 

「TitleMenuManager」では今回作成したクラスへの参照をアサインします。

制御クラスへの参照のアサイン
制御クラスへの参照のアサイン

 

続いて「Game」シーンを開いて、Hierarchyウィンドウから「Managers」の下に空のゲームオブジェクトを作成します。名前は [GameSceneManager] にしました。作成した「GameSceneManager」のInspectorウィンドウから同名のスクリプトファイルをアタッチし、それぞれ参照をアサインします。

スクリプトのアタッチと参照のアサイン
スクリプトのアタッチと参照のアサイン

 

動作確認

スクリプトをアタッチしたら動作を確認してみましょう。

まず「Title」シーンを開き、タイトルメニューから「ゲームの終了」を選択します。この時、画面がフェードアウトしてPlayモードが終了することを確認します。

次に再度ゲームを開始して、タイトルメニューから「はじめから」を選択します。シーンが遷移して、オープニングのイベントが開始することを確認します。

また、「Game」シーン単体でも、ゲームを開始するとオープニングイベントが開始され、定義データが読み込めていることを確認します。

はじめからゲームを開始
はじめからゲームを開始

 

今回のブランチ

今回のブランチでは、各マップ用のPrefabにおいて、「TilemapRenderer」コンポーネントの「Mode」のフィールドの値を「Chunk」から「SRP Batch」に変更しています。今回のチュートリアル作成前に別のブランチをいじっていたら、mainのブランチでタイルマップが表示されなくなったため、設定を変更しました。

このチュートリアルではURPを使っていて、互換性のある設定は「SRP Batch」の方だったので、ずっと「Chunk」になっていたことでもしかしたら途中のブランチから開始したらタイルが表示されない事象などもあったかもしれません(すみません)

過去のブランチに対する修正の反映はチュートリアル完了時に一貫性のある形で行いたいと思います。(今直すとぐっちゃぐちゃになりそうで恐怖)

(参考)

 

まとめ

今回はタイトル画面の動作のうち、「はじめから」と「ゲームの終了」の動作を実装しました。初期化用のシーンを通った場合でも、ゲーム用のシーンから直接開始した場合でも、どちらも動作するようにしておくことでデバッグ作業をしやすくなります。

次回はタイトル画面の動作のうち、「つづきから」の動作を実装していきます。

     

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

CTA-IMAGE

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


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


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