UE4 AnimationModifierについてのメモ

# 概要
UnrealEngine のアニメーションモディファイア(AnimationModifier)についてのメモです。


## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.24

## 参考
以下を参考にさせて頂きました、ありがとうございます。

[[UE4] UE 4.16 の新機能！Animation Modifier について](https://historia.co.jp/archives/6762/)
[UE4 アニメーションモディファイア(Animation Modifier)で自動的にアニメーション情報を追加する](http://unrealengine.hatenablog.com/entry/2017/06/19/231548)


# AnimationModifierとは
独立したアセットとして、紐づけたアニメ―ションシーケンスアセットに対してCurve情報やNotify情報を追加できる機能です。（アニメ―ションモンタージュには不可です。）
スケルトンに対して紐づけるとそのスケルトンに紐づいたアニメ―ションシーケンス全てに対して情報が付与されます。
4.16で先行追加された機能のようですが、4.24、<font color=red>4.25でもまだ実験的機能</font>扱いになっています。


![Warning.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/5486cbe5-699b-ebe4-1d77-1a267e7fd54b.jpeg)


# 前準備 - テスト通知を追加
テスト用のアニメ―ション通知を作成します、イベントタグを通知するものとして以下の様に定義しています。通知クラスはC++である必要はありません。

```cpp:AnimNotity_TestEvent.h
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "GameplayTagContainer.h"
#include "AnimNotify_TestEvent.generated.h"

class UAnimSequenceBase;
class USkeletalMeshComponent;
struct FGameplayTag;

UCLASS(const, hidecategories=Object, collapsecategories, meta=(DisplayName="TestEvent"))
class TEST_API UAnimNotify_TestEvent : public UAnimNotify
{
	GENERATED_BODY()

public:

	UAnimNotify_TestEvent();

	// Begin UAnimNotify interface
	virtual FString GetNotifyName_Implementation() const override;
	virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
#if WITH_EDITOR
	virtual void ValidateAssociatedAssets() override;
#endif
	// End UAnimNotify interface

	// イベントタグ
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimNotify_TestEvent")
	FGameplayTag	EventTag;

};

```

```cpp:AnimNotity_TestEvent.cpp
#include "./AnimNotify_TestEvent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Animation/AnimSequenceBase.h"
#include "Abilities/GameplayAbilityTypes.h"
#include "AbilitySystemBlueprintLibrary.h"

#if WITH_EDITOR
#include "Logging/MessageLog.h"
#include "Misc/UObjectToken.h"
#endif

UAnimNotify_TestEvent::UAnimNotify_TestEvent()
	: Super()
{
}

void UAnimNotify_TestEvent::Notify(
	class USkeletalMeshComponent*	MeshComp,		// スケルタルメッシュコンポーネント
	class UAnimSequenceBase*		Animation		// アニメ―ション
	)
{
	AActor* _Actor = MeshComp->GetOwner();
	if (_Actor) {

		if (!EventTag.IsValid()){
			EventTag = FGameplayTag::RequestGameplayTag(FName("Event.Test"));
		}

		// イベント発行
		FGameplayEventData _Payload;
		_Payload.Target = _Actor;
		UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(_Actor, EventTag, _Payload);
	}
	
}

// 通知名
FString UAnimNotify_TestEvent::GetNotifyName_Implementation() const
{
	return( FString(TEXT("TestEvent")) );
}

#if WITH_EDITOR
void UAnimNotify_TestEvent::ValidateAssociatedAssets()
{
	static const FName NAME_AssetCheck("AssetCheck");

}
#endif

```

ゲームプレイタグを使用しています、当然タグがないとコールバックエラーがでます。


# AnimationModifierの使用(BP)
ブループリントで作成するケースです。

## BPを作成
AnimationModifierを継承してBPを作成します。

![MakeBP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/bf2fba19-8a27-d289-763d-4e1ef81e4e76.jpeg)

作成したBPに対し、適用イベント`OnApply`と元に戻すイベント`OnRevert`を以下の様に書きます。
テストコードではアニメ―ショントラック追加と通知追加をしてみます。

![AnimModifierBP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/61f94e9d-d767-e234-cd9f-bad164f0b46b.jpeg)

`OnApply`では `Add Animation Notify Track`でアニメ―ショントラック[EventTr]を追加してそのトラックに対し、`Add Animation Notify Event`で通知[TestEvent] を追加しています。
`OnRevert`では`Remove Animation Notify Events by Name`にて[TestEvent]を削除して、`Remove Animation Notify Track`でアニメ―ショントラック[EventTr]を消しています。（この場合、アニメ―ショントラックだけ消せば通知も消えます）

また、`OnRevert`は`OnApply`で追加した内容を全部消す形で実装しないと、複数回適用した場合、データが意図しない形になるので注意が必要です。


## アニメ―ションシーケンスに適用する
アニメ―ションシーケンスの[アニメ―ションデータモディファイアブループリント管理のタブ]から先ほど作成したBPを選択し追加します。

![ModifierSetting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/71281e63-c208-71b5-6ef0-557bdca61598.jpeg)

[すべてのモディファイアを適用]ボタンを押下するとBPの`OnApply`での内容で適用されます。

右クリックで「モディファイアを適用」「モディファイアを取り消す」「モディファイアを削除」が選択できます。どれを選んでも `OnRevert`イベントが一回発生するようです。

![ModifierSettingMenu.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/75bc84a3-4c1d-e491-5f27-a702ea6c3bd4.jpeg)


## 適用結果

適用する前のアニメ―ションシーケンスがこの状態です。
![AS_Notify1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/69caccf4-6f7c-cee7-a4dc-12b511cda94c.jpeg)

「モディファイアを適用」をした後がこの状態になります。

![AS_Notify2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/9725f819-db7b-5820-38f6-63fdfbeaf570.jpeg)

[EventTr]トラックが追加され、そこに通知[TestEvent]が追加されています。

「モディファイアを取り消す」を選択すると最初の状態に戻りますが、`OnRevert`処理で、`OnApply`で追加した内容を全部消さないと以下のようなおかしな状態になってしまいますので注意が必要です。

![AS_Notify3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/c0affa8b-6f16-a8ba-759d-e1af6a58a368.jpeg)


# AnimationModifierの使用(C++)
C++で作成するケースです。エディタ用モジュールで作成しないとパッケージ時にエラーがでてしまいますので注意が必要です。

## uprojectの設定
`.uproject` の `AdditionalDependencies` に `AnimationModifiers` を追加する必要があります。

```json:.uproject
"Modules": [
	{
		"Name": "MyGame",
		"Type": "Runtime",
		"LoadingPhase": "Default",
		"AdditionalDependencies": [
			"Engine",
			"UMG",
			"CoreUObject"
		]
	},
	{
		"Name": "MyGameEd",
		"Type": "Editor",
		"LoadingPhase": "PostEngineInit",
		"AdditionalDependencies": [
			"AnimationModifiers"
		]
	}
	],
```


## C++クラスを作成
関連ソース
"Engine\Source\Editor\AnimationModifiers\Public\AnimationModifier.h"

AnimationModifierクラスを継承してC++クラスを追加します。

![CreateClass.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/7f003ead-e413-78b6-9540-3029cadc9bee.jpeg)

追加したクラスに対し、`OnApply_Implementation`と`OnRevert_Implementation`をオーバーライドします。
以下BPと処理的には全く同じになるサンプルコードです。

```cpp:MyAnimationModifier.h
#include "CoreMinimal.h"
#include "AnimationModifier.h"
#include "MyAnimationModifier.generated.h"

UCLASS()
class TESTEDITOR_API UMyAnimationModifier : public UAnimationModifier
{
	GENERATED_BODY()
	
public:
	// 通知
	virtual void OnApply_Implementation(UAnimSequence* AnimationSequence) override;

	// 戻す
	virtual void OnRevert_Implementation(UAnimSequence* AnimationSequence) override;

};

```

```cpp:MyAnimationModifier.cpp
#include "MyAnimationModifier.h"
#include "Animation/AnimSequence.h"
#include "AnimationModifiers/Public/AnimationBlueprintLibrary.h"
#include "{通知クラスのパス}/AnimNotify_TestEvent.h"


void UMyAnimationModifier::OnApply_Implementation(UAnimSequence* AnimationSequence)
{
	// アニメ―ショントラックの追加
	UAnimationBlueprintLibrary::AddAnimationNotifyTrack(AnimationSequence, TEXT("EventTr"), FLinearColor(0.8f, 0.2f, 0.2f));
	// 通知の追加
	UAnimationBlueprintLibrary::AddAnimationNotifyEvent(AnimationSequence, TEXT("EventTr"), 0.1f, UAnimNotify_TestEvent::StaticClass());
}

// 
void UMyAnimationModifier::OnRevert_Implementation(UAnimSequence* AnimationSequence)
{
	// 通知の削除
	UAnimationBlueprintLibrary::RemoveAnimationNotifyEventsByName(AnimationSequence, TEXT("TestEvent"));
	// アニメ―ショントラックの削除
	UAnimationBlueprintLibrary::RemoveAnimationNotifyTrack(AnimationSequence, TEXT("EventTr"));
}
```


## アニメ―ションシーケンスに適用する
BPの時と同様に追加にアニメ―ションシーケンスから「モディファイアを追加」で選択ができるようになります。

![AddModifierCPP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/23c3d223-2638-c32d-ee5c-cd5d6dff7027.jpeg)


## 適用結果
結果はBPと同様になります。


# 補足
BPで使えるノードは `Animation Blueprint Library` にあります。
![BPNode.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/fa51f65a-91e5-c1f2-89d7-cd92a1d4337a.jpeg)

C++コードは以下にあります。
"Engine\Source\Editor\AnimationModifiers\Public\AnimationBlueprintLibrary.h"



# まとめ
上手く使うと手動で行っていた作業が一部楽になる可能性があるかと思います。特にアクターの違いでアニメ―ションシーケンスアセットは別になっているけれども同じタイミングでの通知を入れないとならないようなケースなどが考えられます。
未だ実験的機能扱いなのが気になるところですが。。:confused: