ドキュメント

Unity Engine

Unity Engine

一般的な最適化

Apply broadly useful CPU optimizations, such as addressing properties by ID, to improve runtime performance.
読み終わるまでの所要時間 11 分最終更新 1ヶ月前

パフォーマンスの問題に様々な原因があるように、コードの最適化方法も様々です。通常、 CPU の最適化を行うに当たっては、事前にアプリケーションの綿密なプロファイルを行うことが強く推奨されます。ただし、どんな場合にも適用できる CPU の最適化もいくつかあります。

プロパティのアドレス指定を ID で行う

Unity では、Animator、Material、および Shader プロパティを内部的にアドレス指定するために、文字列名は使用されません。高速化のために、すべてのプロパティ名はプロパティ ID 内でハッシュ化され、プロパティを示すにはこの ID が使用されます。
したがって、Set や Get メソッドをアニメーター、マテリアル、シェーダーなどに使用する場合は、string 型ではなく int 型のメソッドを使用してください。文字列メソッドでは、文字列のハッシュが実行され、ハッシュされた ID が整数値のメソッドに転送されます。
文字列のハッシュから作成されたプロパティ ID は、1 回の実行中の間において有効です。この使用方法として最も簡単なのは、静的な読み取り専用の int 変数を各プロパティ名について宣言し、その int 変数を文字列の代わりに使用することです。これは、起動時に追加の初期化コードなしに自動的に初期化されます。
適切な API として、Animator のプロパティ名には Animator.StringToHash、Material と Shader のプロパティ名には Shader.PropertyToID が用意されています。

割り当てなしの物理演算 API を使用する

Unity 5.3 以降は、Physics クエリ API の割り当てなしのバージョンが提供されています。RaycastAll 呼び出しを RaycastNonAlloc、SphereCastAll 呼び出しを SphereCastNonAlloc などに置き換えます。2D アプリケーションの場合は、すべての Physics2D クエリ API の割り当てなしのバージョンもあります。

UnityEngine.Object サブクラスに対する Null 比較

Mono および IL2CPP のランタイムは、UnityEngine.Object から派生したクラスのインスタンスを特別な方法で処理します。インスタンスでメソッドを呼び出すと、実際にエンジンコードが呼び出されます。エンジンコードは、スクリプト参照をネイティブ参照に変換するための検索と妥当性検査を実行する必要があります。この型の変数を null と比較するコストは小さいですが、純粋な C# クラスと比較するコストよりもはるかに高くなります。このため、タイトなループやフレームごとに実行されるコードでは、これらの null 比較を避けてください。

ベクター、クォータニオンの計算および処理順序

タイトなループ内にあるベクターやクォータニオンの計算に関しては、「整数演算のほうが浮動小数点演算よりも速く、浮動小数点演算のほうがベクターやマトリックス演算やクォータニオン計算よりも速い」ということを覚えておきましょう。
このため、可換演算あるいは結合演算の可能な範囲では常に、 個々の計算処理のコストを最小限に抑えるようにしてください。
Vector3 x;int a, b;// Less efficient: results in two vector multiplicationsVector3 slow = a * x * b;// More efficient: one integer mult, one vector multVector3 fast = a * b * x;

ビルトインの ColorUtility

HTML 形式のカラー文字列 (
#RRGGBBAA
) と Unity ネイティブの
Color
および
Color32
構造体の間で変換を行う必要があるアプリケーションでは、Unify Community のスクリプトを使用するのが一般的です。このスクリプトは処理速度が遅く、また文字列操作のために大量のメモリ割り当てが発生していました。
Unity 5 以降は、これらの変換を効率的に行う ColorUtility API がビルトインで提供されています。可能な限り、こちらの API を使用してください。

Find と FindObjectOfType

一般的に、本番コードでは
GameObject.Find
および
Object.FindObjectOfType
の使用をすべて排除することが推奨されます。これらの API を使用すると、 Unity はメモリ内のすべてのゲームオブジェクトとコンポーネントの反復処理を行わなければならず、プロジェクトの規模が少し拡大すると効率性が大幅に低下します。
ただし、シングルトンオブジェクトのアクセサーの場合は例外となります。グローバルマネージャーオブジェクトは "instance" プロパティを公開することがよくあり、シングルトンの既存のインスタンスを検知するために getter で
FindObjectOfType
を呼び出すことがあります。
class SomeSingleton { private SomeSingleton _instance; public SomeSingleton Instance { get { if(_instance == null) { _instance = FindObjectOfType<SomeSingleton>(); } if(_instance == null) { _instance = CreateSomeSingleton(); } return _instance; } }}
このパターンは基本的には問題ありませんが、シングルトンのオブジェクトが存在しない場合に Scenes でアクセサーが呼び出されるようにするために、入念にコードを調査する必要があります。存在しないシングルトンのインスタンスを getter が自動的に作成しない場合、そのシングルトンを検索するコードが
FindObjectOfType
を繰り返し呼び出す (1 フレームに複数回の場合も多い) 結果となり、パフォーマンスに余計な負荷がかかることがよくあります。

デバッグコードと [conditional] 属性

UnityEngine.Debug
ロギング API は開発用以外のビルドからは削除されることはなく、呼び出されるとログファイルに書き込みを行います。ほとんどの開発者は開発用以外のビルドにデバッグ情報を書き込む意図はないため、以下のように、開発版限定のロギングコールをカスタムメソッドでラップすることをお勧めします。
public static class Logger { [Conditional("ENABLE_LOGS")] public static void Debug(string logMsg) { UnityEngine.Debug.Log(logMsg); } }
これらのメソッドを [Conditional] 属性で decorate することで、 Conditional 属性の使用する define が、コンパイルされたソース内に decorate されたメソッドが含まれるかどうか決定します。
Conditional 属性に渡された define がいずれも定義されていない場合は、デコレートされたメソッド、およびデコレートされたメソッドへのすべての呼び出しがコンパイルされます。この効果は、メソッドおよびメソッドへのすべての呼び出しが
#if … #endif
のプリプロセッサブロックでラップされた場合と全く同じになります。
Conditional
属性の詳細については、MSDN のウェブサイト (msdn.microsoft.com) を参照してください。