一般的な最適化
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 形式のカラー文字列 () と Unity ネイティブの および 構造体の間で変換を行う必要があるアプリケーションでは、Unify Community のスクリプトを使用するのが一般的です。このスクリプトは処理速度が遅く、また文字列操作のために大量のメモリ割り当てが発生していました。
#RRGGBBAAColorColor32Unity 5 以降は、これらの変換を効率的に行う ColorUtility API がビルトインで提供されています。可能な限り、こちらの API を使用してください。
Find と FindObjectOfType
一般的に、本番コードでは および の使用をすべて排除することが推奨されます。これらの API を使用すると、 Unity はメモリ内のすべてのゲームオブジェクトとコンポーネントの反復処理を行わなければならず、プロジェクトの規模が少し拡大すると効率性が大幅に低下します。
GameObject.FindObject.FindObjectOfTypeただし、シングルトンオブジェクトのアクセサーの場合は例外となります。グローバルマネージャーオブジェクトは "instance" プロパティを公開することがよくあり、シングルトンの既存のインスタンスを検知するために getter で を呼び出すことがあります。
FindObjectOfTypeclass SomeSingleton { private SomeSingleton _instance; public SomeSingleton Instance { get { if(_instance == null) { _instance = FindObjectOfType<SomeSingleton>(); } if(_instance == null) { _instance = CreateSomeSingleton(); } return _instance; } }}
このパターンは基本的には問題ありませんが、シングルトンのオブジェクトが存在しない場合に Scenes でアクセサーが呼び出されるようにするために、入念にコードを調査する必要があります。存在しないシングルトンのインスタンスを getter が自動的に作成しない場合、そのシングルトンを検索するコードが を繰り返し呼び出す (1 フレームに複数回の場合も多い) 結果となり、パフォーマンスに余計な負荷がかかることがよくあります。
FindObjectOfTypeデバッグコードと [conditional] 属性
UnityEngine.Debugpublic static class Logger { [Conditional("ENABLE_LOGS")] public static void Debug(string logMsg) { UnityEngine.Debug.Log(logMsg); } }
これらのメソッドを [Conditional] 属性で decorate することで、 Conditional 属性の使用する define が、コンパイルされたソース内に decorate されたメソッドが含まれるかどうか決定します。
Conditional 属性に渡された define がいずれも定義されていない場合は、デコレートされたメソッド、およびデコレートされたメソッドへのすべての呼び出しがコンパイルされます。この効果は、メソッドおよびメソッドへのすべての呼び出しが のプリプロセッサブロックでラップされた場合と全く同じになります。
#if … #endifConditional