正式リリースされたHavok Physics for Unityでニュートンのゆりかごは実現出来るのか

# はじめに

Havok Physics for Unityが製品リリースとなりました。
https://blog.unity.com/technology/havok-physics-now-supported-for-production

Unity Physicsに比べ、物体同士が重なっても安定してシミュレーションできるのが特徴のようです。
![スクリーンショット 66.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/2b72d3e1-63ba-0f32-4418-813cb00fe82a.png)
> Unity Physics（右の例）は重なっている静的/動的なリジッドボディを１フレームで解決することを試みます。一方、Havok Physics（左の例）は数フレームにわたってスムーズに解決します。
https://blog.unity.com/ja/technology/havok-physics-in-unity

そこで今回は。ニュートンのゆりかごを検証に使い
誤差が生じやすい跳ね返りの運動について、
どの程度改善されているのかを確かめました。

# 検証環境
Macbook Air M1, 2020
macOS Monterey
Unity 2022.2.0f1

# 検証

[EntityComponentSystemSamples-master](https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/PhysicsSamples)
こちらのサンプルを改造していき、以下３つの物理シミュレーションで比較します。
- PhysX(バニラのUnityプロジェクト)
- Unity Physics(DOTS)
- Havok Physics(DOTS)

なおHavok Physicsへの切り替えはスイッチ１つで行えるため、同一シーンでの検証が可能です。
![スクリーンショット 67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/41199655-37c3-1e22-b9e1-050bae9ef2a1.png)

## １.単純な反発運動
まずはボールを床に自由落下させ、跳ねさせるシーンを作ってみます。
![スクリーンショット 52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/62c97a39-0d28-9c81-7e72-3e0284ef5d5f.png)

条件は以下のように設定してみます
- ボールと床の反発係数を１にする
- 摩擦や空気抵抗を0にする

エネルギー保存則から、もし理想的な動きをした場合は
ボールはスーパーボールみたいにはねてもとの高さに戻ると考えられます。

### 結果
Unity Physics、Havok Physicsともに
跳ね返るたびに最大到達点が減衰し、最終的には床で静止してしまいます。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/1d51b02c-4ad6-21c4-1a64-252e1591f441.png)

一方PhysXでは逆の結果となります。
最初はこの高さでしたが
![スクリーンショット 69.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/db1089a8-5b38-e813-91a0-333702b211f0.png)
このようにどんどん高くなってしまいます。
![スクリーンショット 70.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/3cbdafa5-725f-bb88-eead-dd6a95b8a88b.png)

この原因は、以下の動画でも説明されているように物体同士が潜り込んで誤差が生まれているからだと考えられます。

https://youtu.be/Ju4ILgpuVHE?t=1848

衝突したフレームを見ると確かにめりこんでいます。
このめりこみを解決するために、垂直上方向に余計な力が加わっているのでしょう。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/de86b023-9fb2-094b-2c24-c44e4d473d84.png)

この例では、PhysXはめり込みのせいで直感とだいぶ異なる動きをするものの、
Unity PhysicsとHavok Physicsの差はほぼ見られないという結果になりました。

## ２.ニュートンのゆりかご

### PhysXでの例
PhysXで純粋なシミュレーションをしようとしている方がいたので動画を紹介します。やはり難しそうです。
<iframe width="560" height="315" src="https://www.youtube.com/embed/yh4nE1GIqGM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### 実装
サンプルのJoint Paradeを改造し、複数の振り子を横に並べて
ニュートンのゆりかごを作ってみます。
画像ではボールが空中に浮いているように見えますが、上からロープで吊るされていると想像してください。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/46bf6c68-2077-43fe-5790-0a0ffc5aae37.png)

反発や摩擦の設定は１と同様で、
今度はボールはZ軸(奥)方向には動かさず、回転も固定していきます。
Rigidbodyではコンストレイントで設定できるものですね。
![スクリーンショット 71.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/38c1e2ad-498f-3b55-afb3-c3a7bbe2c1bd.png)

Physics Body(ECS)ではLimit DOF Jointなるコンポーネントで設定できます。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/0067ef41-52b4-d778-e173-bb252f2e48ff.png)

また、ロープで吊っているようなジョイントを設定するにはLimited Distance Jointを使います。
今回はロープはたるまず常にピンと張った状態にしたいので、Min DistanceとMaxDistanceは同じ値に設定します。
![スクリーンショット 72.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/ffb47412-082f-d5ab-48e5-c5063d8240b4.png)

また衝突のめりこみ解決について、精度を上げるために iteration countを上げてみます。
![スクリーンショット 73.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957530/e4c9c811-8377-4498-fef6-9e0ec45039a5.png)

### 結果
ボールの数を増やしたり、より高い位置から落としたりするとどうしてもうまく行かないようでした。しかしUnityとHavokで弾性衝突について差が見られたので紹介します。

#### ボールが2つのとき
以下のビリヤードの例のように、最初に動いていたのボールは衝突後にピタッと静止するはずです。
<iframe width="560" height="315" src="https://www.youtube.com/embed/QK2r0w-1vFU?start=53" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Unity Physicsでは、ボールは衝突後も静止せず、若干動いてしまっています。後半はより誤差が大きくなっていますね。
<iframe width="560" height="315" src="https://www.youtube.com/embed/VWkftPxs7rU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

一方Havok Physicsでは、若干の動きはあるもののほぼ安定して動いているようです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/LYpmoUj1ck0?start=37" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

#### ボール5個の例
そのままではなかなかうまく動作しないので、ボール同士の間隔を若干増やしました。ピタッとくっついているとボール同士がすぐめり込んでしまうので、解決が難しそうですね。

Unity Physicsでは、２個の例と同じくボールが動いているせいで減衰が早いですね。
<iframe width="560" height="315" src="https://www.youtube.com/embed/yGTBw8-uVuU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Havok Physicsでは、ある程度までの速度であれば上手に弾性衝突を処理してくれるようですが、ボールの高さを上げるとめちゃくちゃな動きになってしまいます。
<iframe width="560" height="315" src="https://www.youtube.com/embed/W7aOSUvpXIQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# まとめ

やはり純粋に物理シミュレーションだけでニュートンのゆりかごを実装するのは難しそうですが、Havok Physicsはめりこみ解決が上手く、弾性衝突そのものの動きがとてもそれらしく処理できていると感じました。
もちろん自前でうまく制御する手はありますが、それでもプロダクトの性質によっては選択肢に入ってくるかもしれませんね。