【UE5】お手軽ControlRigで表情を制御する

[Unreal Engine (UE) Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ue) 9日目の記事です。

# はじめに

お手軽にControlRigを作って、シーケンサーから表情(MorphTarget)を制御する方法を紹介します。

表情の制御自体はBlueprintやAnimSequenceでも可能です。ただ調整やプレビューにやや難がありますので、状況に応じて使い分けください。

# ControlRigでMorphTargetを制御しよう

シーケンサーからこんな感じに設定できます。

| ControlRigで表情を設定している様子 |
|:-:|
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/8e859997-1be3-e218-0df1-5d67c85b1f09.png) |
|[AvatarSample_Aさん](https://hub.vroid.com/characters/2843975675147313744/models/5644550979324015604)|

UE5.5で動作確認しています。
それ以前のバージョンではControlRigの挙動があやしいのでご注意ください。

# 表情のControlRigを作る（手動、モデル固有バージョン）

### 手順（1/2）
下図のようなControlRigを作成します。あちこち警告が出るかもしれませんが、以降の手順で解消します。

ざっくり解説
- ConstructionEventにて、MorphTargetに対応するControlを生成します
- ForwardsSolveにて、Controlの値をMorphTargetに反映させます


|  |
|:-:|
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/832b8372-70ea-9003-58c2-61d129af1b4f.png)|

なおSpawnFloatControl ノードを使うには、 SpawnControlノードのInitialvalueにfloatを接続すればOKです。

|  |
|:-:|
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/271aa958-ddb8-4e44-89e3-cd6e7a198992.png)|



### 手順（2/2）
MorphTarget名の配列を設定します。SkeletalMeshからMorphTargetを全て選択、コピーして、

| MorphTarget名をコピー |
|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/eeb577ee-4de8-3dce-3360-28002918429b.png)|

テキストエディタに貼り付けて編集します。
 ↓ 貼り付けるとMorphTarget名が行ごとに並んでいます。これを、
```
vrc_v_sil
vrc_v_pp
vrc_v_aa
 :
mouthStretchLeft
mouthStretchRight
tongueOut
```
 ↓ カンマで区切り、括弧で括るよう書き換えます。最終行のカンマは無いので注意ください。
```
(
vrc_v_sil,
vrc_v_pp,
vrc_v_aa,
 :
mouthStretchLeft,
mouthStretchRight,
tongueOut
)
```

このテキストをコピーし、配列にペーストします。格納されます。

|配列にペースト|格納された様子|
|:-:|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/62666fc5-9636-604d-8fcb-13f1f6e4501d.png)| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/1f87976b-1ac3-0675-1ac0-df4076b38695.png) |

シーケンサーからControlRigを割り当てれば完了です。

| ControlRigを割り当てる | 全てのMorphTargetを制御できる |
|:-:|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/47e1197f-37b9-d793-41bf-0990eae926f5.png)| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/38cdfeda-dda7-9cba-aeef-7ee186c8e779.png) |
||[(New NecoMaidさん)](https://booth.pm/ja/items/4252664)|

ただこのRigは別のモデルに利用できず、個別にMorphTargetのリストをコピペしなおす必要があります。
（正確には「MorphTarget名が異なるモデルでは利用できない」です。同名なら流用できます）

# 表情のControlRigを作る（C++による自動、汎用バージョン）

MorphTarget取得を自動化し、どのモデルにも利用できる汎用ControlRigを作成します。

C++でノードを作ります。
色々省略していますが、ノード部分のソースはこんな感じ

```c++
#include "Units/RigUnit.h"
#include "ControlRigDefines.h" 

#include "RigUnit_VrmDynamicHierarchy.generated.h"

/**
 * get curve list
 */
USTRUCT(meta = (DisplayName = "VRM4U GetCurveNameFromMesh", Keywords = "VRM4U", Varying))
struct FRigUnit_VRMGetCurveNameFromMesh : public FRigUnitMutable
{
	GENERATED_BODY()

	FRigUnit_VRMGetCurveNameFromMesh() {}

	RIGVM_METHOD()
	virtual void Execute() override;

	UPROPERTY(meta = (Output))
	TArray<FName> Items_Morph;

	UPROPERTY(meta = (Output))
	TArray<FName> Items_Curve;
};

```

```c++
FRigUnit_VRMGetCurveNameFromMesh_Execute() {
	Items_Curve.Reset();
	Items_Morph.Reset();

	const USkeletalMeshComponent* skc = ExecuteContext.UnitContext.DataSourceRegistry->RequestSource<USkeletalMeshComponent>(UControlRig::OwnerComponent);
	if (skc == nullptr) return;

    auto* sk = skc->GetSkinnedAsset();
    auto morph = sk->GetMorphTargets();
    for (auto& a : morph) {
        Items_Morph.Add(a.GetFName());
	}
}

```

このノードでMorphTargetのリストを取得できます。
先ほどのControlRig内の配列を置き換えましょう。

| 配列をノードに置き換えたControlRig |
|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/4d9174d4-bf2e-39cf-dffb-2f3335de1575.png) |

完成です。

さっそくモデルに割り当ててみます。

| 自動版Rigを割り当てた様子1 | 自動版Rigを割り当てた様子2 |
|:-:|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/e6fce1cd-3a14-aa90-80e4-b6bb39cfa2d0.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36233/ebe5fd4a-8a25-7bfd-5602-93a6968ae0c7.png)  |
|[シアノスさん](https://booth.pm/ja/items/3224415)| [桔梗さん](https://booth.pm/ja/items/3681787) |

どんなモデルでも、いい感じにMorphTargetを制御できるようになりました。
めでたしめでたし。

# おわりに

ここで紹介した汎用ControlRigは私が作成しているプラグイン[VRM4U](https://ruyo.github.io/VRM4U/)のパッケージに入っています。
パラメータを手軽に設定するためのWidgetなども入っているので 興味があればご利用ください。

ちなみに、CurveパラメータとPoseAssetを利用すると、1つのパラメータで複数のMorphTargetを同時に操作できます。
VRM4Uはこの仕組みを利用してVRMの表情セット(例えばsmileとかblinkとか)を操作しています。