関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2017.07.07UE4UE/ Animation
UE4.16 で追加された新機能『Animation Modifier』について解説します。
Animation Modifier (以下、Modifier と呼びます) は、Animation Sequence (以下、Sequence) に対して追加・適用することで、Sequence の内容に合わせて自動的に Animation Notify (以下、Notify)、Animation Notify State (以下、Notify State)、Curve、Virtual Bone などの情報を追加・編集・削除することができる機能です。
例えば、「特定の Bone の Z 値が最小となる部分に Notify を追加する」ような Modifier を作成し、歩行 Sequence に適用すれば、足が接地するフレームに Notify を自動追加することができるので、足音の実装が簡単になります。
また、適切に処理を記述すればの話ですが、Modifier の取消・削除で適用前の状態に戻すことができるので、実質的な非破壊編集が可能となります。
なお、この機能は早期アクセスプレビューという位置付けのため、今後のアップデートで仕様が変更される可能性があります。ご注意ください。
Content Browser → Add New → Blueprint Class と選択し、Parent Class として AnimationModifier を指定して作成します。
作成した Blueprint にて、Event OnApply と Event OnRevert の処理を実装します。
OnApply には、Sequence に対して行いたい処理を記述します。先の足音の例では、「特定の Bone の Z 値が最小となる部分に Notify を追加する」が OnApply の処理となります。
OnRevert には、OnApply で行った処理をすべて取り消す処理を記述します。OnApply で追加した Notify は削除する必要がありますし、逆に Notify を削除した場合は復元する必要があります。
Modifier 適用時には、OnApply に先立って OnRevert が呼び出されます。これは、Modifier を何度適用しても処理が一回分となることを保証するためです。そのため、OnRevert は OnApply が一度も呼び出されていない状況でも正常に動作することが求められます。また、取消処理は何度も呼び出される可能性があるため、OnRevert は連続で複数回呼び出しても正常に動作することが求められます。更に、Sequence には同じ種類の Modifier を複数追加・適用することが可能なので、OnRevert は同じクラスの別の Modifier の適用結果に対して意図しない影響を及ぼさないことが求められます。アクセス違反を起こしたり、追加したもの以外のものを削除したりしないように注意して作成する必要がありますが、前述した「実質的な非破壊編集」を実現するために大切な部分となります。
なお、Sequence に対する情報の追加・削除・取得 などの操作は、Animation Blueprint Library として提供されています。
Event の記述が終わったら、Sequence に対して Modifier を追加します。
Sequence を開き、Window → Animation Data Modifiers を選択すると、Modifier 関連の操作を行うことができるパネルが表示されます。このパネルで Modifier の追加 (Add)・適用 (Apply)・取消 (Revert)・削除 (Remove) を行うことができます。
Add Modifier から先ほど作成・編集した Modifier を選択して追加し、右クリック → Apply Modifier で適用します。
Modifier 名に『(Out of Date)』と表示されている場合は、その Modifier が未適用であることを示しています。
Apply All Modifier ボタンでリスト内のすべての Modifier を一括で適用することができますが、このときの適用順はリストの上からとなるようです。Modifier の処理内容によっては適用順で結果が変わることがありますので、先に適用したいものは 右クリック → Move Up でリストの上に持っていきましょう。
なお、Skeleton を開いているときにも Animation Data Modifiers パネルを表示させることができます。このパネルで Modifier を適用すると、Skeleton に紐づくすべての Sequence に対して Modifier を一括適用することができます。
実例として、以下のようなロボットダンスのアニメーションに対して動作音を自動で付加する Sequence を作成したいと思います。
まずは、Modifier で追加する Notify State を作成します。この Notify State は、NotifyBegin で動作音の再生を開始し、NotifyEnd で動作音の停止と停止音の再生を行うというものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#pragma once #include "CoreMinimal.h" #include "Animation/AnimNotifies/AnimNotifyState.h" #include "AnimNotifyState_OperatingNoise.generated.h" UCLASS(EditInlineNew, Blueprintable, const, hidecategories = Object, collapsecategories, meta = (ShowWorldContextPin, DisplayName = "OperatingNoise")) class TEST_ANIMMODIFIER_API UAnimNotifyState_OperatingNoise : public UAnimNotifyState { GENERATED_BODY() public: virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override; virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override; UFUNCTION(BlueprintCallable) void Init(FName BoneName, USoundBase* LoopSound, USoundBase* StopSound); UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AnimNotify") FName BoneName; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AnimNotify") class USoundBase* LoopSound; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AnimNotify") class USoundBase* StopSound; private: class UAudioComponent* AudioComponent_LoopSound; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include “AnimNotifyState_OperatingNoise.h” #include “Runtime/Engine/Classes/Components/AudioComponent.h” #include “Runtime/Engine/Classes/Kismet/GameplayStatics.h” void UAnimNotifyState_OperatingNoise::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) { AudioComponent_LoopSound = UGameplayStatics::SpawnSoundAttached(LoopSound, MeshComp, BoneName, FVector(ForceInit), EAttachLocation::KeepRelativeOffset); Received_NotifyBegin(MeshComp, Animation, TotalDuration); } void UAnimNotifyState_OperatingNoise::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) { if (AudioComponent_LoopSound) { AudioComponent_LoopSound->Stop(); } UGameplayStatics::SpawnSoundAttached(StopSound, MeshComp, BoneName, FVector(ForceInit), EAttachLocation::KeepRelativeOffset); Received_NotifyEnd(MeshComp, Animation); } void UAnimNotifyState_OperatingNoise::Init(FName BoneName, USoundBase* LoopSound, USoundBase* StopSound) { this->BoneName = BoneName; this->LoopSound = LoopSound; this->StopSound = StopSound; } |
この Notify State は Blueprint ではなく C++ で作成しています。Blueprint で override できる Received_NotifyBegin は const 関数になっており、Received_NotifyEnd において動作音の停止処理のために必要となる AudioComponent の参照を、メンバ変数として保持することができないためです。C++ では、内部的に Received_NotifyBegin を呼び出している非 const 関数 NotifyBegin を override することができるので、こちらを使っています。もちろん、このような事情がない限りは Notify State の作成に C++ を使う必要はありません。
Notify State の作成が終わったら、Modifier を新規作成し、Event の処理を記述します。
OnApply では、指定した Bone が回転しているフレーム区間に Notify State を追加する処理を行っています。
最初に AddAnimationNotifyTrack 関数を利用して Notify Track の追加を行っています。この Track に対して Notify State を追加していくことにします。この Modifier が Sequence に複数追加された場合に、同一の Notify Track に対して適用・取消処理が行われないようにするため、Notify Track 名に Modifier の Object Name を含めて区別できるようにしています。
Notify State の追加は AddAnimationNotifyEvent 関数を利用して行っています。この関数は引数として TSubclassOf<UAnimNotifyState> を取って Notify State を追加するものですので、返却値の型は追加した Notify State を指す UAnimNotifyState * であることが望ましいと思うのですが、実際に返却されるのは何故か UAnimNotify * となっています (UAnimNotify と UAnimNotifyState はどちらも UObject を直接継承しており、両者間での継承関係はありません)。UAnimNotifyState* を返してくれるのであれば直後に Notify State のプロパティ変更などを行えるのですが、そうなっていないので、Notify State の追加が一通り終わった後で Notify Track を走査し、Notify State のプロパティ変更関数を呼び出しています。
OnRevert では、Notify State を Notify Track ごと削除しています。本当は追加した Notify State の参照を保存しておいて、それらのみを削除する方が正確だと思われます。
作成した Notify State を Sequence に追加・適用・取消してみた様子が以下の動画です (音が出ます)。
Animation Modifier を利用すると、今まで手動で行っていた作業の一部を自動処理することができ、作業時間の短縮やヒューマンエラーの発生抑制が期待できます。うまく活用していきましょう。