実際のプロジェクトで起こる問題の多くは「ケアレスミス」によるものです。「試験的な」臨時修正や、疲れたディベロッパーがうっかりクリックしたことにより、知らないうちにパフォーマンス性の低いアセットが追加されてしまったり、既存のアセットの重要な設定が変更されてしまうことも多々あります。
ある程度規模の大きなプロジェクトでは、人的ミスに対する防衛線を張るようにしましょう。小さなコードを書くことで比較的簡単に、誰かが 4K の非圧縮テクスチャをプロジェクトに追加してしまうのを防ぐことは可能です。
しかし、それでもなお、このようなミスは驚くほど頻繁に起こっています。 4K の非圧縮テクスチャは 60 MB のメモリを占領します。 iPhone 4S などの低性能モバイル端末では、 180~200 MB 程度を超えるメモリを消費するのは危険です。このようなテクスチャをうっかり追加してしまえば、アプリケーションのメモリ予算の 3 分の 1 から 4 分の 1 が意図せず占領されることとなり、診断が困難なメモリ超過エラーが発生してしまいます。
現在では 5.3 Memory Profiler を使用してこうした問題のトラッキングを行うことが可能ですが、このようなミスが発生する可能性自体を排除するほうが明らかに賢明です。
Unity エディタの AssetPostprocessor
クラスは、 Unity プロジェクトにおいて一定の最低基準が確実に満たされるようにするために使用できます。このクラスは、アセットのインポート時にコールバックを受け取ります。これを使用するには、 AssetPostprocessor
から継承して、 OnPreprocess
メソッドを 1 つ以上実装します。以下はその主な例です。
OnPreprocessTexture
OnPreprocessModel
OnPreprocessAnimation
OnPreprocessAudio
利用可能なその他の OnPreprocess
メソッドについては、スクリプト リファレンスの AssetPostprocessor をご覧ください。
public class ReadOnlyModelPostprocessor : AssetPostprocessor {
public void OnPreprocessModel() {
ModelImporter modelImporter =
(ModelImporter)assetImporter;
if(modelImporter.isReadable) {
modelImporter.isReadable = false;
modelImporter.SaveAndReimport();
}
}
}
これは、 AssetPostprocessor
がプロジェクトに規則を設定する単純な例です。
このクラスは、プロジェクトへのモデルのインポート時と、モデルのインポート設定が変更された際に呼び出されます。コードは Read/Write enabled
フラグ( isReadable
プロパティで表される)が true
に設定されているかどうか確認します。 true
に設定されていれば、フラグを強制的に false
にしてからアセットを保存して再インポートします。
SaveAndReimport
を呼び出すと、このコード スニペットが再度呼び出されることにご注意ください。ただし、この時点で isReadable
が確実に false になっているので、このコードが再インポートの無限ループを発生させることはありません。
この変更の理由は以下の「モデル」の項で説明しています。
Read/write enabled フラグは無効にする
Read/Write enabled
フラグを有効にすると、テクスチャがメモリ内に 2 回書き込まれます。 GPU で 1 回、 CPU アドレサブル メモリで 1 回です (1) ([注]
これは、ほとんどのプラットフォームで、 GPU メモリからのリードバックが極端に遅いためです。 GPU メモリからテクスチャを一時バッファに読み込んで CPU で使用する{[例] Texture.GetPixel }のは、非常に非効率的です。)。 Unity のデフォルトではこの設定は無効になっていますが、間違って有効にしてしまうことがあります。
Read/Write Enabled
は、(Texture.GetPixel
や Texture.SetPixel
API などで)シェーダー外でテクスチャデータを操作する場合以外は必要になりません。必要な場合以外は使用を控えるようにしてください。
Mipmap は可能な限り無効にする
Z 深度がカメラに対して比較的不変であるオブジェクトに関しては、Mipmap を無効にすることで、テクスチャの読み込みに掛かるメモリの約 3 分の 1 を節約することができます。オブジェクトの Z 深度が変化する場合は、 Mipmap を無効にすると、 GPU におけるテクスチャ サンプリングのパフォーマンスが劣化することがあります。
これは基本的に、UI テクスチャなどの、画面上において一定のサイズで表示されるテクスチャに適しています。
テクスチャは全て圧縮する
メモリを節約する上では、プロジェクトのターゲット プラットフォームに適したテクスチャ圧縮方式を使用することが非常に大切です。
選択したテクスチャ圧縮形式がターゲットプラットフォームに適していないと、テクスチャが読み込まれた際、解凍のために CPU 時間と過剰な量のメモリが消費されます。この問題は Android デバイスで最も頻繁に見られます。 Android デバイスは往々にして、チップセットに応じて多種のテクスチャ圧縮形式に幅広く対応しているためです。
テクスチャサイズの妥当な上限値を設定する
これは簡単なことですが、テクスチャのリサイズを忘れたり、テクスチャ サイズのインポート設定を意図せず変えてしまうことはよくあります。テクスチャのタイプに応じて適切な最大サイズを決め、コードから強制するようにしてください。
多くのモバイルアプリケーションの場合、テクスチャ アトラスには 2048x2048 あるいは 1024x1024、 3D モデルに適用されるテクスチャには 512x512 で十分です。
Read/Write enabled フラグは無効にする
モデルの Read/Write enabled
フラグも、上述のテクスチャの場合と全く同様に機能します。ただし、モデルの場合はこれがデフォルトで有効になっています。
このフラグは、スクリプト経由でランタイムでメッシュを修正する場合や、メッシュが MeshCollider コンポーネントの基盤として使用される場合には有効になっている必要があります。モデルが MeshCollider 内で使用されておらず、スクリプトによって操作されていない場合は、そのモデルのメモリを半分に節約するためにこのフラグは無効にしてください。
非キャラクターモデルのリグは無効にする
Unity のデフォルトでは、非キャラクターモデルには汎用リグがインポートされます。これは、モデルがランタイムでインスタンス化された場合に Animator
コンポーネントを 1 つ追加します。モデルがアニメーションシステムによってアニメーション化されていない場合は、このためにアニメーションシステムに不要なオーバーヘッドが追加されることになります。なぜなら、アクティブな Animator は全て 1 フレーム毎に 1 回チェックされる必要があるからです。
このように Animator コンポーネントが自動的に追加されてしまったり、不要な Animator がミスによってシーンに追加されてしまうのを防ぐために、アニメーションではないモデルのリグは無効にしてください。
アニメーションモデルの Optimize Game Objects オプションを有効にする
Optimize Game Objects オプションは、アニメーションモデルのパフォーマンスに大きく影響を及します。このオプションが無効になっている場合、 Unity は、モデルがインスタンス化されるたびに、そのモデルのボーン構造をミラーリングする大きな Transform ヒエラルキーを作成します。この Transform ヒエラルキーの更新には高い負荷が掛かります。特に、(Particle System や Collider などの)別のコンポーネントが添付されている場合はなおさらです。またこれにより、Unity の、メッシュ スキニングやボーンアニメーション計算をマルチスレッド処理する能力に制限が掛かります。
モデルのボーン構造の特定箇所をアクセス可能にする必要がある場合([例]武器モデルを動的に添付するためにモデルの手をアクセス可能にする場合など)は、そういった箇所は Extra Transforms
リスト内で手動でホワイトリストに登録することが可能です。
Unity マニュアルの モデルインポーターに関するページで、さらに詳細情報をご覧いただけます。
可能な限りメッシュ圧縮を使用する
メッシュ圧縮を有効にすると、モデルのデータの各チャンネルの浮動小数点数を表すのに使用されるビット数が削減されます。これにより精度が若干低下することがあります。最終版プロジェクトで使用する前に、アーティストがこの精度低下の影響をチェックする必要があります。
特定の圧縮レベルで使用される具体的なビット数は、スクリプト レファレンスの ModelImporterMeshCompression のページでご確認いただけます。
チャンネルごとに異なるレベルの圧縮を使用することも可能です。したがって、例えば同一プロジェクトでタンジェントと法線だけ圧縮し、 UV と頂点位置は非圧縮のままにするということもできます。
(注) Mesh Renderer Settings
Mesh Renderer をプレハブやゲームオブジェクトに追加する際は、コンポーネントの設定に注意が必要です。 Unity のデフォルトでは、 Cast Shadows(影の投影)、Receive Shadows(影の表示)、 Light Probe のサンプリング、 Reflection Probe のサンプリング、 Motion Vector の計算が有効になっています。
こうした機能を一つも必要としないプロジェクトの場合は、スクリプトによってこれらが自動的にオフになるようにしてください。また MeshRenderers を追加するランタイムコードも全て、これらの設定を切り替える必要があります。
2D ゲームでは、シャドウのオプションがオンになった MeshRenderer が間違ってシーンに追加されると、レンダリングループに完全なシャドウパスが追加されてしまいます。これは基本的にパフォーマンスの浪費です。
プラットフォームに適した圧縮設定
対応ハードウェアに適したオーディオ圧縮形式を有効にしてください。全ての iOS デバイスにはハードウェア MP3 解凍装置が搭載されており、 Android デバイスの多くは Vorbis にネイティブ対応しています。
さらに、非圧縮オーディオファイルを Unity にインポートしてください。プロジェクトがビルドされる際は毎回 Unity によってオーディオが再圧縮されます。圧縮オーディオをインポートしてからそれを再圧縮する必要はありません。そのようにするとオーディオの最終品質が低下してしまいます。
オーディオクリップを強制的に Mono にする
ステレオスピーカーを搭載したモバイルデバイスは少数です。モバイルプロジェクトの場合は、インポートされたオーディオクリップを強制的にモノチャンネルにすることで、その消費メモリが半分に削減できます。この設定は、 UI サウンドエフェクトなどの、ステレオエフェクトのないオーディオ全てに適用できます。
オーディオのビットレートを低くする
これはオーディオデザイナーと相談しながら行う必要がありますが、メモリ消費量やビルドされたプロジェクトのサイズを抑えるために、オーディオファイルのビットレートは可能な限り低くしてください。