この記事では、UE4.26 バージョンと ActionRPG チュートリアルを例として、アニメーション シーケンスによるモンタージュを自動生成する機能をコードで実装します。
含む:
- アニメーションを通じて対応するモンタージュをバッチで生成します (UE4 には付属していますが、変更する必要があります)
- 複数のアニメーションを通じてモンタージュを作成し、対応するセクションを設定します
1. 機能的背景
1.1 要件とシナリオ
- 多くのアニメーション モンタージュ (AM) は、アニメーション シーケンス (A) を通じて直接生成されます。右クリックして、Content ディレクトリに新しいモンタージュを作成します。変更は必要ありません。理論的には、バッチで生成できるはずです。
- 一部のモンタージュは、セクションの位置と名前が固定された複数の A でつなぎ合わされています(位置はアニメーションに合わせられ、名前は固定されています)。
この場合、プログラムはバッチを直接生成するため、効率が大幅に向上し、品質が向上します (アニメーションが正しい限り、計画の不一致は発生しません) が、計画が一致するには遅すぎます。。
1.2 必要な背景知識
必要なのは、モンタージュ上のセクションとスロット、そしてセグメントが正確に何なのか、そしてそれが何に使用されるのかという非常に基本的なものだけです。
UCLASS(config=Engine, hidecategories=(UObject, Length), MinimalAPI, BlueprintType)
class UAnimMontage : public UAnimCompositeBase
{
GENERATED_UCLASS_BODY()
// composite section.
UPROPERTY()
TArray<FCompositeSection> CompositeSections;
// slot data, each slot contains anim track
UPROPERTY()
TArray<struct FSlotAnimationTrack> SlotAnimTracks;
}
上記は、この記事が対象とするモンタージュ クラスの 2 つのメンバーです。CompositeSections (セクションと呼ばれます) と SlotAnimTracks (スロットと呼ばれ、主に内部のセグメントに関係します) です。
1.2.1 複合セクション
下の図の 3 つの赤いボックスは 3 つのセクションで、CompositeSections
このモンタージュのサイズは 3 です。
セクションはアニメーションを複数のセグメントに分割するために使用されます。ループ、スキップ、その他の操作が可能です。以下は、モンタージュの特定のセクションに直接ジャンプするスキルの機能です。
void UGameplayAbility::MontageJumpToSection(FName SectionName)
{
check(CurrentActorInfo);
UAbilitySystemComponent* const AbilitySystemComponent = GetAbilitySystemComponentFromActorInfo_Checked();
if (AbilitySystemComponent->IsAnimatingAbility(this))
{
AbilitySystemComponent->CurrentMontageJumpToSection(SectionName);
}
}
1.2.2 スロットとセグメント
下の図では、左側の Default.Default がスロットです。つまり、SlotAnimTracks
このモンタージュのサイズは 1 (通常は 1 つのスロットのみ) です。
USTRUCT()
struct FSlotAnimationTrack
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, Category=Slot)
FName SlotName;
UPROPERTY(EditAnywhere, Category=Slot)
FAnimTrack AnimTrack;
};
スロットに 1 つありますAnimTrack
。
USTRUCT()
struct FAnimTrack
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, Category=AnimTrack, EditFixedSize)
TArray<FAnimSegment> AnimSegments;
}
各セグメントには、アニメーション シーケンス、開始時間、終了時間、モンタージュの開始時間などを含むアニメーションがAnimTrack
含まれます。AnimSegments
/** this is anim segment that defines what animation and how **/
USTRUCT()
struct FAnimSegment
{
GENERATED_USTRUCT_BODY()
/** Anim Reference to play - only allow AnimSequence or AnimComposite **/
UPROPERTY(EditAnywhere, Category=AnimSegment)
UAnimSequenceBase* AnimReference;
/** Start Pos within this AnimCompositeBase */
UPROPERTY(VisibleAnywhere, Category=AnimSegment)
float StartPos;
/** Time to start playing AnimSequence at. */
UPROPERTY(EditAnywhere, Category=AnimSegment)
float AnimStartTime;
/** Time to end playing the AnimSequence at. */
UPROPERTY(EditAnywhere, Category=AnimSegment)
float AnimEndTime;
/** Playback speed of this animation. If you'd like to reverse, set -1*/
UPROPERTY(EditAnywhere, Category=AnimSegment)
float AnimPlayRate;
UPROPERTY(EditAnywhere, Category=AnimSegment)
int32 LoopingCount;
}
注意しなければならないことは次のとおりです。
- セクションの位置は必ずしも各アニメーションの先頭に合わせる必要はなく、任意の位置で構いませんが、アニメーションの先頭付近にセクションをドラッグすると吸引が発生し、自動的に移動します。放すと整列します。
- セグメント
StartPos
と はAnimStartTime
混同されやすいです。
StartPos
- このアニメーションはモンタージュ全体のどの時点で始まりますか?AnimStartTime
- このセグメントは、対応するアニメーションから始まります。通常は 0、つまり最初から開始することを意味します。AnimEndTime
- 上記と同様に、このセグメントはいつアニメーションの最後まで再生されますか? 通常はアニメーションの長さ、つまり完全に再生されます。AnimPlayRate
- このセグメントのアニメーション再生速度のデフォルトは 1 です。たとえば、2 の場合、アニメーションは2 倍の速度で再生しても半分の時間しかかかりません。LoopingCount
- このアニメーションのループ数。デフォルトは 1 です。0 の場合、アニメーションは消えますが、永久にループするわけではありません (一貫したループはセクションを通じて実装されます)。
2.効果を実感する
3. 実施方法
3.1 参考方法
3.1.1 右クリックしてモンタージュ効果を生成する
参照方法は、Create -> Create AnimMontage
次の図に示すように、[コンテンツ] でアニメーション シーケンスを右クリックし、機能を選択することです。
この方法で生成されたモンタージュには、 と呼ばれるセクションDefault
と、選択したアニメーションであるセグメントが含まれます。
知らせ:
- この右クリック操作によって生成されるモンタージュの名前は、デフォルトで xx_Montage になります。xx はアニメーション リソースの名前です (この名前のモンタージュがすでに存在する場合は、xx_Montage1 などになります)。元のファイルを上書きする問題。
- この操作はバッチで生成できます。10 個のアニメーションを選択し、右クリックしてモンタージュを生成します。これらの 10 個のアニメーションに対応する 10 個のモンタージュが生成され、10 個のアニメーションが 1 つのモンタージュに結合されることはありません。
3.1.2 右クリックしてモンタージュコードを生成
この種の機能にはボタンとインターフェイスがあり、通常は、対応するヒント テキストを直接検索することで機能を見つけることができますCreates an AnimMontage using the selected anim sequence
。
MenuBuilder.AddMenuEntry(
LOCTEXT("AnimSequence_NewAnimMontage", "Create AnimMontage"),
LOCTEXT("AnimSequence_NewAnimMontageTooltip", "Creates an AnimMontage using the selected anim sequence."),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.AnimMontage"),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetTypeActions_AnimSequence::ExecuteNewAnimMontage, Sequences),
FCanExecuteAction()
)
);
つまりExecuteNewAnimMontage
、関数は内部で呼び出されますFAssetTypeActions_AnimSequence::CreateAnimationAssets
。
/** Creates animation assets of the supplied class */
void CreateAnimationAssets(
const TArray<TWeakObjectPtr<UAnimSequence>>& AnimSequences, /* 所选中的动画序列资源,可以是多个 */
TSubclassOf<UAnimationAsset> AssetClass, /* 要生成的动画资源类型, 即 - UAnimMontage::StaticClass() */
UFactory* AssetFactory, /* 蒙太奇生成工厂 - NewObject<UAnimMontageFactory>() */
const FString& InSuffix, /* 生成的资源名称后缀, 默认 - TEXT("_Montage"); */
FOnConfigureFactory OnConfigureFactory) const;
この方法は、AnimSequence の数に応じて、アニメーション リソースが 1 つだけあり、対応するモンタージュが直接生成される場合と、アニメーション リソースが複数あり、複数のモンタージュが生成される場合の 2 つに分かれます。実際には、アニメーションの一部だけを見る必要があります。
auto AnimSequence = AnimSequences[0].Get();
if ( AnimSequence )
{
// Determine an appropriate name for inline-rename
FString Name;
FString PackageName;
CreateUniqueAssetName(AnimSequence->GetOutermost()->GetName(), InSuffix, PackageName, Name);
if (OnConfigureFactory.IsBound())
{
if (OnConfigureFactory.Execute(AssetFactory, AnimSequence))
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
ContentBrowserModule.Get().CreateNewAsset(Name, FPackageName::GetLongPackagePath(PackageName), AssetClass, AssetFactory);
}
}
}
その中にはCreateUniqueAssetName
、生成されるリソースの名前を決定するためのものがあります。デフォルトは xx_Montage です。競合がある場合、実際には xx_Montage1 が生成されます。CreateNewAsset
上記の目的はOnConfigureFactory
、どのアニメーションであるかをバインドすることです。
3.2 実践的な方法
3.2.1 アニメーションを通じて対応するモンタージュをバッチで生成する
実はこの機能はUEに備わっているので変更する必要はありませんが、名前は自動的に変更されるため、特定の名前を生成して元の名前を上書きしたい場合はエンジンを変更する必要があります。
UObject* UAssetToolsImpl::CreateAsset(
const FString& AssetName,
const FString& PackagePath,
UClass* AssetClass,
UFactory* Factory,
FName CallingContext)
{
const FString PackageName = UPackageTools::SanitizePackageName(PackagePath + TEXT("/") + AssetName);
// Make sure we can create the asset without conflicts
if (!CanCreateAsset(AssetName, PackageName, LOCTEXT("CreateANewObject", "Create a new object")) )
{
return nullptr;
}
// 生成资源 blabla
return NewObj;
}
リソースが最終的に生成されると、最後の関数が呼び出されますが、リソースが生成される前に、リソースが存在するかどうかがチェックされ、存在する場合はメッセージ ボックスが表示されるので、これをクリックする必要があります。
UObject* ExistingObject = StaticFindObject( UObject::StaticClass(), Pkg, *AssetName );
if( ExistingObject != nullptr )
{
// Object already exists in either the specified package or another package. Check to see if the user wants
// to replace the object.
bool bWantReplace =
EAppReturnType::Yes == FMessageDialog::Open(
EAppMsgType::YesNo,
EAppReturnType::No,
FText::Format(
NSLOCTEXT("UnrealEd", "ReplaceExistingObjectInPackage_F", "An object [{0}] of class [{1}] already exists in file [{2}]. Do you want to replace the existing object? If you click 'Yes', the existing object will be deleted. Otherwise, click 'No' and choose a unique name for your new object." ),
FText::FromString(AssetName),
FText::FromString(ExistingObject->GetClass()->GetName()),
FText::FromString(PackageName) )
);
}
メッセージ ボックスをカスタマイズするには、この記事を参照してください。
競合するモンタージュが 20 個ある場合は、20 回クリックする必要があります。クリックしたくない場合は、エンジンを変更し、競合をCreateAsset
考慮するかどうかのパラメーターを追加して、チェックを停止する必要があります。
3.2.2 複数のアニメーションを 1 つのモンタージュに結合する
複数のアニメーションを 1 つのモンタージュに結合するには (1.2.2 に示すように)、まずアニメーションを使用して 3.2.1 の方法に従ってモンタージュを生成し、次に対応する CompositeSections と SlotAnimTracks を調整します。
注: セクションを作成した後、それを設定する必要がありますNextSectionName
。そうしないと、1 つのセクション アニメーションだけが再生されます。セクションの 1 つをループさせたい場合は、次のセクションを独自のセクションに作成するだけです。
CompositeSections[3].NextSectionName = CompositeSections[3].SectionName;
4. 参考資料
- アニメーション モンタージュの概要(公式ドキュメント)
- AnimMontageのご紹介(公式ブログ)