DOTSベースのUnity PhysicsはPhysXよりどの程度軽量なのか？

[PONOS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ponos)の7日目の記事です。

昨日は[@block](https://qiita.com/block)さんでした。

# はじめに
Unityの物理演算は**PhysX**が採用されていますが、既に発表されているように新たにDOTSベースで動作する**Unity Physics/Havok Physics**が開発されており、現在Preview版となっています。

Unity Physics/Havok Physicsについては[こちら](https://unity.com/ja/unity/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を使用してみようと思います。

一定の高さから地面に対して定期的に１０００個のCubeを自由落下させます。
Cube一つのサイズは1、Massは0.3で、BoxColliderを持つものとします。
プロジェクトのPhysics関係の設定や、Time関連の設定は同Unityでプロジェクトを新規作成したときのデフォルト値とします。

つまり、定期的に1000個ずつCubeが落下してきて、衝突し、蓄積していきます。この時のFPSの変化を計測してみます。

# 検証
今回はどちらのバージョンもスクリプトバックエンドにはIL2CPPを選択して行います。
## 1. 従来版のプロジェクトを作る
ごくごく単純なので手順はほぼ割愛しますが、検証用のプロジェクトでいじったところを簡単に箇条書きします。

1. 広めの地面を用意する（Colliderもね）
1. CubeのPrefabを作成し、Rigidbodyを設定し、Massを0.3に設定する
1. 適当なスクリプトを作成する。ここではこのCubeのPrefabをある程度の高さのところから、縦10x横10x高10に並べて生成するプログラムにしました。
1. カメラ位置を全体を見渡せるところに移動させる。
1. 生成タイミングをコントロールしたかったので、Cubeの生成を呼ぶボタンをつけました。
1. その他、FPSを表示するためのコンポーネントをつけたりなんだりしました。
1. スクリプトバックエンドをIL2CPPに変更

## 2. Unity Physics版のプロジェクトを作る
#### 2-1 プロジェクトコピー
先ほど作ったプロジェクトを丸ごとコピー
#### 2-2 パッケージを導入する
PackageManagerで`Unity Physics`と`Hybrid Renderer`をインストールします。
※ `Unity Physics`はGameObjectではなくEntity（ECS）として動作するため、そのままだとレンダリングされません。なので今回は`Hybrid Renderer`を利用しました。

![スクリーンショット 2021-12-01 19.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/b57e3940-566b-ea88-191b-aa21dfa2bd99.png)


（余談ですが、2020系のPackageManagerでは、これらのパッケージが一覧に表示されないようになっていました。設定からPreview Packageを有効にしても、それでも表示されません。
その際のインストール方法はUnityの[ヘルプデスク](https://helpdesk.unity3d.co.jp/hc/ja/articles/900002171066-Unity-2020-1-%E3%81%8B%E3%82%89%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%81%A7%E7%99%BA%E8%A6%8B%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%96%B9%E6%B3%95)にあります。）

#### 2-3 Unity Physicsに切り替える
今回は`Convert To Entity`コンポーネントを使用します。
このコンポーネントはGameObjectからEntityに自動的に変換するためのコンポーネントです。プログラムから呼び出すAPIも存在しますが、特に必要ないためこれを使用することにしました。

1. 地面のオブジェクトに`Convert To Entity`をアタッチする
1. CubeのPrefabに`Convert To Entity`をアタッチする

![スクリーンショット 2021-12-01 19.29.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/0dc3daf3-9315-6852-bb3e-1e799367f369.png)


以上です。
この状態でゲームを実行すると、地面もCubeもGameObjectからEntityに変換され、ヒエラルキーから消え去ります。
コンバート方法を選べばGameObjectを残したりも出来ます。これらのオブジェクトを操作するためには、DOTS（ECS）を前提に行う必要が出てくるでしょうけれども、今回の検証ではそういったことは一切しないため、単純に変換して終わりです。

## 3. 従来版 vs Unity Physics版
#### 1000個のときー
多少のスパイクはありますが、この時は双方ともほぼほぼ60FPSを維持できています。
（動きは少し異なっています。このケースではPhysX版のほうが大胆に動いています。）
**従来版**
<img width="800" alt="スクリーンショット 2021-12-01 19.43.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/eb2800f1-6900-96ed-574f-0cfc1f9feb7c.png">
**Unity Physics版**
<img width="800" alt="スクリーンショット 2021-12-01 19.44.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/f3407200-2464-3ff9-8e6c-8324ed97ebd2.png">

#### 3000個のときー
従来版では、新たな１０００個が出現し、衝突した時に大きくFPSが落ち、最終的には40FPS前後で落ち着いています。
Unity Physics版では、わずかなスパイクはありますが、一貫してほとんど60FPSから落ちておらず安定しています。
**従来版**
<img width="800" alt="スクリーンショット 2021-12-01 19.50.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/1eb3594f-d278-9123-34cf-fd0e8df61593.png">
**Unity Physics版**
<img width="800" alt="スクリーンショット 2021-12-01 19.51.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/a5c1d5b3-98ef-f606-7d4b-45d929b5ae95.png">

#### 6000個のときー
従来版では、流石に厳しくなりました。Cubeの出現時や衝突時には10FPSを下回り、平均的には15FPS程度です。
Unity Physics版では、瞬間的なスパイクを除けば、概ね30~35FPS程度で安定しています。しかしFPSは落ちてきましたね。
**従来版**
<img width="800" alt="スクリーンショット 2021-12-01 19.56.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/fdaeea49-1a8b-76b5-b947-f6b056eb72ee.png">
**Unity Physics版**
<img width="800" alt="スクリーンショット 2021-12-01 19.57.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/519c1dca-2c2a-c678-1c5d-18f87cd66e09.png">

#### 10000個のときー
従来版では、2FPSとかになったので割愛します。
Unity Physics版も15FPSまで落ちてきました。
**Unity Physics版**
<img width="800" alt="スクリーンショット 2021-12-01 20.02.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911864/2ed166ae-383a-b298-bf42-662974a0b6d4.png">

# まとめ
今回の検証では、ほぼほぼ２倍強程度のパフォーマンスの違いが確認できました。
Unity PhysicsはDOTSベースで動作するため、GameObjectを持ちませんし、純粋な物理演算以外の部分でもパフォーマンスに違いが生じていると思います。
また、負荷やFPSの推移をみている限りでは、従来版は出現時や衝突時のスパイクが大きく、一方でDOTSベースのシステムでは全くスパイクが無いわけではないですが、かなり緩やかな変化に感じました。

現在まだPreview版ですが、正式版では更なるパフォーマンスの改善が見込めるのでしょうか！？

ということで、明日も引き続き私の記事です！よろしくお願いします。