物理演算のベストプラクティス

確認済のバージョン: 4.3

-

難易度: 中級

この講座では、ゲームに物理演算を使用する際に推奨される方法と、その理由も合わせて解説します。

Layer と Collision Matrix

すべてのゲームオブジェクトは、とくに設定が行なわれている場合を除いては Default レイヤー上に作成され、初期設定ではすべてのゲームオブジェクトが互いに衝突し合う状態になっています。しかしこれでは非常に非効率的ですので、どのオブジェクトをどのオブジェクトに衝突させるかの設定を行ないましょう。そのためには、各レイヤーを各種オブジェクトに対して定義する必要があります。

新規レイヤーごとに新しい行と列がひとつずつ Collision Matrix に追加されます。このマトリックスがレイヤー同士の相互作用の定義を扱います。デフォルトでは、新しいレイヤーを追加すると、そのレイヤーがその他すべての既存のレイヤーと衝突するように Collision Matrix に設定されます。したがって、相互作用の設定は開発者自身で行なう必要があります。レイヤーと Collision Matrix の設定を正しく行なうことで、不要な衝突や、衝突リスナーのテストを回避することができます。

ここに、説明のための簡単なデモを作成しました。ボックスのなかに2000個のオブジェクト(赤が1000、緑が1000)をインスタンス化しました。各色とも、同じ色どうしとボックスの壁に対してのみ相互作用を起こすようになっています。片方のテストでは、すべてのインスタンスが Default レイヤーに属し、文字列で衝突リスナーのゲームオブジェクトタグを比較することで相互作用が行なわれます。

もう一方のテストでは、各オブジェクトのタイプはそれ自体の Layer に設定されており、私自身で Collision Matrix を使用して各レイヤーの相互作用を設定しています。この場合は正しい衝突しか起こらないので文字列のテストは必要ありません。

Alt physicsbestpractices01

図 1:Collision Matrixの設定

下の画像は実際のデモの画像です。衝突の数をカウントして5秒後に自動的に一時停止する簡単なマネージャーが含まれています。共用のレイヤーを使用した方では、不要な衝突が大量に起こっていることがわかります。

Alt physicsbestpractices02

図 2: 5秒間に起こる衝突の数

より具体的にデータを確認できるよう、物理エンジンのプロファイラーもキャプチャーしました。

Alt physicsbestpractices03

図 3: 共用レイヤーを使用した場合および個別レイヤーを使用した場合の物理プロファイラーの比較

プロファイラーのデータを見るとわかるとおり、単一のレイヤーを使用した場合(平均 27.7 ms)と個別のレイヤーを使用した場合(平均 17.6ms)では、物理演算に使用される CPU のサイズが著しくことなります。

レイキャスト

レイキャストは、物理エンジンで使用できる非常に強力で便利なツールです。長さと方向を指定してレイを放射することができ、それが何かに当たったかどうか知らせてくれます。しかしこの処理には大きなコストがかかります。レイの長さやシーン内のコライダーの種類がパフォーマンスに大きく影響します。

ここで、レイキャストの使用に役立つヒントをご紹介しましょう。

  • ひとつ目は、基本的なことですが、レイの使用を必要最低限にとどめることです。
  • 必要以上にレイを長くしすぎないようにしましょう。レイのサイズが大きければ大きいほど、多くのオブジェクトのテストが必要になります。
  • FixedUpdate() 関数のなかではレイキャストを使用しないようにしましょう。場合によってはUpdate()のなかでさえも無駄になることがあります。
  • 使用しているコライダーの種類に注意してください。メッシュコライダーに対してレイキャストを使用すると非常にコストが高くなります。
    • この解決策として、プリミティブコライダーを持つ子を作成してメッシュの形を見積もる方法があります。同一の親リジッドボディの下にある子コライダーのすべてが、ひとつの複合コライダーとして挙動します。
    • どうしてもメッシュコライダーを使用する必要がある場合は、せめてそれをコンベックス型(凸型)にしてください。
  • レイが何に当たるようにするか具体的に指定し、レイキャスト関数にはつねにイヤーマスクをひとつ指定するようにします。
    • これは公式ドキュメントでも説明されていますが、レイキャスト関数で指定するのはレイヤーIDではなくビットマスクです。
    • つまり、IDが 10 のレイヤー上のオブジェクトにレイが当たるようにしたい場合は、10 と指定するのではなく、1<<10 (ビットが「1」を左に 10x シフトする)と指定します。
    • レイヤー 10 上のもの以外のすべてにレイをヒットさせたい場合は、単純に、ビット単位の補数演算子 (~) を使用します。これによってビットマスクの各ビットが無効になります。

緑のボックスだけに衝突するレイを放つオブジェクトの、簡単なデモを作成しました。

Alt physicsbestpractices04

図 4: 簡単なレイキャストのデモシーン

さらに上記の説明を、プロファイラーデータを実際に見て確認できるようにするため、レイの数と長さを編集してみました。以下の画像を見ると、レイの数と長さがパフォーマンスにどれほど影響を与えるかがわかります。

Alt physicsbestpractices05

図 5: レイの数がパフォーマンスに与える影響

Alt physicsbestpractices06 図 6: レイの長さがパフォーマンスに与える影響

こちらもデモ目的で、通常のプリミティブコライダーをメッシュコライダーに切り替えられるようにしてみました。

Alt physicsbestpractices07

図 7: Mesh Colliders シーン(コライダーひとつにつき頂点110個)

Alt physicsbestpractices08

図 8: プリミティブコライダーとメッシュコライダーのプロファイラーデータの比較

プロファイルグラフからもわかるように、メッシュコライダーに対するレイキャストのほうが、フレームごとの物理エンジンへの負荷が大きくなっています。

2D物理演算と3D物理演算

プロジェクトに適した物理エンジンを選びましょう。2D ゲームや 2.5D ゲーム(2D 面上の 3D ゲーム)の開発に 3D 物理エンジンを使用すると、無駄に負荷を掛けることになります。次元がひとつ高くなることで余計な CPU コストが掛かります。2D エンジンの場合と 3D エンジンの場合のパフォーマンスのちがいに関しては、私の以前の記事でご確認いただけます。

http://x-team.com/2013/11/unity3d-v4-3-2d-vs-3d-physics/

リジッドボディ

RigidBody コンポーネントは、オブジェクト同士の物理的な相互作用を追加する際に不可欠なコンポーネントです。コライダーをトリガーとして扱っている場合であっても、正常に機能させるためにはこれを OnTrigger イベント用にゲームオブジェクトに追加する必要があります。

RigidBody コンポーネントを持たないゲームオブジェクトは静的コライダーとして扱われます。この理解は非常に重要です。静的コライダーを動かそうとすれば物理エンジンは物理世界をいちから計算し直さなければならず、非常に非効率的だからです。幸いプロファイラーには、静的コライダーを動かそうとすると通知してくれる機能が付いており、CPU Profiler の警告タブに警告が出るようになっています。

静的コライダーを動かした際の影響をよりわかりやすく確認できるよう、上記で最初にご紹介したデモからすべての動くオブジェクトの RigidBody を削除し、新しいプロファイラーデータをキャプチャーしました。

Alt physicsbestpractices09

図 9: 静的コライダーを動かした際の警告

画像を見るとわかるとおり、動くオブジェクトごとにひとつずつ、合計で2000件の警告が出ています。また物理演算に使用された CPU の平均が 17.6ms から 35.85ms へとかなり増加しています。

ゲームオブジェクトを動かす場合、そのオブジェクトへの RigidBody の追加が不可欠です。オブジェクトの動きを直接制御したい場合は、そのリジッドボディのプロパティーでキネマティックとして設定します。

Fixed Timestamp (固定タイムスタンプ)

Time Manager でFixed Timestampを調整すれば、FixedUpdate()と 物理演算の更新頻度に直接影響を与えられます。この値を変更することで、物理演算に掛かる CPU 時間と精度とのバランスを調整することができます。

まとめ

ここにご紹介したヒントは、どれも簡単に設定・実装できます。皆さんの開発するゲームのほとんどに(たとえ衝突判定のためだけだとしても)物理エンジンが使用されますので、きっとプロジェクトのパフォーマンス向上につながるはずです。

Ricardo Aguiar

コミュニティー作者

Ricardo Aguiar is an Azorean developer with over 7 years of professional experience in the video game industry. He started his game development journey when completing a Masters Thesis on Game Development using Tracking and Visualisation Devices. From there he joined Seed Studios where he worked on games for Nintendo DS, iOS and the first portuguese PS3 game ‘Under Siege’. After that he went to work on X-Team where he’s been developing games for mobile devices using Unity.

関連するチュートリアル