[UE4] Animation Sequenceをアセット複製せずに直接リターゲットする方法について

# はじめに
UE4にはアニメーションのリターゲット機能があり、この機能を使うことで一つのアニメーションを様々なキャラクターで流用・共有できます。そして、アニメーションのリターゲット機能には**<font color="Blue">「同じスケルトンアセットを共有するキャラクター間で行うリターゲット」** と **<font color="Red">「スケルトンアセットが異なるキャラクター間で行うリターゲット」**の2種類があります。まずはこれらに関して軽く説明します。

「細かい話はいいから早く機能を使いたいYO！」という方は https://github.com/pafuhana1213/AnimationPreviewer で公開しているサンプルの EUW_RetargetAnimation をご利用ください

## 同じスケルトンアセットを共有するキャラクター間で行うリターゲット
![RetargetingHeader.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/7a1c90fb-6047-7e3f-f3a7-e54b366645c2.jpeg)
https://docs.unrealengine.com/ja/Engine/Animation/AnimationRetargeting/index.html

異なるSkeletal Meshアセットでもスケルトンアセットが共有している場合はアニメーションを流用・共有することができます。また、AnimationBPも流用・共有することができるため、AnimationBPのAnimGraphやEventGraphで実装したロジックも使い回すことができます。更に、アニメーションアセットの数を抑えることができるため、メモリ使用量も最小限に抑えることが可能です。

## スケルトンアセットが異なるキャラクター間で行うリターゲット
![Retarget1_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/d89c4b7c-88d2-07a8-4590-8bc41c9df44c.jpeg)
https://docs.unrealengine.com/ja/Engine/Animation/RetargetingDifferentSkeletons/index.html
スケルトンアセットが異なるキャラクタ間の場合でも、リターゲット用のリグを設定しておけばアニメーションの流用は可能です。しかし、リターゲットされたアニメーションアセットは完全に別アセットとして保存されます。そのため、例えば10個のモーションを10個のキャラクタ（スケルトンは別々）で流用しようとした場合は合計100個のアニメーションアセットが出来上がります。

完全に別アセットになるため、アニメーションの共有はできません。そのため、AnimationBPで実装したロジックも使い回せません。更に大量のアニメーションアセットをロードすることになるため、メモリ使用量が増加していまいます。マーケットプレイスで購入したアニメーションアセットを自作キャラモデルに流用する際は便利な機能ではあるのですが、キャラクタの数やプロジェクトの規模が増えるにつれてこれらのデメリットが問題になる可能性が大きくなります。

アニメーション・スケルトンの共有に関するより詳細な情報に関しては、上述のドキュメントや以下のスライドをご確認ください。
https://www.slideshare.net/EpicGamesJapan/cedec2018-ue4-111104578/103
https://www.slideshare.net/EpicGamesJapan/ue4animation-blueprint/14
https://www.slideshare.net/EpicGamesJapan/ue4-bp18/122

という前提を踏まえて、ようやく本題へ。

# Animation Sequenceをアセット複製せずに直接リターゲットする方法について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/221d50a1-23d4-42bc-2505-321e04cf4cbd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/0d594dd1-219e-000c-f3fc-529b894f3223.png)
マーケットプレイスで購入したアニメーションアセットを自作キャラモデルで使用する場合、最も有名な方法は上図の方法かと思います。しかし、この方法を使った場合に走る処理は**<font color="Red">「スケルトンアセットが異なるキャラクター間で行うリターゲット」**です。スケルトンの構成が完全に一致している場合でも**<font color="Red">「スケルトンアセットが異なるキャラクター間で行うリターゲット」**が走り、完全に別アセットとして保存されます。

**<font color="Blue">「同じスケルトンアセットを共有するキャラクター間で行うリターゲット」** を行うためには、対象のアニメーションアセットに紐付いているスケルトンを変更する必要があります。つまり、購入したアニメーションアセットを自作キャラモデルのスケルトンアセットに紐付ければOKです。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/b29827fd-7ccc-69ac-3f82-77b804e026d4.png)
この紐付けの変更が簡単にできればいいのですが…ひと手間かかってしまう状況です。具体的には以下の手順が必要です。
1. 対象のアニメーションアセットをFBX出力
2. その出力したFBXをContent Browserにドラッグアンドドロップ
3. 表示されたインポートオプションのSkeletonに自作キャラモデルのスケルトンアセットを設定

**面倒ですね！**
ということで、Animation Sequenceに紐付ているスケルトンアセットを別のものに差し替える仕組みを Editor Utility Widgetで作成してみました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">オレオレアニメーションリターゲットUIを作ってみた！アセットを複製しないリターゲットを可能に（正しくはスケルトン変更してます）<br>I publish a little more useful anim retargeting UI. It doesn&#39;t duplicate the asset. it only changes the skeleton :)<a href="https://t.co/L7Cw0f86GX">https://t.co/L7Cw0f86GX</a> <a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://t.co/n37x3kChSB">pic.twitter.com/n37x3kChSB</a></p>&mdash; おかず (@pafuhana1213) <a href="https://twitter.com/pafuhana1213/status/1201533423942111233?ref_src=twsrc%5Etfw">December 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

具体的な実装方法に関しては https://github.com/pafuhana1213/AnimationPreviewer で公開しているサンプルの「EUW_RetargetAnimation」と 「AnimationPreviewerUtilEd.h/.cpp」を見て頂くとして、この機能を実装する上で最も重要なリターゲット処理の呼び出し部分について解説します。といっても、C++コードを一行呼び出すだけの簡単な内容です。

## アニメーションのリターゲット処理を実行するC++関数について
```cpp:EditorAnimUtils.h
UNREALED_API UObject* RetargetAnimations(USkeleton* OldSkeleton, USkeleton* NewSkeleton, TArray<TWeakObjectPtr<UObject>> AssetsToRetarget, bool bRetargetReferredAssets, const FNameDuplicationRule* NameRule, bool bConvertSpace);
UNREALED_API UObject* RetargetAnimations(USkeleton* OldSkeleton, USkeleton* NewSkeleton, const TArray<FAssetData>& AssetsToRetarget, bool bRetargetReferredAssets, const FNameDuplicationRule* NameRule, bool bConvertSpace);
UNREALED_API UObject* RetargetAnimations(USkeleton* OldSkeleton, USkeleton* NewSkeleton, FAnimationRetargetContext& RetargetContext, bool bRetargetReferredAssets, const FNameDuplicationRule* NameRule);
```
```cpp:EditorAnimUtils.cpp
UObject* RetargetAnimations(USkeleton* OldSkeleton, USkeleton* NewSkeleton, FAnimationRetargetContext& RetargetContext, bool bRetargetReferredAssets, const FNameDuplicationRule* NameRule)
{
	check(NewSkeleton);
	UObject* OriginalObject  = RetargetContext.GetSingleTargetObject();
	UPackage* DuplicationDestPackage = NewSkeleton->GetOutermost();

	if(	RetargetContext.HasAssetsToRetarget() )
	{
		if(NameRule)
		{
			RetargetContext.DuplicateAssetsToRetarget(DuplicationDestPackage, NameRule);
		}
		RetargetContext.RetargetAnimations(OldSkeleton, NewSkeleton);
	}
...
```

```EditorAnimUtils::RetargetAnimations```関数を呼び出すことで、アニメーションのリターゲット処理を実行することが可能です。ここで特に重要なのは引数の一つである ```const FNameDuplicationRule* NameRule```です。この```NameRule```が```nullptr```では **ない** 場合、**<font color="Red">「スケルトンアセットが異なるキャラクター間で行うリターゲット」**によるアセットの複製処理が走ります。そして、```nullptr```をした場合は**<font color="Blue">「同じスケルトンアセットを共有するキャラクター間で行うリターゲット」** が走ります。つまり、アセットの複製処理が走ることなくAnimation Sequenceに紐づくスケルトンの変更処理が行われます。

```cpp:AnimationPreviewerUtilEd.cpp
void UAnimationPreviewerUtilEd::RetargetAnimations(USkeleton* NewSkeleton, const TArray< UAnimSequence*> AssetsToRetarget, bool bDuplicateAsset, bool bRemapReferencedAssets, bool bAllowRemapToExisting, bool bConvertSpaces)
{
	if (!NewSkeleton || AssetsToRetarget.Num() == 0)
	{
		return ;
	}

	TArray<TWeakObjectPtr<UObject>> ObjectToRetarget;
	for (UAnimSequence* AnimSequence : AssetsToRetarget)
	{
		ObjectToRetarget.Empty();
		ObjectToRetarget.Add(AnimSequence);

		if (bDuplicateAsset)
		{
			EditorAnimUtils::FNameDuplicationRule NameDuplicateRule;
			EditorAnimUtils::RetargetAnimations(AnimSequence->GetSkeleton(), NewSkeleton, ObjectToRetarget, bRemapReferencedAssets, &NameDuplicateRule, bConvertSpaces);
		}
		else 
		{
			EditorAnimUtils::RetargetAnimations(AnimSequence->GetSkeleton(), NewSkeleton, ObjectToRetarget, bRemapReferencedAssets, nullptr, bConvertSpaces);
		}
	}
};
```
そのため、公開しているサンプルでは上記のように実装しています（…あまりテストせずに勢いで書いたコードなのでご注意くださいまし）。

このようにやってることは単純なのですが、僕のようなマーケットプレイスガチ勢にとっては結構便利なエディタ拡張かと思います。是非是非お試しあれ。

# おまけ：Skeletal Meshのスケルトン変更について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/2961accf-81bb-92ed-a2cc-179277144200.png)
Animation SequenceではなくSkeletal Meshに対してのスケルトン変更処理は右クリックメニューの「Assign Skeleton」から実行可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/8e73a7de-5a8a-c661-14e7-e017e3592e01.png)
変更対象のスケルトンと現在のスケルトンアセットにおける各ボーン名の比較を表示してくれますし…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/68524705-a3cf-f877-2740-883e71e8d4ba.png)
ボーン構成が一部異なる場合はスケルトンのマージ処理を行ってくれます。いくつか注意点はありますので、以下の資料を事前に確認することをオススメします！
https://www.slideshare.net/EpicGamesJapan/cedec2018-ue4-111104578/103


おしまい