今回は、Unityプロジェクトの最適化のヒントを紹介する連載の第5回目です。より少ないリソースでより高いフレームレートで動作させるためのガイドとしてご利用ください。これらのベスト・プラクティスを試したら、シリーズの他のページもぜひご覧ください:
- パフォーマンスを向上させるためのUnityプロジェクトの設定
- ハイエンドグラフィックス向けのパフォーマンス最適化
- PCおよびコンソールゲームのGPU使用量を管理する
- 高度なプログラミングとコード・アーキテクチャ
物理学は複雑なゲームプレイを作り出すことができるが、これにはパフォーマンス上のコストがかかる。これらのコストがわかれば、シミュレーションを微調整して適切に管理することができる。これらのヒントを使用して、目標フレームレートを維持し、UnityのビルトインPhysics(NVIDIA PhysX)を使用してスムーズな再生を作成します。
物理学で使用されるメッシュは、クッキングと呼ばれるプロセスを経る。これは、レイキャストやコンタクトなどの物理クエリで動作するようにメッシュを準備します。
MeshCollider には、メッシュを物理的に検証するのに役立ついくつかのCookingOptions があります。メッシュにこれらのチェックが必要ないと確信がある場合は、これらを無効にして調理時間を短縮することができる。
各MeshCollider のCookingOptionsで、EnableMeshCleaning、WeldColocatedVertices、CookForFasterSimulation のチェックを外します。これらのオプションは、実行時にプロシージャルで生成されたメッシュには有効ですが、メッシュがすでに適切な三角形を持っている場合は無効にできます。
PCをターゲットにしている場合は、Use Fast Midphaseを有効にしてください。これは、シミュレーションの中間段階で、PhysX 4.1の高速アルゴリズムに切り替わります(物理クエリのために交差する可能性のある三角形の小さなセットを絞り込むのに役立ちます)。非デスクトッププラットフォームでは、以下のような低速のアルゴリズムを使用しなければならない。 Rツリー.
メッシュコライダーは一般的に高価なので、より複雑なメッシュコライダーをプリミティブまたは簡略化したもので代用し、元の形状に近づけることを検討してください。
詳しくはCookingOptionsのドキュメントをご覧ください。
ゲームプレイ中にメッシュをプロシージャルに生成する場合、ランタイムにメッシュコライダーを作成することができます。しかし、メッシュに直接メッシュ・コライダー・コンポーネントを追加すると、メイン・スレッドで物理演算が行われます。これはかなりのCPU時間を消費する可能性がある。
Physics.BakeMeshを使用して、メッシュコライダーで使用するメッシュを準備し、ベイクされたデータをメッシュ自体と一緒に保存します。このメッシュを参照する新しいMesh Colliderは、メッシュを再度ベイクするのではなく、このベイク済みデータを再利用します。これは、シーンのロード時間やインスタンス化の時間を短縮するのに役立つ。パフォーマンスを最適化するために、メッシュの調理を別のスレッドにオフロードすることができます。 C#ジョブシステム.
複数のスレッドにまたがってメッシュをベイクする方法の詳細については、この例を参照してください。
Player Settingsで、Prebake Collision Meshesを可能な限りチェックしてください。また、コリジョンマトリックスの設定を見直して、プレイヤーとゲームメカニクスのオブジェクトが正しいレイヤーにあることを確認することをお勧めします。
不要なレイヤーのトリガーからコールバックを取り除くことは大きな勝利につながるので、レイヤーのコリジョンマトリックスをシンプルにしてみてください。Project Settings > Physicsで Physics Settingsを編集できます。
詳しくはCollision Matrixのドキュメントをご覧ください。
物理エンジンは、固定タイムステップで動作します。プロジェクトの固定レートを確認するには、Edit>Project Settings>Timeを選択してください。
Fixed Timestepフィールドは、各物理ステップで使用される時間差を定義する。例えば、デフォルト値の0.02秒(20ms)は、50フレーム/秒(fps)、つまり50Hzに相当する。
Unityの各フレームには可変の時間がかかるため、物理シミュレーションと完全に同期しているわけではありません。エンジンは次の物理タイムステップまでカウントアップする。フレームがわずかに遅くなったり速くなったりした場合、Unityは適切なタイムステップで物理シミュレーションを実行するタイミングを知るために経過時間を使用します。
フレームの準備に時間がかかる場合、パフォーマンスの問題につながる可能性がある。そのため、ゲームにスパイクが発生した場合(たとえば、多数のGameObjectをインスタンス化したり、ディスクからファイルをロードしたり)、フレームの実行に40ミリ秒以上かかる可能性があります。デフォルトの20ミリ秒固定タイムステップでは、次のフレームで2つの物理シミュレーションが実行され、可変タイムステップに "追いつく "ことになります。
余分な物理シミュレーションは、フレームの処理時間を増やすことになる。ローエンドのプラットフォームでは、これは性能の下降スパイラルにつながる可能性がある。
次のフレームの準備に時間がかかると、物理シミュレーションのバックログも長くなる。このため、フレームがさらに遅くなり、1フレームあたりに実行するシミュレーションの回数が増える。その結果、パフォーマンスが低下する。
最終的には、物理演算の更新間隔が最大許容タイムステップを超える可能性がある。この制限を超えると、Unityは物理演算のアップデートを中断するようになり、ゲームは膠着状態に陥る。
物理学のパフォーマンス問題を避けるため:
- シミュレーションの頻度を下げる:ローエンドプラットフォームの場合、Fixed Timestepをターゲットフレームレートよりわずかに大きくします。例えば、モバイルで30fpsの場合は0.035秒を使用する。これは、パフォーマンスの下降スパイラルを防ぐのに役立つだろう。
- 最大許容タイムステップを下げる:より小さな値(0.1秒など)を使用すると、物理シミュレーションの精度が多少犠牲になりますが、1フレームで行える物理アップデートの回数も制限されます。あなたのプロジェクトの要件に合うものを見つけるために、値を試してみてください。
- 必要に応じて、物理ステップを手動でシミュレートします:Physics SettingsのAuto Simulation オプションを無効にし、フレームのUpdateフェーズでPhysics.Simulateを直接呼び出します。これにより、物理学ステップを実行するタイミングを積極的に決定することができます。Time.deltaTimeを Physics.Simulateに渡して、物理演算とシミュレーションの時間を同期させます。
- 注:この方法は、特に複雑な物理現象やフレームタイムが大きく変化するシーンでは、物理シミュレーションが不安定になる可能性があります。使用には注意が必要だ。
詳細はPhysics.Simulateのドキュメントをご覧ください。
Unityの物理エンジンは2段階で動作する:
- 大まかな段階:スイープとプルーンのアルゴリズムを使用して、潜在的な衝突を収集します。
- 狭い段階:エンジンが実際に衝突を計算するとき
ブロードフェイズのデフォルト設定であるSweep and PruneBroadphase(Edit > Project Settings > Physics > Broadphase Type)は、一般的に平坦で、多くのコライダーがあるワールドに対して誤検出を発生させる可能性があります。シーンが広く、ほとんどが平らな場合は、この問題を避けて、自動ボックス剪定またはマルチボックス剪定ブロードフェイズに切り替えてください。これらのオプションは世界をグリッドに分割し、各グリッドセルはSweepとPruneを実行する。
マルチボックスプルーニングBroadphaseでは、ワールドの境界とグリッドセルの数を手動で指定できます。
フィジックスの全物件リストはこちら。
特定の物理ボディをより正確にシミュレートしたい場合は、Rigidbody.solverIterationsを増やします。これはPhysics.defaultSolverIterationsをオーバーライドします。Physics.defaultSolverIterationsは、Edit>Project Settings>Physics>Default Solver Iterationsで確認できます。
物理シミュレーションを最適化するには、プロジェクトのdefaultSolveIterationsに比較的低い値を設定します。そして、より詳細な情報が必要な個々のインスタンスに、より高いカスタムRigidbody.solverIterations値を適用する。
Rigidbody.solverIterationsの詳細はこちら。
トランスフォームを更新しても、Unityはそれを物理エンジンに自動的に同期させません。Unityはトランスフォームを蓄積し、物理アップデートが実行されるか、ユーザーが を呼び出すのを待ちます。.
物理演算とTransformをより頻繁に同期させたい場合は、次のように設定できます。 Physics.autoSyncTransformをTrueに設定できます(Project Settings > Physics > Auto Sync Transformsにもあります)。これを有効にすると、そのTransform上のRigidbodyや Colliderは、その子も含めて、自動的にTransformと一緒に更新されます。
ただし、どうしても必要でない場合は無効にしてください。一連の連続した物理クエリ(レイキャストなど)は、パフォーマンスの低下につながる可能性がある。
Physics.SyncTransformsの詳細をご覧ください。
衝突とトリガーイベント(OnCollisionEnter, オンコリジョンステイ, OnCollisionExit, オン・トリガー・エンター, オントリガーステイ, OnTriggerExit) は、これらの関数を実装し、相互作用の条件を満たすMonoBehaviourに対して発火します。これらのイベントは無効化されたモノアビにも送られる。
そのため、これらの関数は必要なときだけ実装することが推奨されている。空っぽの必要のない関数が呼び出されることになるからだ。OnCollisionStayとOnTriggerStayは、コライダーの数によって複数回呼び出される可能性があるため、特に注意が必要である。
コールバックMonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStayおよび MonoBehaviour.OnCollisionExitも衝突インスタンスをパラメータとして取ります。この衝突インスタンスは管理ヒープ上に割り当てられ、ガベージコレクションされなければならない。
生成されるゴミの量を減らすには Physics.reuseCollisionCallbacks(を有効にします(Project Settings > Physics > Reuse Collision Callbacksにあります)。これを有効にすると、Unityは各コールバックに1つのコリジョンペアインスタンスだけを割り当てる。これにより、ガベージコレクタ(GC)の無駄が減り、全体的なパフォーマンスが向上する。
注:ポスト処理のためにコリジョンコールバックの外でコリジョンインスタンスを参照する場合は、Reuse Collision Callbacksを無効にする必要があります。
スタティックコライダーは、コライダーコンポーネントを持ち、リジッドボディを持たないGameObjectです。その名前に反して、スタティック・コライダーは動かすことができる。
物理ボディの位置を変更し、位置の変化を蓄積し、物理が更新される前に同期させるだけだ。動かすためだけにスタティック・コライダーにリジッドボディ・コンポーネントを追加する必要はない。しかし、スタティック・コライダーをもっと複雑な方法で他の物理ボディと相互作用させたい場合は、スタティック・コライダーに キネマティック・リジッドボディ.使用 リジッドボディと 回転を使って動かす。これにより、物理エンジンの挙動がより予測しやすくなる。
注:2D物理では、ツリーの再構築に時間がかかるので、スタティック・コライダーを動かさないでください。
リジッドボディについての詳細はこちら。
特定の距離、特定の方向にあるコライダーを検出、収集するには、レイキャストや、次のような物理クエリーを使用する。 ボックスキャスト.
のように、複数のコライダーを配列として返す物理クエリ。 OverlapSphereや OverlapBoxのように、複数のコライダーを配列として返す物理クエリは、これらのオブジェクトをマネージドヒープに割り当てる必要があります。これは、ガベージ・コレクタが最終的に割り当てられたオブジェクトを回収する必要があることを意味する。
このオーバーヘッドを減らすには、これらのクエリのNonAllocバージョンを使用する。例えば、OverlapSphere を使用して、ある点の周りのすべての潜在的なコライダを収集する場合、必ず OverlapSphereNonAlloc.これにより、コライダーの配列(resultsparameter)を渡してバッファのように振る舞うことができる。
NonAllocメソッドはガベージを発生させずに動作する。そうでなければ、対応するアロケート・メソッドと同じように機能する。NonAllocメソッドを使用する場合は、十分なサイズの結果バッファを定義することを忘れないでください。バッファーの容量がなくなっても、バッファーは増えない。
詳しくはNonAllocメソッドのドキュメントを参照。
レイキャストクエリは 物理.レイキャストでレイキャストクエリーを実行できますが、かなりのCPU時間がかかります。これは、レイキャスト操作の数が多い場合に特に当てはまります(例えば、10,000エージェントの視線計算など)。
使用方法 RaycastCommandを使用して、C# ジョブシステムを使用してクエリをバッチ処理します。これにより、メインスレッドから作業をオフロードし、レイキャストを非同期かつ並列に行うことができる。
RaycastCommands ドキュメントの例を参照してください。
Physics Debugウィンドウ(Window > Analysis > Physics Debugger)を使用して、問題のあるコライダーや不一致のトラブルシューティングに役立てましょう。このウィンドウは、互いに衝突する可能性のあるGameObjectを色分けして表示します。
詳細については、Physics Debug visualizationページを参照してください。
これまでで最も包括的なガイドの1つで、PCとコンソール向けにゲームを最適化する方法について、80以上の実用的なヒントを集めている。サクセスとアクセラレート・ソリューションズの専門エンジニアが作成したこれらの詳細なヒントは、Unityを最大限に活用し、ゲームのパフォーマンスを向上させるのに役立ちます。