PONOS Advent Calendar 2021の7日目の記事です。
昨日は@blockさんでした。
はじめに
Unityの物理演算はPhysXが採用されていますが、既に発表されているように新たにDOTSベースで動作するUnity Physics/Havok Physicsが開発されており、現在Preview版となっています。
Unity Physics/Havok Physicsについてはこちら
こちらによるとUnity Physicsは高速、軽量、ステートレス、カスタマイズ可能
と謳われています。
既に色々なところで紹介されていると思いますので、詳しい紹介は割愛し、今回はDOTSベースのUnity Physicsがこれまでのシステムと比較して本当に高速/軽量なのかどうか、試してみたいと思います。
検証内容
動作環境
今回はエディタ実行ではなく、Mac向けのアプリビルドを行ってパフォーマンスを確認します。
私の手元にあるMacBookProを使用します。スペックは以下の通りです。
CPU: Core i5-1038NG7 (Cores: 4, Threads: 8, Clockspeed: 2.0GHz)
Memory: LPDDR4X PC4-29800 32GB
GPU: Iris Plus Graphics G7
使用バージョン
Unity: 2019.4.33f1
* 2020を使用してもよいのですが、2020.3系でUnity Physicsを使用した場合、Default World DebugStream
というおそらくデバッグ用の何かだと思われる仕組みが、パフォーマンスの大半を使用してしまう現象に遭遇したためです。
PhysX: 4.1
* 2019.3以降は4.1が使用されている。。はず
Unity Physics: 0.4.1 preview
※ 2020.3だと0.6が最新だと思います。
何をするのか
負荷を計測するといっても、実際のゲームでは様々なシーンが想定されますし、一つのケースを見ただけではなんとも言えないとは思いつつも。。今回は単純に多くのCubeを使用してみようと思います。
一定の高さから地面に対して定期的に1000個のCubeを自由落下させます。
Cube一つのサイズは1、Massは0.3で、BoxColliderを持つものとします。
プロジェクトのPhysics関係の設定や、Time関連の設定は同Unityでプロジェクトを新規作成したときのデフォルト値とします。
つまり、定期的に1000個ずつCubeが落下してきて、衝突し、蓄積していきます。この時のFPSの変化を計測してみます。
検証
今回はどちらのバージョンもスクリプトバックエンドにはIL2CPPを選択して行います。
1. 従来版のプロジェクトを作る
ごくごく単純なので手順はほぼ割愛しますが、検証用のプロジェクトでいじったところを簡単に箇条書きします。
- 広めの地面を用意する(Colliderもね)
- CubeのPrefabを作成し、Rigidbodyを設定し、Massを0.3に設定する
- 適当なスクリプトを作成する。ここではこのCubeのPrefabをある程度の高さのところから、縦10x横10x高10に並べて生成するプログラムにしました。
- カメラ位置を全体を見渡せるところに移動させる。
- 生成タイミングをコントロールしたかったので、Cubeの生成を呼ぶボタンをつけました。
- その他、FPSを表示するためのコンポーネントをつけたりなんだりしました。
- スクリプトバックエンドをIL2CPPに変更
2. Unity Physics版のプロジェクトを作る
2-1 プロジェクトコピー
先ほど作ったプロジェクトを丸ごとコピー
2-2 パッケージを導入する
PackageManagerでUnity Physics
とHybrid Renderer
をインストールします。
※ Unity Physics
はGameObjectではなくEntity(ECS)として動作するため、そのままだとレンダリングされません。なので今回はHybrid Renderer
を利用しました。
(余談ですが、2020系のPackageManagerでは、これらのパッケージが一覧に表示されないようになっていました。設定からPreview Packageを有効にしても、それでも表示されません。
その際のインストール方法はUnityのヘルプデスクにあります。)
2-3 Unity Physicsに切り替える
今回はConvert To Entity
コンポーネントを使用します。
このコンポーネントはGameObjectからEntityに自動的に変換するためのコンポーネントです。プログラムから呼び出すAPIも存在しますが、特に必要ないためこれを使用することにしました。
- 地面のオブジェクトに
Convert To Entity
をアタッチする - CubeのPrefabに
Convert To Entity
をアタッチする
以上です。
この状態でゲームを実行すると、地面もCubeもGameObjectからEntityに変換され、ヒエラルキーから消え去ります。
コンバート方法を選べばGameObjectを残したりも出来ます。これらのオブジェクトを操作するためには、DOTS(ECS)を前提に行う必要が出てくるでしょうけれども、今回の検証ではそういったことは一切しないため、単純に変換して終わりです。
3. 従来版 vs Unity Physics版
1000個のときー
多少のスパイクはありますが、この時は双方ともほぼほぼ60FPSを維持できています。
(動きは少し異なっています。このケースではPhysX版のほうが大胆に動いています。)
従来版
Unity Physics版
3000個のときー
従来版では、新たな1000個が出現し、衝突した時に大きくFPSが落ち、最終的には40FPS前後で落ち着いています。
Unity Physics版では、わずかなスパイクはありますが、一貫してほとんど60FPSから落ちておらず安定しています。
従来版
Unity Physics版
6000個のときー
従来版では、流石に厳しくなりました。Cubeの出現時や衝突時には10FPSを下回り、平均的には15FPS程度です。
Unity Physics版では、瞬間的なスパイクを除けば、概ね30~35FPS程度で安定しています。しかしFPSは落ちてきましたね。
従来版
Unity Physics版
10000個のときー
従来版では、2FPSとかになったので割愛します。
Unity Physics版も15FPSまで落ちてきました。
Unity Physics版
まとめ
今回の検証では、ほぼほぼ2倍強程度のパフォーマンスの違いが確認できました。
Unity PhysicsはDOTSベースで動作するため、GameObjectを持ちませんし、純粋な物理演算以外の部分でもパフォーマンスに違いが生じていると思います。
また、負荷やFPSの推移をみている限りでは、従来版は出現時や衝突時のスパイクが大きく、一方でDOTSベースのシステムでは全くスパイクが無いわけではないですが、かなり緩やかな変化に感じました。
現在まだPreview版ですが、正式版では更なるパフォーマンスの改善が見込めるのでしょうか!?
ということで、明日も引き続き私の記事です!よろしくお願いします。