はじめに
前回はC#におけるメモリ管理の記事を出しました。
(https://qiita.com/Mizutamari/items/49ebf10aa249b9b67c62)
今回はUnityにおけるメモリ管理の話としてResourcesの話をしていきたいと思います。
特殊なフォルダについて
まず初めに特殊フォルダについて。
Unityは作成したフォルダに自由に名前を付けることが出来る。が、ある特殊な名前で設定するとUnity側に「これは特殊なフォルダだ」と判定させることができ、「フォルダ内のコンテンツは特定の方法で扱ってね」と指定することができる。
例えばEditor拡張用のスクリプトは「Editor」と名前を付けたフォルダに入れることで正しく機能する。Unityでプロジェクトを作成した時に必ず見る「Asset」フォルダも特殊なフォルダである。
Resoucesフォルダについて
前節で話したようにUnityには特殊なフォルダが存在する。その中でもResourcesフォルダについて説明する。
Resoucesフォルダについて公式ドキュメントからの引用
ゲームで使用するために、シーンのアセットのインスタンスを作成する代わりに、スクリプトから必要に応じてアセットを読み込むことができます。これを行うには、アセットを Resourcesというフォルダーに配置します。
Assets フォルダー内の任意の場所に複数の Resources フォルダーを配置できます。必要なアセットファイルを Resources フォルダーか、その中のサブフォルダーに配置します。アセットファイルがサブフォルダーにある場合は、Resources.Load 関数に渡されるパスに常にサブフォルダーパスを含有させます。
Resources フォルダーが Editor のサブフォルダーである場合、その中のアセットは Editor 用スクリプトから読み込み可能ですが、ビルドからは削除されます。
アセットをResourcesフォルダに置いておき、Resouces.Load()を呼び出すことで任意のアセットを読み込むことが出来る。これはゲーム開始前からアセットを配置しないで、任意のタイミングで任意のアセットを呼び出したいときとかに使える。
また、Resources.UnloadAssets()でアセットをアンロード、Resources.UnloadUnusedAssets()で使用していないアセットをアンロードしてくれる。
一見よさげなフォルダ、機能に見えるが実はResoucesはUnity公式が自ら「推奨していない方法」としている。Resourcesの良くない点と使ってもいい場面をUnityLearnからの引用をもとに説明していこうと思う。
Resoucesの良くない点
使用しないでください。
この強く推奨する理由はいくつかあります。
- Resources フォルダを使用すると、きめ細かいメモリ管理がより困難になります
- Resources フォルダーを不適切に使用すると、アプリケーションの起動時間が長くなり、ビルド時間が長くなります
- リソース フォルダーの数が増えると、それらのフォルダー内のアセットの管理が非常に難しくなります。
- リソース システムは、カスタム コンテンツを特定のプラットフォームに配信するプロジェクトの能力を低下させ、段階的なコンテンツ アップグレードの可能性を排除します。
- AssetBundle バリアントは、デバイスごとにコンテンツを調整するための Unity の主要なツールです
もはや使用しないでくださいと書いてある。
きめ細かいメモリ管理について、前述したようResources.UnloadAssets()や、Resources.UnloadUnusedAssets()を使うことでアセットの解放を行ってくれるのだが、*参照カウンタによる厳密なメモリ管理や依存関係を含めたアンロードはできないのでプロジェクトの要件にもよるがメモリの管理がしづらい。
-
*参照カウンタ
オブジェクト自身がオブジェクトの生存期間を管理するためのカウンタ。
オブジェクトが生成されると参照カウンタは1にセットされ、以降そのオブジェクトが参照されるたびに参照カウンタが増えていく。オブジェクトを利用する側がRelease()を呼び出すと参照カウンタは減っていく。そうして参照カウンタが0になるとオブジェクトはメモリから消去される。
また、Resourcesフォルダ内にあるアセットはビルド時に一つのファイルにまとめてシリアル化される。アプリの起動時にそれらのアセットを効率的に読み込むための処理が必ず走るためアセットの数が多くなればなるほど起動時のロード時間が長くなってしまう。(起動時に必要なアセット以外にもすべてまとめてこの処理を行うため厄介。ビルド時間も長くなる。)
さらに言うとアップデート処理やパッチ変更にも弱い。
前述したとおり、Resourcesフォルダの中身はビルド時に1つのファイルにまとめられる。そのため、「アップデートにおける変更があった部分だけアセットを差し替える、追加する」ことが出来ず、アップデートの度に(変更があれば)Resourcesフォルダ分、丸々容量が増えてしまう。
Resourcesを使ってもいい場面
- Resources フォルダーの使いやすさにより、迅速にプロトタイプを作成するための優れたシステムになります。ただし、プロジェクトが本格的な運用に移行するときは、Resources フォルダーの使用を排除する必要があります。
- Resources フォルダーは、内容が次の場合、いくつかの些細なケースで役立つ場合があります。
- 通常、プロジェクトの存続期間全体で必要
- メモリ集約的ではない
- パッチが適用されにくい、またはプラットフォームやデバイス間で変化しない
- 最小限のブートストラップに使用
この 2 番目のケースの例には、プレハブをホストするために使用される MonoBehaviour シングルトン、または Facebook アプリ ID などのサードパーティの構成データを含む ScriptableObjects が含まれます。
アプリが立ち上がっている間常に存在しなければいけないアセットがあるときやメモリをそんなに使用しないもの、どのプラットフォーム、デバイス間でも同じものを使うときやアプリ立ち上げ処理用に使う場合はResourcesフォルダを使用してもいいと書いてある。プロトタイプの作成時に使えると書いてあるがどうも今はそうじゃないらしい(Addressableが登場したことでプロトタイプすらResourcesは使わなくてよくなったらしい)。
でも
とりあえず動く部分だけ急ぎで作ろう
とか
軸となる部分作って感触を確かめよう
みたいな試作段階においてはResourcesフォルダを使ったアセット管理は役立つかもしれない。
最後に
UnityのResourcesについてまとめてみました。
Resourcesでのアセット管理が基本的にはあまり好ましくないことが書いててわかりました。
Resourcesを使わないアセット管理の方法としてAseetBundleとかAddressableAssetSystemというものがあるのですがそれについても記事にしたいと思います。
(ほんとはAseetBundleとかAddressableAssetSystemについてもこの記事にまとめたかったのですが長くなってしまいそうなのとこれらについての自分の理解をもっと深めたいという理由から別記事として出したいと思います)