自作アニメーションノードを作ってみようということで、シンプルなところから始めてみました。
アニメーションノードの構成
アニメーションノードは、以下の2つで構成されています。
- Anim Behavior ノード
アニメーションノードの実処理(更新、ポーズ反映など)が定義されたノードです。
* Anim Graph ノード
UE4Editor の「**アニムグラフ**」で配置するノードの情報(ノード名やノードの色など)が定義されたノードです。
これらのノードを定義するだけでアニメーションノードが使用可能になります。
受け取ったポーズを返すだけのアニメーションノードを作ってみる
アニメーションノードの参考となるソースは数多くあります。
しかし、「結局、何が正しいのか?」と言うのはこれらのソースを見ただけでは判断が付きづらいです……。
そこで、まずは一番シンプルな実装になるであろう「受け取ったポーズを返すだけのアニメーションノード」を作ってみます。
Anim Behavior ノードを用意する
まずは、Anim Behavior ノードを用意します。
Anim Behavior ノードは構造体 FAnimNode_Base を継承して定義します。
また、継承した構造体はモジュールのタイプが「Runtime」のモジュールに定義します。
(モジュールについての説明は、ヒストリアさんのこちらの記事が参考になります。)
規模は大きくないので、ソースコードをそのまま載せます。
#pragma once
#include "Animation/AnimNodeBase.h"
#include "AnimNode_MySimple.generated.h"
USTRUCT()
struct MYSANDBOX_API FAnimNode_MySimple : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Links)
FPoseLink SourcePose;
public:
FAnimNode_MySimple();
virtual void Initialize(const FAnimationInitializeContext& Context) override;
virtual void CacheBones(const FAnimationCacheBonesContext& Context) override;
virtual void Update(const FAnimationUpdateContext& Context) override;
virtual void Evaluate(FPoseContext& Output) override;
};
#include "MySandbox.h"
#include "AnimNode_MySimple.h"
// ----------------------------------------------------------------------------
FAnimNode_MySimple::FAnimNode_MySimple()
{
}
// ----------------------------------------------------------------------------
void FAnimNode_MySimple::Initialize(const FAnimationInitializeContext& Context)
{
FAnimNode_Base::Initialize(Context);
SourcePose.Initialize(Context);
}
// ----------------------------------------------------------------------------
void FAnimNode_MySimple::CacheBones(const FAnimationCacheBonesContext& Context)
{
SourcePose.CacheBones(Context);
}
// ----------------------------------------------------------------------------
void FAnimNode_MySimple::Update(const FAnimationUpdateContext& Context)
{
SourcePose.Update(Context);
}
// ----------------------------------------------------------------------------
void FAnimNode_MySimple::Evaluate(FPoseContext& Output)
{
SourcePose.Evaluate(Output);
}
後で別のモジュールで定義する Anim Graph ノードがこの構造体を内包するため、**モジュール API 指定子(このソースでいうところの MYSANDBOX_API)**が必要です。
(モジュール API 指定子については、こちらのページの「Module API」の項目に載っています。)
以下、各メンバの簡単な説明です。
メンバ関数に関しては、ノード自身が処理を必要としない場合でも「メンバに入力ポーズがある場合」や「別のアニメーションノードを内包している場合」は、それらの各種関数を必ず呼ぶ必要があります。
(今回の場合、入力ポーズである SourcePose があるため、各関数内で SourcePose の関数を呼んでいます。)
もし呼ばなかった場合、入力ポーズの先にリンクされているノードや内包されているノードでそれらの処理が実行されなくなり、正しく処理されない可能性があります。
Anim Graph ノードを用意する
次に、Anim Graph ノードを用意します。
Anim Graph ノードはクラス UAnimGraphNode_Base を継承して定義します。
また、継承したクラスはモジュールのタイプが「Developer」のモジュールに定義します。
(モジュールのタイプが「Editor」のモジュールに定義した場合は、「Play > Standalone Game」でアニメーションが流れなくなります。)
こちらも規模が大きくないので、ソースコードをそのまま載せます。
#pragma once
#include "AnimGraphNode_Base.h"
#include "Animation/AnimNode/AnimNode_MySimple.h"
#include "AnimGraphNode_MySimple.generated.h"
UCLASS()
class UAnimGraphNode_MySimple : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MySimple Node;
public:
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
};
#include "MySandboxDeveloper.h"
#include "AnimGraphNode_MySimple.h"
#define LOCTEXT_NAMESPACE "MySandboxDeveloper"
// ----------------------------------------------------------------------------
UAnimGraphNode_MySimple::UAnimGraphNode_MySimple(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
// ----------------------------------------------------------------------------
FText UAnimGraphNode_MySimple::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("AnimGraphNode_MySimple_Title", "My Simple");
}
#undef LOCTEXT_NAMESPACE
前述の通り、Anim Graph ノードが Anim Behavior ノードを内包する形になっています。
また、今回は「ノードのタイトル名」のみを変えています。
このクラスを定義する際、以下のモジュールを「~.Build.cs」の「PublicDependencyModuleNames」に追加する必要があります。
- AnimGraph
- BlueprintGraph
UE4Editor で確認してみる
これで必要となるクラスを2つとも定義できましたので、UE4Editor で配置して確認してみます。
問題なく動作しているようであれば、以下のようにアニメーションが再生されているはずです。
ちなみに、ポーズのリンクに失敗した場合(入力ポーズの変数名を Pose にした場合)はこのように接続されていないときと同じ状態になります。