読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】ゲームのビルド直前に "ファイルの退避" や "シーンの最適化" 等の処理を挟む

今回はUnityでゲームをビルドする前にプロジェクトやシーンに何らかの処理を行い、、ビルド完了後に戻す方法についての紹介です。

Unity 5.6以降で動作します。

f:id:tsubaki_t1:20170328003531g:plain

作る際に便利なものとパフォーマンスに良いもの

ゲームを作る上では、多くのパラメータが露出し細かく分割出来ている方が良いです。しかし、作りやすい構造がパフォーマンスに良いとは限りません。

 

例えば、ゲームオブジェクトをシーン内にばら撒くのは良くないのでGameObjectをフォルダのように使い構造化する手法はよくやることですが、これはパフォーマンス的に良くありません。

f:id:tsubaki_t1:20170327225059j:plain

またUIのEvent Systemのような物は各シーンについていると検証が何かと容易ですが、ビルド後にWarningを発行したりしますし、SingletonMonobehaviourを使用している場合も「生成・破棄」のコストがかかります。*1

 

同様に、ResourcesやStreamingAssets等、入れとくと何かと便利だがビルド時には必要ない…といった物も存在します。

例えばMovieTextureはVideo Playerと異なりiOSAndroid といったモバイル環境では動作しない為、Handheld.PlayFullScreenMovieを使用し、ハードウェアのムービー再生機能を直接叩く必要がありました。その為、基本的にStreaming Assetsフォルダに直接格納する必要がありました。

しかし逆にStandaloneやWebPlayer(故)といったプラットフォームにはフルスクリーンで動画を再生する機能が無いのか、Handheld.PlayFullScreenMovieは使えませんでした。

f:id:tsubaki_t1:20170327230522j:plain

で、5.6から

PostProcessBuildAttributeというAttributeとは別に、

  • IPreprocessBuild
  • IProcessScene
  • IPostprocessBuild

というInterface が追加されました。

ビルド前とビルド後に処理を行う(全体)

ゲームのビルド前後に処理を行う場合、IPreprocessBuildIPostprocessBuildを利用します。

このインターフェースが定義しているAPIを実装すると、ゲームをビルドする際、ビルド前とビルド後に処理が実行されます。

 

例えば下のコードでは、StreamingAssetsに格納されている01.pngというファイルをビルド直前にStreamingAssetsから取り除き、ビルド完了後に元の場所に戻しています。

 

gist.github.com

f:id:tsubaki_t1:20170327232006j:plain

この処理ではファイルを直接操作しているため、ビルド完了後に巻き戻さないと元に戻りません。これはPrefabやScriptableObjectを操作した場合も同様です。

 

例えば下の画像では、PrefabのTextをResorucesに格納し、ビルド時に内容をTextからHogehogeに更新しています。これで変更された項目はUndoで戻せないので、OnPreprocessBuildで変更した物はOnPostprocessBuildで巻き戻す事にするのが良さそうです。

f:id:tsubaki_t1:20170327233323j:plain

ビルド前にシーンの変更

シーンの場合は、IProcessSceneでシーンを構築する前に各シーンの内容を更新できます。対象は、ビルドに含まれるオブジェクト群です。

この処理では、この場でシーンに対して加えた変更もビルド終了後に変更前の状態に戻るみたいです。なお、呼ばれるのは少なくともライトが焼かれた後みたいです。

 

下の例ではビルド時に以下の処理を行ってみました。

  • bakedなライトを全て排除
  • 親子関係で整理されているオブジェクトをシーンにばら撒き
  • コンポーネントの無いルートオブジェクトを削除

gist.github.com

この結果、GameObject数は減り、子を動かした際にかかっていた負荷が減る(規模によってはシーンロードの高速化も)訳ですが、見た目にはほぼ違いはありません。また、ビルド後に元の状態に戻っています。

場合によってはコンポーネントの削減やメッシュの結合等も有りかもしれませんし、missingを探す処理を入れても良いかもしれません。他にも色々とアイディアがありそうです。

f:id:tsubaki_t1:20170328002307j:plain

この手法の問題点は、シーン構築の際の中間状態が見えないという点です。

上のビルド後は「予想」となっている通り、シーン構築時に行う処理を取り出して現在のシーンに投げてみた結果です。ビルド後の結果は「正しく動いたかどうか」でしか判別出来ないので、注意しながら処理を実装する必要がありそうです。

処理の実行順

処理の実行順は当然下の順です。

  1. IPreprocessBuild
  2. IProcessScene
  3. IPostprocessBuild

ただし、複数合った場合、callbackOrderで呼ぶ順を制御します。小さいものから順に呼ばれます。

f:id:tsubaki_t1:20170327223847j:plain

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

qiita.com

*1:意外と知られてない事ですが、パラメータの多い項目は生成・破棄のコストが割高です