StrideでBepuPhysicsを物理エンジンとして使う

# 始めに

[Stride](https://stride3d.net)では、物理エンジンとして標準では[Bullet Physics](https://github.com/bulletphysics/bullet3)をラッピングしたものを使用している。

この記事では、これを[Bepu Physics](https://github.com/bepu/bepuphysics2)に差し替える方法を記述する。
なお、まだ若いパッケージなので頻繁にAPI変更がされているが、記事執筆時点では0.9.2.0をベースに記述する

# 背景

BulletPhysicsそのものは現在でもアクティブにメンテナンスされているものであり、特に問題は無い。
しかし、それを使っているStride側が以下の問題を抱えている。

* 最新のBulletPhysicsライブラリに追従していない
    * ネイティブライブラリ(しかも自前ビルド)+ラッパーライブラリという構成上追い辛い
* ラッピングライブラリの参照バージョンが古いかつあまり更新されていない
    * [BulletSharpPInvoke](https://github.com/AndresTraks/BulletSharpPInvoke)を使っている
* 内部にネイティブライブラリを抱えているので、リリース時の取り回しが面倒

そこで、代替として[公式のディスカッション](https://github.com/stride3d/stride/discussions/1858)ではBepu Physicsが挙げられている。
ただし、かなり大きな変更になるため、現時点では公式にサポートされるまでは時間がかかりそうな具合になっている。
現在非公式ではあるものの、[Stride.BepuPhysics](https://github.com/Nicogo1705/Stride.BepuPhysics)として公開されているものがあるため、これを使えば評価等は可能となっている。

# Bepu Physicsとは

[公式サイト](https://www.bepuentertainment.com/)

一言で言えば**ピュアマネージドな物理エンジン**。
また、v2メジャーバージョンアップしたことでパフォーマンスも優れているという売り文句。
Stride専用ということはなく、[Unityで利用することも可能らしい](https://github.com/AntoineCharton/Bepuphysics-Unity)。また、[Godotでも動作デモが見つかるので組み込みが可能と思われる(ソースは見つからず)](https://www.reddit.com/r/godot/comments/1725cyf/11000_physics_bodies_in_godot_with_bepuphysics_lib/)。
ただし、[Unityに関してはランタイムの影響でパフォーマンスはcoreclr版に比べてあまり良くないとのこと(チューニングすれば良くなる?)。](https://github.com/bepu/bepuphysics2/discussions/147)

# プロジェクトの準備

## 共通

1. [公式マニュアルに従って空プロジェクトを作る](https://doc.stride3d.net/latest/en/manual/get-started/create-a-project.html)
2. 作成されたslnに対してVSを起動する。
    * 便利なのでVSにしているだけで、VSCodeでも可
3. ゲームプロジェクト(Windowsとか付かない方)にStride.BepuPhysicsの参照を追加する

追加の仕方は下記

### ソースを参照する

git submodule等で [Stride.BepuPhysicsリポジトリ](https://github.com/Nicogo1705/Stride.BepuPhysics)を取り込み、ゲームプロジェクトにリポジトリ中の"Stride.BepuPhysics/Stride.BepuPhysics.csproj"をProjectReferenceに加える。

**執筆時点では仕様変更を含む修正が頻繁に行われているため、安定リリースになるまではどちらかと言えばこちらを推奨**

### NuGetパッケージを追加する

ゲームプロジェクトに、[Stride.BepuPhysics](https://www.nuget.org/packages/Stride.BepuPhysics)パッケージの追加を行う。

# アセットの設定

パッケージの追加が完了したら、次はアセットの設定を行う。
New Gameテンプレートで作るとSphereとGroundオブジェクトが存在するはずなので、本記事ではこの二つに物理エンジンの設定を行う。

組み込みの物理設定(RigidBody等)とは異なり、Strideエディタでの表示はできないので、形状を見ながらパラメーター調整というのは今はできない所に注意すること。

## コンポーネントを追加する

あるEntityに対して"Add Component"でコンポーネントを追加しようとすると、以下のような項目が追加されている。
![stride-bepu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103253/80a1e6bb-bb53-9693-4ab3-9b3e01807fc2.png)
組み込みの"RigidBody"あるいは"Static Collider"に対応するのは、"Bepu"以下のものになる。
"BodyComponent"が"RigidBody"、"StaticComponent"が"Static Collider"に対応する。
大きさ以外の大体の項目(Kinematicフラグ等)はそのコンポーネント内で設定する。

形状を表すコライダーに関しては、コンポーネント内部に"Colliders"というプロパティがあるので、そこに追加する。
StrideのRigidBodyは、元のエンティティのScaleに合わせて拡大したりするが、Stride.BepuPhysicsのコライダーは拡大したりしないので注意が必要。

### パラメーターの設定

物体が衝突したときの反発についての設定は、Stride組み込みのものやUnityと異なり、Restitutionではなく、以下のパラメーターで反発について設定するらしい。

* 固有振動周波数(SpringFrequency)
* 振動減衰率(SpringDampingRatio)
* 最大反発力(MaximumRecoveryVelocity)

それぞれ値を調整して具体的にどうなるかはまだ調査中。
SpringFrequencyがその物体の硬さ、振動減衰率が跳ねやすさといったところだろうか。
これについては以下URLにあるようにBepuPhysics2の仕様らしい。
https://github.com/bepu/bepuphysics2/blob/master/Documentation/QuestionsAndAnswers.md#where-is-the-bouncinessrestitution-material-property

## Sphereに設定を行う

1. Sphereオブジェクトに対して、"BodyComponent"を追加
2. 追加した"Body Component"の中に"Colliders"があるので、そこに"SphereCollider"を追加する

## Groundに設定を行う

1. Groundオブジェクトに対して、StaticComponentを追加する
2. 追加した"Static Component"の中に"Colliders"があるので、"BoxCollider"を追加する
3. 大きさをGroundに合わせて大きい値にする

# スクリプトの中で使う

## ボールを動かす

### コンテナを取得する

スクリプトの中で使う時は、Stride組み込みのコンポーネントと同様に `Entity.Get<T>` すればOK。
何らかの物理的な処理(物体を押す、ジャンプする、重力を変化させる)を行いたい場合、`Stride.BepuPhysics.Components`以下の名前空間から対応するコンテナを選択してGetする。

### 移動する

現時点では、コンテナをKinematicにした場合、親EntityのTransformではなく、コンテナのPositionとRotationの方を動かせば、親Entityも一緒に移動する。
この時、親Entityの方のTransformを動かしても、コンテナの場所に戻る挙動になるので注意が必要。

### 力を加える

Entityからコンテナコンポーネントの取り出しをしたら、それに対してApply系のメソッドを実行すると、弾き飛ばす動きやジャンプを表現できる
執筆時点では下記三つを適用できる

* `ApplyImpulse(Vector3 impulse, Vector3 impulseOffset)`
    * impulseで力を加える方向、impulseOffsetで、物体の中心からどれだけ離れた所から力を加えるかを設定する
    * 後述するApplyLinearImpulseとApplyAngularImpulseはこれの特殊形
* `ApplyLinearImpulse(Vector3 impulse)`
    * 直線方向に力を加える
* `ApplyAngularImpulse(Vector3 impulse)`
    * 回転を与える

# 終わりに

Stride標準の物理エンジンで若干挙動が不満な所があったので、代替としてこちらを試してみたが、
出力される動きとしては、個人的にはこちらの方がいいかなと思った。

ただ、Stride.BepuPhysicsの方はまだ若いパッケージなので、今後仕様変更を伴う修正も起こり得るので注意が必要となる。また、不具合もあるかもしれないので転んでも泣かないこと。

現在はStride本体に統合のPRを出しているので、その時になったらまた記事を更新するかもしれない。
https://github.com/stride3d/stride/pull/2131