【Unity】RPGを作るチュートリアルその120 ビルドして動作を確認

【Unity】RPGを作るチュートリアルその120 ビルドして動作を確認

シンプルなRPGをUnityで作るチュートリアルシリーズの120回目です。流石にあと10回は行かないと思うので、いよいよ終わりが見えてきたのではないでしょうか(期待)

第119回ではデバッグ作業を行いました。

今回はゲームをビルドして動作を確認していきます。ビルドした際もエラーがたくさん出るんですよね……(白目)

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

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

 

チュートリアルの一覧

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

 

前回の内容

前回はデバッグ作業を行いました。

 

まずはビルドしてみる

ある程度全体が確認できたら、ビルドして動作するか確認してみましょう。リリースする上では実際の環境で動作することが大切なので、エディタ上の動きだけではなくビルドしてみるのも重要です。

ビルドするとなると、エディタ内で参照が確認できていたものでも、実機では参照が切れていたりしてNullReferenceExceptionがたくさん出てきたりします。特にAddressablesを使っている場合はよく出くわすので、エディタでも実機でも動作するように修正を行なっていきましょう。

何はともあれ、まずはビルドしてみましょう。エラーが出ると分かっていてこのまま話を進めるのもアレですが、チュートリアルなのでこうした対応についても入れておきたいと思います。

ビルドに含めるシーンを設定するため、画面上部のメニューバーから [File] -> [Build Profiles] を選択してBuild Profilesのウィンドウを表示します。

Build Profileを確認
Build Profileを確認

 

第108回で「Title」シーンを作成した時に設定を行なったのですが、念の為Scene Listの画面にて、「Title」シーンと「Game」シーンが存在することを確認しましょう。

シーンを追加する
シーンを追加する

 

続いて左のPlatformのメニューからお使いのマシン向けの環境を選択します。私の場合はmacでビルドを進めるので「macOS」を選択します。プラットフォームの画面では、「Development Build」にチェックが入っていることを確認し、右上の「Build」のプルダウンから [Clean Build…] を選択します。

クリーンビルドする
クリーンビルドする

 

ビルドを保存するパスを聞かれるので、任意のフォルダを選択してファイルを保存します。ビルドに成功すると、以下のようにAddressablesのビルド結果についても表示されます。

Addressables側のビルドも行われる
Addressables側のビルドも行われる

 

もしビルドに失敗する場合は、

  • コンパイルエラーが出ていないか
  • マシンの容量は足りているか

といった部分を確認すると良いかと思います。

作成したビルドを起動して動作を確かめていきます。「Development Build」にチェックを入れているので、もし何か問題があれば画面上にログが表示されるのでそれを確認していきましょう。もし画面が小さくて調整したい場合には、「Project Settings」ウィンドウの「Player」タブを開き、「Resolution and Presentation」のペインにてサイズを変更しましょう。

サイズを変更
サイズを変更

 

ゲームの起動

ゲームを起動してみると以下のように「Title」シーンが表示されました。BGMも再生されているので、ひとまず今のところは問題なさそうですが……。

ひとまずゲームは起動したので安心
ひとまずゲームは起動したので安心

 

「はじめから」を選択すると以下のようにピンクな世界になってしまいました。Unityでこのピンクは多くの場合、マテリアルやテクスチャなどのリソースが読み込めていない時に見られるものです。とても馴染み深いですね(白目)

ピンクな世界
ピンクな世界

 

実装上はそのまま下方向に進むと村の出入り口があるので、試しにそのマスまで進んでみます。するとフィールドは表示されず、画面はフェードアウトされたままになりました。お馴染みのNullReferenceExceptionも表示されているので、画面内の「Open Log File」からPlayer.logを開いて確認すると、MoveMapのイベントプロセスを実行するメッセージの後にNullReferenceExceptionが出ているので、おそらく「MapManager」でPrefabをインスタンス化する際に何か起きているようです。

フィールドに進まず真っ暗なまま
フィールドに進まず真っ暗なまま

 

対応1 リソースの解放をしないようにする

マップのPrefabなどはAddressablesを使ってロードしています。例えば「MapManager」では以下のようにPrefabをロードしています。

気付いたかもしれませんが、Prefabをロードした後に「handle.Release();」を呼んでロードしたリソースを解放しちゃっていました。これだと、最初のフレームでインスタンス化はできても村を出る頃にはPrefabがメモリからいなくなっているので、「MapManager」からの参照が切れてNullReferenceExceptionが出たものと考えられます。

対応方法としては、

  • マップのロード時に都度個別にロードする
  • リソースを解放しない

の2通りになりそうです。今回のチュートリアルではマップ数も少ないので、「リソースを解放しない」という方向で考えていきたいと思います。マップの数が増えてくる場合には、ロード時に都度個別にロードしてインスタンス化する方が良いかと思います。ロードの待ち合わせ処理など、ちょっと改修範囲が広くなりそうだったので、チュートリアル完了後に拡張していく際に変えていくと良さそうです。

「MapManager」以外にも、定義データを管理するクラスでロードする際に「handle.Release();」を呼んでいるため、以下のクラスにて「handle.Release();」の行を削除しましょう。個別に示すとすごく長くなるので、クラス名を列挙します。検索を使って対象の行を特定していただくか、一括置換すると楽ちんです。

  • CharacterDataManager(3行)
  • EnemyDataManager
  • ItemDataManager
  • MagicDataManager
  • FlagManager
  • MapDataManager
  • MapManager

 

対応1の動作確認

上記の修正を行なったらスクリプトを保存して再度ゲームをビルドします。ゲームを実行して、「はじめから」を選択して村に進みましょう。上記の修正で以下のようにTilemapやキャラクターが表示されるようになりました。ピンクから解放されて世界が色づき始めました。

ピンクからの解放
ピンクからの解放

 

村から出てフィールドが表示されることも確認できました。Addressablesでロードしたリソースが解放されてしまっていた部分を修正したのは有効だったようです。念の為戦闘も行い、敵キャラクターや魔法などのリソースも読み込めていることを確認しました。

戦闘まで進めた
戦闘まで進めた

 

侵入できないはずのタイルに入れてしまう問題

Tilemapが表示されるようになったことで、新たな問題に気付くことができました。壁に向かって歩き続けると、本来であれば侵入できないはずですが、そのマスに入れてしまいました。次はこちらを修正していきましょう。念の為キャラクター同士でぶつかってみると、こちらは問題なく同じマスに入れないようになっていたので、タイル側の問題と考えられます。

壁にめり込む主人公
壁にめり込む主人公

 

対応2 タイルの比較方法を変更する

侵入できるタイルの比較は「TilemapManager」のCanEntryTile()のメソッドで行なっています。移動先のタイルを取得して、定義データとして保持している「NoEntryTileData」のタイルと比較しています。

このメソッド内ではタイルのオブジェクト同士を比較していたのですが、実はエディタ上で実行した時と、実機で実行した時とでインスタンス化されているタイルのインスタンスIDが変わっていたりします。特に今回はAddressablesを使ってグループ単位で別のアセットバンドルになっていて、マップ用Prefabのグループ内にタイルが含まれています。そのため、インスタンス化した時にインスタンスIDが変わって別のタイルという扱いになってしまい、侵入禁止の対象ではないと判断されたと考えられます。

そこで、比較はオブジェクト同士を直接比較するのではなく、タイル名の文字列を使って比較したいと思います。これによって「タイル名は一意である必要がある」という制約が生まれますが、今回はこの仕様を許容して実装していきます。

 

タイルの比較方法の修正

「TilemapManager」にてタイルの比較を行う処理を修正します。

タイルに侵入できるかどうかの判定を行うCanEntryTile()のメソッドに加えて、ひとつ先まで確認する対象のタイルか判定するIsThroughTile()のメソッドについてもタイル名で比較を行うようにしました。

目の前のマスのタイルを取得後、そのタイル名と比較して、侵入できないタイルのリスト、またはひとつ先まで確認する対象のタイルのリストを確認して、一致する名前のタイルがあるか確認します。

また、細かい部分ですがデバッグログを出力する処理についてはUnityのDebugクラスを呼んでいたので、「SimpleLogger」を使う形に変更しました。

 

対応2の動作確認

上記の修正を行なったらスクリプトを保存して再度ゲームをビルドします。ゲームを実行して、「はじめから」を選択して村に進みましょう。上記の修正で以下のように侵入できないタイルには入れず、カウンター越しにイベントを実行できるようになりました。

侵入できないタイルに入れなくなった
侵入できないタイルに入れなくなった

 

エディタ上で実行した時にも問題なく動作することを確かめておくとよりグッドです。

 

セーブファイルのレベル表示が意図しない形になる

ある程度ビルドでも全体で遊べるようになったのでプレイしていたところ、以下のような事象が発生しました。

  1. セーブを行う
  2. レベルアップする
  3. 最後にセーブしたスロットのレベル表示がセーブした時点ではなく今のレベルに

セーブした時点のレベルが表示されていて欲しいのですが、なぜか現在のレベルがそのまま表示されているため、修正を行いたいと思います。

 

対応3 セーブ前にファイルからデータをロードする

セーブするデータを収集する過程で、「CharacterStatus」への参照を保持することで、ゲーム中の主人公のステータスとセーブ枠のステータスが共通して表示されるようになっていました。そのため、メニューのセーブ画面を表示するタイミングでファイルからロードし直してセーブ画面に表示するようにします。これにより、ファイルとの一貫性を保つようにします。

「MenuSaveWindowController」にて、以下のように修正を行います。

ShowWindow()のメソッドにて、SetUpSlotInfo()でセーブ画面内に情報をセットする前に、SaveDataHolder.Load()を呼び出してファイルからデータをロードする形にしました。

 

対応3の動作確認

上記の修正を行なったらスクリプトを保存してゲームを実行します。今回の事象はビルド特有ではなくエディタでも同様に発生するものなので、まずはエディタ上で確認しておきましょう。

レベルアップ直前まで経験値を稼いだら一度セーブします。セーブ後、経験値を稼いでレベルアップさせ、再びメニューのセーブ画面を表示します。先ほどセーブしたセーブ枠のレベル表示が、セーブした時点のものになっていることを確認しましょう。

セーブ時のレベル表示を確認
セーブ時のレベル表示を確認

 

ビルドして気付いた範囲のものは修正できたので、次回は各種調整作業を行います。

 

今回のブランチ

 

まとめ

今回はゲームをビルドして動作を確認しました。案の定たくさんエラーが出てきたので、ビルドは早い段階でやっておくと安心です。といってもこのチュートリアルでは説明の都合上後回しになってしまいました。

次回はパラメータや初期装備、エンカウント率などを調整していきましょう。

 

     

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

CTA-IMAGE

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


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


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