UE4の物理アセットと自作アニメーションブループリントノードでキャラクターの物理挙動をいい感じにする

#概要
この記事では、__「UE4の物理アセットを使い、アニメーションブループリントノードを自作することで、キャラクターの物理挙動を実装する方法」__について記述しています。
物理シミュレーション対象のシェイプとコリジョンは、物理アセットで設定します。
キャラクターの物理、いわゆる骨物理に話を限定します。

先に結果を見せると、この記事の中ではこのようなことをやります。

遅延揺れによる髪の揺れ
![GrayChanHairDelayAlpha.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/a2aefa0f-2b6f-b480-2b05-f1caec0ff769.gif)

階段を揺れながら降りるスライム
![階段を揺れながら降りるスライム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/971b3f90-0eb0-31d0-7894-5e7af8948415.gif)

背後からグレイマンに襲いかかり肩にのってしまうスライム
![背後からグレイマンに襲いかかり肩にのってしまうスライム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/52672ef7-6ddb-d26f-aaf9-787147175e1e.gif)

#背景
物理シミュレーションをカスタマイズしたいと思ったことはないでしょうか。

剛体のパラメータを設定するとか、物理エンジンのパラメータを設定するとか、コンストレイントを設定するという話ではありません。
シミュレーションの内部アルゴリズムをカスタマイズしたいと思ったことはないでしょうか。
僕はたまにあります。

物理エンジンにとってのコンストレイントは描画エンジンにとってのシェーダのようなものだ、という言葉をGDCのセッションか何かで聞いたことがあります。
逆に言うと、コンストレイントくらいしか物理シミュレーションをカスタマイズする手段が用意されてないということです。

皆さんおなじみのKawaiiPhysicsは、UE4の物理エンジンで揺れ物の表現が難しいという問題を、揺れ物向きのシミュレーションアルゴリズムをアニメーションブループリントノード内で実装することで解決しています。
独自のアニメーションブループリントノードを作れば、独自の骨物理は実装できます。

様々な物理シミュレーションアルゴリズムを検証する砂場がほしいと思ったとき、キャラの骨にシェイプをアタッチしたりコンストレイントを設定できるエディタがほしくなります。
ただ、砂場のためにエディタをスクラッチから自作するのは面倒くさいです。
物理アセットエディタという素晴らしいエディタがあるのですから、それを使いたくなるのが人情（？）というものです。

この記事では、物理アセットをアニメーションブループリントの設定データとして使う方法を説明します。
その上で、2つほど独自の骨物理を実装します。


#自作アニメーションブループリントノードで物理アセットのデータを読み出す

アニメーションブループリントノードの実装手段はC++のみです。
BPは使えません。
アニメーションブループリントノードの実装方法について知りたい方は、他の記事を見たり、UE4の既存のノードのソースコードを読んでください。

まずは、物理アセットのデータを読み出してキャラクター上にデバッグ描画するところまでやってみましょう。
Githubにソースコードを置いているので直接読みたい方はそちらからどうぞ。
[物理アセットを読みだしてデバッグ描画するカスタムノードのソースコード](https://github.com/monguri/UE4ProceduralSkeletalMeshDemo/blob/main/Source/UE4ProcSkelMeshDemo/AnimNode_DrawPhysicsAsset.cpp "物理アセットを読みだしてデバッグ描画するカスタムノードのソースコード")

ソースコードの中から主要な部分を抜粋します。

```cpp
for (int32 i = 0; i < UsePhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
	int32 BoneIndex = SkeletalMeshComp->GetBoneIndex(UsePhysicsAsset->SkeletalBodySetups[i]->BoneName);

	FTransform BoneTM = SkeletalMeshComp->GetBoneTransform(BoneIndex);
	float Scale = BoneTM.GetScale3D().GetAbsMax();
	FVector VectorScale(Scale);
	BoneTM.RemoveScaling();

	FKAggregateGeom* AggGeom = &UsePhysicsAsset->SkeletalBodySetups[i]->AggGeom;

	for (int32 j = 0; j < AggGeom->SphereElems.Num(); ++j)
	{
		FTransform ElemTM = AggGeom->SphereElems[j].GetTransform();
		ElemTM.ScaleTranslation(VectorScale);
		ElemTM *= BoneTM;
		::DrawDebugSphere(World, ElemTM.GetLocation(), AggGeom->SphereElems[j].Radius * Scale, 16, ElemSelectedColor, bPersistent, LifeTime, ESceneDepthPriorityGroup::SDPG_Foreground);
	}

	for (int32 j = 0; j <AggGeom->BoxElems.Num(); ++j)
	{

	・・・省略・・・
}
```

今回、UPhysicsAssetEditorSkeletalMeshComponent::RenderAssetTools()という物理アセットエディタのソースコードを参考にしています。

要所のみ取り出して説明します。

UPhysicsAsset.SkeletalBodySetupsはのTArray\<USkeletalBodySetup*\>型で、骨ごとに物理ボディの設定を保持しています。

```cpp
for (int32 i = 0; i < UsePhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
```

USkeletalBodySetupが骨の名前を保持しているので、骨インデックスを取得して、ワールド座標系のFTransformを取得します。

```cpp
	int32 BoneIndex = SkeletalMeshComp->GetBoneIndex(UsePhysicsAsset->SkeletalBodySetups[i]->BoneName);
	FTransform BoneTM = SkeletalMeshComp->GetBoneTransform(BoneIndex);
```

物理ボディから、設定されているシェイプ配列を取り出します。
SphereElems、BoxElems、SphylElems（カプセル）、ConvexElems、TaperedCapsuleElemsと、シェイプの種類ごとにTArrayをもっています。
各シェイプのTArrayをループします。

```cpp
	FKAggregateGeom* AggGeom = &UsePhysicsAsset->SkeletalBodySetups[i]->AggGeom;
	for (int32 j = 0; j < AggGeom->SphereElems.Num(); ++j)
	{
```

シェイプがもっているFTrasnformは、骨の座標系での骨の位置からのオフセットにあたります。
よって、先に取得しておいた骨のFTransformと乗算すると、ワールド座標系でのシェイプのFTransformを計算できます。

デバッグ描画関数でシェイプを描画します。

```cpp
		FTransform ElemTM = AggGeom->SphereElems[j].GetTransform();
		ElemTM *= BoneTM;

		::DrawDebugSphere(World, ElemTM.GetLocation(), AggGeom->SphereElems[j].Radius * Scale, 16, ElemSelectedColor, bPersistent, LifeTime, ESceneDepthPriorityGroup::SDPG_Foreground);
	}

	・・・省略・・・
}
```

デバッグ描画の結果は、グレイマンですと以下のようになります。
モーションを乗算した物理アセットのシェイプの位置が正確に読み取れていることがわかります。
![ThirdPersonDrawPhysicsAsset.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/efd8cb91-f06c-f200-96d8-5e6f76709c5c.gif)
物理アセットは骨からの各シェイプ（物理ボディ）のオフセット位置をもっていて、骨の位置自体はアニメーションブループリントへの入力を使えばいい、というのが扱いやすいですね。

これで、物理アセットをアニメーションブループリントノードで利用できることがわかりました。
次の節から、独自の骨物理を実装してみます。


#KawaiiPhysicsで物理アセットを使う

次の節から独自の骨物理を実装する、と書きましたが、すみません、あれは嘘でした。
最初はKawaiiPhysicsの改造をやってみることにします。
皆さんお馴染みなので具体例としてイメージしやすいですからね。

ソースコードは以下に置いています。
[フォークして改造したKawaiiPhysicsノード](https://github.com/monguri/KawaiiPhysics/blob/master/Plugins/KawaiiPhysics/Source/KawaiiPhysics/Private/AnimNode_KawaiiPhysics.cpp "フォークして改造したKawaiiPhysicsノード")

KawaiiPhysicsでは揺れる側と衝突する側の2つのシェイプ設定がありますが、まずは衝突する側を物理アセットで設定できるようにします。
コリジョンのアルゴリズム自体は変えません。
やってることは前節の延長なので、ソースコードの説明は省略し、結果だけ見せてさらっと流します。

KawaiiPhysicsに入ってるGrayChanの太もものカプセルを物理アセットで作って設定します。
![GrayChanSkirtPhysAssetSetting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/cff85954-b56d-ab35-4d11-fbf16bc462fa.jpeg)

こちらが結果です。
スカートにKawaiiPhysicsを設定しています。
右が物理アセットのカプセルで左がKawaiiPhysics内でのカプセルの設定で動作しています。
コリジョンの挙動が同じなので成功です。
![KawaiiPhysicsSkirt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/2d597bee-d62b-5bcd-a39c-a1ea9be11a42.gif)

チェインのモデルでも一応やってみましょう。
スフィアコリジョンを物理アセットで作っています。
特に問題ありません。
![Chain0_CollisionPhysicsAsset.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/cafccfcc-9069-5225-c6fe-5c88c99cfbd5.jpeg)

次に、揺れる側も物理アセットの設定を使ってみます。
KawaiiPhysicsは揺れる側はスフィアシェイプのみ使える仕様なので、試しにカプセルシェイプも使えるように拡張してみました。

チェインに対し、揺れる側も衝突する側も物理アセットにして、カプセルシェイプを設定した結果が下の画像です。
まあまあかなと思いますが、カプセルの押し出しはスフィアほどシンプルではないので、今回はほどほどのクオリティで作り込みを止めています。
ソースコードをご覧になる方はその点をご了承ください。
![Chain1_CollisionCapsuleShapePhysicsAsset.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/bd8b5bae-7e0c-0023-10c7-94b52fc124f9.jpeg)


#遅延型の揺れアルゴリズムを実装してみる

次こそは独自の骨物理を実装してみます。

ここでは、遅延を用いた揺れアルゴリズムを実装してみました。
__遅延でチェーンを揺らすというわけですね。__

新規にノードをスクラッチから実装するのが面倒だったため、KawaiiPhysicsノードのソースコードに別モードとして処理を追加しました。
[改造したKawaiiPhysicsアニメノード](https://github.com/monguri/KawaiiPhysics/blob/master/Plugins/KawaiiPhysics/Source/KawaiiPhysics/Private/AnimNode_KawaiiPhysics.cpp "改造したKawaiiPhysicsアニメノード")

まずは使い方と結果から見ましょう。

画像のようにフラグを立てて、PhysicsSetting内でDelayAlphaという0から1の範囲のパラメータを設定します。
GrayChanの髪に適用してみます。
![GrayChanHairDelayAlpha.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/74ee5837-05ab-12e2-22be-d94476572db2.jpeg)

冒頭でも貼ったように、以下のようになります。
悪くはないと思います。
![GrayChanHairDelayAlpha.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/a2aefa0f-2b6f-b480-2b05-f1caec0ff769.gif)

実装を見ていきましょう。
KawaiiPhysicsノードとFKawaiiPhysicsSettingsに以下のようなフラグとパラメータを追加しました。

```cpp
UPROPERTY(EditAnywhere, Category = Mode)
bool bUseDelayMode = false;

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"), category = "KawaiiPhysics")
float DelayAlpha = 0.5f;
```

以下が揺れの処理です。

```cpp
for (int i = 0; i < ModifyBones.Num(); ++i)
{
	auto& Bone = ModifyBones[i];
	Bone.PrevLocation = Bone.Location;
	auto& ParentBone = ModifyBones[Bone.ParentIndex];

	FVector BoneNotDelayedLocation;
	if (ParentBone.ParentIndex < 0)
	{
		// Parentがルートのときは入力位置をDelayのガイドとする
		BoneNotDelayedLocation = Bone.PoseLocation;
	}
	else
	{
		// ParentがルートでないときはGrandParentとParentを結んだ方向にローカルなRotationを加えた位置をDelayのガイドとする
		auto& GrandParentBone = ModifyBones[ParentBone.ParentIndex];
		const FQuat& BoneLocalPoseRotation = FQuat::FindBetweenVectors(ParentBone.PoseLocation - GrandParentBone.PoseLocation, Bone.PoseLocation - ParentBone.PoseLocation);
		BoneNotDelayedLocation = ParentBone.Location + BoneLocalPoseRotation * (ParentBone.Location - GrandParentBone.Location).GetSafeNormal() * (Bone.PoseLocation - ParentBone.PoseLocation).Size();
	}

	Bone.Location = FMath::Lerp(Bone.PrevLocation, BoneNotDelayedLocation, Bone.PhysicsSettings.DelayAlpha);
}
```

アルゴリズムを説明します。
親の骨の位置が決まったら、次の子の骨の位置は、
前フレームの位置と、入力モーションどおりの回転を適用した位置との間の線形補間（Lerp）で決定します。
線形補間に用いるAlpha値として先程上げたDelayAlphaパラメータを用います。
親の骨が動くと、それに子供が遅れてついてくるような挙動になります。
KawaiiPhysicsのStiffness項とDamping項の部分のみ、上記の計算で差し替えている形です。

遅延揺れアルゴリズムのメリットは、揺れ具合を1パラメータで調整できるので挙動を直観的に制御しやすいことです。
KawaiiPhysicsなど、StiffnessとDampingの2パラメータで揺れを設定するものは多くあります。
ですが、意図通りの揺れをこの2パラメータで設定するのが難しいことがあります。
ひとつの理由としては、バネの固さと減衰は直観的に独立した2パラメータだと感じるのに、実際に調整してみると、一方を変えるともう一方に影響する感じになり直観的な調整が難しいケースがある、ということがあります。

デメリットは、いわゆるオーバーシュート（反動により基準位置を超えて振動すること）の挙動が表現できないことです。
オーバーシュートがないと、揺れ物によっては、やはりそぐわない感じがします。

一方で、オーバーシュートが無いことで、オーバーシュートによって生まれる暴れが原理的に生じません。それも制御しやすい一因になっています。
特に、高速で動くチェイン構造や高周波数で振動するチェイン構造の揺れ表現では、暴れが一切生じないので扱いやすいです。
例えばマグロとか蛇など。

KawaiiPhysics以上に、実際の物理からは程遠い、見た目を満足させることが目的の揺れアルゴリズムとなっています。


#スライムの物理を作ってみる

揺れものはゲームプレイに組み込むというより賑やかしのためにつけるケースが多いです。
ですが、ここではキャラクターのゲーム性そのものを表現する物理を模索してみます。

冒頭で紹介したスライムの物理を作ります。
![階段を揺れながら降りるスライム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/971b3f90-0eb0-31d0-7894-5e7af8948415.gif)

まずは、表現に適したスケルタルメッシュを用意します。
用意したメッシュがこちらとなります。
![BoxSkeletalMeshTetrahedronSkeleton.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/68a9089d-49db-8b62-4ae5-5d2f14635ed9.jpeg)

この記事のために、プログラムからスケルタルメッシュを生成する方法を調査しました。
その際に得たノウハウは、もう一個アドベントカレンダーの枠をもらって記事にさせていただきました。
[UE4のスケルタルメッシュアセットをプログラムで生成する]
(https://qiita.com/monguri/items/e74e7bfaad29f692483c "UE4のスケルタルメッシュアセットをプログラムで生成する")

マテリアルを設定してスライムの外観を作ります。
![SK_Slime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/78daea04-9fe6-e164-59d1-6d9f7feb616b.gif)
骨の位置がメタボールの位置になっているので、骨が動けばスライムも変形します。
ここまでは上記の記事で書きました。

物理アセットエディタでChild骨にスフィアシェイプを設定します。
![PH_Slime.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/d619854b-2f38-8784-c4c2-0afeca0812ae.jpeg)

今回はAnimNode_SimulateSlime.cppというアニメーションブループリントノードを作りました。
先ほど作った物理アセットを読み取って、スライムの物理シミュレーションをします。
![PABP_Slime.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16365/0b5e9cd4-e247-1083-6ee0-1c4831e15736.jpeg)

ソースコードは以下に置いています。
動作を確認したい方はビルドしてThirdPersonExampleMapをプレーしてみてください。
[SimulateSlimeノードのソースコード]
(https://github.com/monguri/UE4ProceduralSkeletalMeshDemo/blob/main/Source/UE4ProcSkelMeshDemo/AnimNode_SimulateSlime.cpp "SimulateSlimeノードのソースコード")

ここからは、実装した骨物理アルゴリズムを説明します。
毎フレームの計算をおおまかに列挙すると以下のようになります。

1. Root骨との距離を補正する
1. ベルレ積分
1. スフィア同士の距離コンストレイント計算
1. 他のオブジェクトとのコリジョン計算

各々、詳細に見ていきます。

まずは「1. Root骨との距離を補正する」です。
今回はChild骨のみSimulateSlimeノードで制御します。
よって、アクタの位置を動かすと、Root骨は追従します。
そのときChild骨も追従するように、Root骨との距離を半径に保つように位置を補正します。
距離だけの補正であり、RootとChildの間の方向関係は変えていません。
それによって前フレームまでの揺れをある程度は維持しつつ、アクタ移動についていくようになっています。

```cpp
const FVector& RootPosePos = Output.Pose.GetComponentSpaceTransform(RootBoneIndex).GetLocation();
for (FPhysicsAssetSphere& Sphere : Spheres)
{
	const FVector& Diff = Sphere.WorkLocation - RootPosePos;
	Sphere.WorkLocation += -(Diff.Size() - Sphere.Radius) * Diff.GetSafeNormal();
}
```

次に「2. ベルレ積分」です。
これはPBDでよくやる通常のベルレ積分です。
距離コンストレイントやコリジョンコンストレイントによる位置補正を反映しつつ次フレームの位置を決めていくようなシミュレーションをするときによく使います。

```cpp
// ワールド座標での移動の、コンポーネント座標への反映項の計算
const FVector& ComponentMove = CSToWS.InverseTransformPosition(PreCSToWS.GetLocation());
PreCSToWS = CSToWS;

// コンポーネントスペースでの重力*質量のベクトル
const FVector& GravityCS = CSToWS.InverseTransformVector(Gravity);

// ベルレ積分
for (FPhysicsAssetSphere& Sphere : Spheres)
{
	FVector Velocity = (Sphere.WorkLocation - Sphere.WorkPrevLocation) / DeltaTime;
	Velocity *= (1.0f - Damping);
	Sphere.WorkPrevLocation = Sphere.WorkLocation;
	Sphere.WorkLocation += Velocity * DeltaTime;
	Sphere.WorkLocation += ComponentMove * (1.0f - WorldDampingLocation);
	Sphere.WorkLocation += 0.5 * GravityCS * DeltaTime * DeltaTime;
}
```

次に「3. スフィア同士の距離コンストレイント計算」です。
1.でRootとの距離を半径になるように位置補正しましたが、今度はChild間で総当たりでそれを行います。
Rootのときと違って、今回はStiffness値で距離補正力を調整できるようにします。
これがばねのような柔らかい動きを生みます。

```cpp
for (int32 i = 0; i < Spheres.Num(); ++i)
{
	FPhysicsAssetSphere& Sphere = Spheres[i];

	for (int32 j = 0; j < Spheres.Num(); ++j)
	{
		if (i == j)
		{
			continue;
		}

		FPhysicsAssetSphere& AnotherSphere = Spheres[j];
		float TargetRadius = Sphere.Radius + AnotherSphere.Radius;
		const FVector& Diff = Sphere.WorkLocation - AnotherSphere.WorkLocation;

		Sphere.WorkLocation += -(Diff.Size() - TargetRadius) * Diff.GetSafeNormal() * Stiffness;
	}
}
```

単純に総当たりしましたが、それでもスフィアが4個しかないので悪くない挙動になっています。
たくさんスフィアがある場合は、距離が遠いもの同士は計算を省いたり、影響を小さくしたりしないといけません。
また、相互作用するスフィアが多くなると、タイムステップをサブステップに分割して何回かイテレーションしないと、妥当な位置関係に収束しない可能性があります。

最後に、「4. 他のオブジェクトとのコリジョン計算」です。
スフィアを動かさないスイープキャストのコリジョンクエリを行い、検出したコリジョンがあれば、コリジョンしたメッシュの法線方向に押し出すということをやっています。
柔らかさを表現したいので少しめりこむようにPenetrateDepthRadiusRatioというパラメータを設けています。

```cpp
const FVector& PosWS = CSToWS.TransformPosition(Sphere.WorkLocation);
bool bTraceComplex = false;

FHitResult HitResult;
// クエリはスフィアのあるその場所で、直近のHitひとつのみ確認する
bool bHit = World->SweepSingleByChannel(HitResult, PosWS, PosWS, FQuat::Identity, ECollisionChannel::ECC_WorldStatic, FCollisionShape::MakeSphere(Sphere.Radius), Params);
if (bHit)
{
	const FVector& ImpactPosCS = CSToWS.InverseTransformPosition(HitResult.ImpactPoint);
	const FVector& ImpactNormalCS = CSToWS.InverseTransformVector(HitResult.ImpactNormal).GetSafeNormal();

	float OverPenetrateDistance = Sphere.Radius * PenetrateDepthRadiusRatio - ((Sphere.WorkLocation - ImpactPosCS) | ImpactNormalCS);
	if (OverPenetrateDistance > 0.0f)
	{
		Sphere.WorkLocation += OverPenetrateDistance * ImpactNormalCS;
	}
}
```

これで実装の説明は終了です。

こうやって実装したSimulateSlimeノードをスケルタルメッシュコンポーネントに適用したキャラクタークラスを作り、サードパーソンテンプレートのプレイヤーと差し替えれば、冒頭で紹介したような物理挙動を見ることができます。
まだまだブラッシュアップの余地は大きいですが、習作としてはそれなりですかね。

今回は時間の制約でやりませんでしたが、ブラッシュアップするとしたら以下をやりたいです。

- 衝突したときに少し弾ませたい。今はぶよっと落ちてる感じ。距離コンストレイントで弾みはしているが、もっとわかりやすくできるのでは。
- 現在は壁や他キャラに衝突したときに、自由落下と同じ速度で落下している。へばりついてゆっくり落下するようにしたい。へばりついて変形している表現は、スフィアの数を増やして変形自由度を上げないとクオリティは上がらないかも。
- 敵として攻撃するときのモーションをつけたい。ひとつのスフィアだけぐいーんと伸ばすなど。

ここまで読んでわかる方はわかると思いますが、__物理アセットエディタを使わねばならないほど複雑なシェイプ設定はしてないので、物理アセットを使う意味がある具体例になってない__んですよね。
なんか中途半端な例になってすみません。
まあ3日後にはクリスマス、9日後には大晦日ということで、広い心で受け止めてくださると助かります。


#独自の骨物理が必要なケースとは

独自の骨物理を実装せねば表現できないケースはこういうものだ、という共通点があるか考えてみます。

独自のシミュレーションアルゴリズムを実装せねばならないケースとはすなわち、既存物理エンジンだと苦手なケースと言うことです。
UE4に使われているNVIDIA PhysXは、基本的に剛体向けのものです。
柔体（弾性体、粘性体）と流体向けのシミュレーションは、NVIDIA GameWorksのシリーズの中では、FlexやNvClothやFlowなど別のSDKに分離されています。
[NVIDIA GameWorksサイト](https://developer.nvidia.com/what-is-gameworks "NVIDIA GameWorksサイト")

とすると、柔体のシミュレーションが適用例となると言えそうです。
KawaiiPhysicsのようなチェインの揺れものも、その一例です。

後述するBonamIKのようなクロス表現にも向いています。
骨物理でクロスを実装して公開されているものとして以下があります。
[SPCRJointDynamics](https://github.com/SPARK-inc/SPCRJointDynamicsUE4 "SPCRJointDynamics")

今回のスライムは、粘性体のごく簡易な表現でした。
他にも、スパイダーマンのネットみたいな表現にも使えそうですね。
スライムやスパイダーマンネットは、コリジョンクエリを駆使すれば既存物理エンジンでも表現できる気はしますが、まあそれはおいておいて。


#（おまけ）アニメーションブループリントの豆知識

今回、実装に自作アニメーションブループリントを選んだのは、それが作りやすかったのと、僕が作り慣れていたからです。

骨物理は骨を制御できさえすればいいので、必ずしも自作アニメーションブループリントノードで実装する必要はありません。
たとえば自作コンポーネントで実装することもできます。
（コントロールリグは今後の骨制御の本命になっていくと思いますが、4.26時点ではExperimentalなので考慮に入れませんでした。）

アニメーションブループリントノードで実装するメリット・デメリットをあげてみます。

メリット

1. 骨の制御を目的とした機能であるため実装しやすい。実装に必要な機能が充実している。
1. EvaluateSkeletalControl_AnyThread()への実装で、処理が自動的にワーカスレッドで走る。
1. ノードにすることで、アニメーションブループリント内で他のノードと組み合わせて自由にプロシージャルアニメーションを組み立てられる。

デメリット

1. TickGroupがPrePhysicsである。つまり、現在のフレームの物理シミュレーション結果を反映できない。
1. メリット2つ目は、逆にいうと、処理のバッチ化やマルチスレッド化の制御を自分でしたいならアニメーションブループリントは制約になるかもしれないということ。

メリットについては追加で説明することはありません。
デメリットの1つ目は有名です。

たとえばKawaiiPhysicsでキャラクターのしっぽを作っているとして、キャラクターがラグドールで動いているとします。
ラグドールによるコリジョンシェイプの位置をアニメーションブループリントノードの計算に反映させるのが1フレーム遅れるため、めりこんでしまう可能性が出ます。
ポストプロセスアニメーションブループリントであっても、通常のアニメーションブループリントの後に実行されるというだけで、実行されるのはPrePhysicsです。

この点はEpicGamesが質問に対して、「PostPhysicsでもアニメーションブループリントを実行するフェイズを設ける予定はあるが、期日は未定だ」、という趣旨の回答をしているのを何年か前に見たことがあります。
今のところ対応される雰囲気はないので期待しすぎない方がいいでしょう。

この点およびデメリットの2つ目が気になるなら、コンポーネントなり他の方法で実装した方がよいです。
スクエニさんのとあるゲームでは揺れものをコンポーネントで実装されています。
[BonamIKのUE4インテグレーションの解説](https://www2.slideshare.net/EpicGamesJapan/ss-137253035?ref=https://www.unrealengine.com/ "BonamIKのUE4インテグレーションの解説")
ComponentTick.TickGroupをPostPhysicsにすればPostPhysicsで実行させられます。

また、ひとつのシーンで大量の揺れものが動く場合、Entity Component System的な実装を自前で行うことでより最適化できる可能性があります。


# （さらにおまけ）骨物理に対する雑感

上記のスライムやスパイダーマンネットの例で、柔体であれば、骨が入ってなくてもFEMだとかクロスの頂点シミュレーションだとかでいいのではないかと思う人もいるかもしれません。
NVIDIA GameWorksのFlexによる弾性体シミュレーションは、おそらくシェイプマッチングでGPUでシミュレーションする形ですしね。

しかし、頂点アニメーションにしたり頂点シミュレーションにすると、ゲームプレイ中に登場するキャラクターに使うには取り回しが難しくなるし負荷も大きいです。
骨であればプロシージャルアニメーションや物理アニメーションの低負荷なノウハウがたくさんあります。
また、頂点アニメーションよりも骨アニメーションの方がアニメータにとってモーションを量産しやすいというメリットもあります。

僕は、PS4/XBoxOne時代のときは、次世代のアニメーションや物理は、よりGPUを駆使する形に変わっていくものと思っていました。
ですが、いざPS5とXBoxSeriesXの時代が来てみると、レイトレや高解像度モニタ対応によって相変わらずGPUは描画でリソースを使いつくすのが予想される一方、CPUが強力になって余裕がある状態になっています。

余裕のできたCPUをうまく使って、リッチでインタラクティブな表現を生み出すべき時代になったのかなと思います。
骨物理は、多くがCPU駆動で、インタラクティブ性に対応しやすく、負荷を抑えやすいです。
骨は、まだまだゲーム開発においては中心に居続けるのではないかと今は思っています。

まあ別に僕は「アニメーションや物理シミュレーションはCPU」派ってわけでもないんですけどね。
アニメーションやシミュレーションにもGPUをじゃんじゃん使えた方が楽しいと思います。
__GPUリソースを描画以外に割り当てられるんであれば。__


# （またまたおまけ）専用エディタの実装について

物理アセットを転用したのは、あくまで、自分がいろいろテストするための砂場のためにわざわざ専用エディタを実装するのが面倒だったからです。

大人数が関わるプロジェクトなら、専用エディタを実装するなり、KawaiiPhysicsのようにEditModeの実装で対応するといいでしょう。

MayaなどのDCCツール側に設定ツールを作ってDCCツール上で動作確認できるようにし、設定をUE4にインポートできるようにするのもいいと思います。
アーティストがDCCツール内だけで設定と調整を完結できるので、アーティストフレンドリーなワークフローとなると思います。
そのツールがUE4とLiveLink対応できたりするともっとフレンドリーになると思います。


# あとがき

ちょうどいい機会なのでアニメーションと物理に対しての雑感をつめこんだら、おまけが長くなってしまいました。
皆さんが骨物理で（エフェクト物理でもいいですが）面白い表現を作ったらぜひ僕に共有してください。

明日はwaka2yaさんの記事です！楽しみですね！