Help us understand the problem. What is going on with this article?

ボーンを制御するカスタムアニメノードグラフを作る

More than 3 years have passed since last update.

この記事はUnreal Engine 4 (UE4) Advent Calendar 2016の12日目の記事になります。

アニメーションブループリントで使う、ボーンを制御するカスタムアニメノードグラフを作る手順についてまとめたいと思います。
情報は公式サイトとかぶっている部分があるのですが、一連の手順と注意点を含めてまとめたいと思います。
以下公式の情報
UnrealEngineのサイト
https://www.unrealengine.com/ja/blog/creating-custom-animation-nodes
ドキュメント「アニメーションノードのテクニカルガイド」
https://docs.unrealengine.com/latest/JPN/Programming/Animation/AnimNodes/index.html

この記事の前提

  • バージョンはUE4.14
  • 独自のアニメノードグラフを作るのが目的で便利さは二の次です。

アニムノードグラフを自作する前に

通常通りにUE4のプロジェクトを作るとゲームモジュールのみ作られ、エディタモジュールが作られませんので独自に作る必要があります。
作らなくても一応は動くのですが、パッケージを作った際にエラーが起きるので作ったほうがいいと思います。
AnimGraphノードはエディタ時のみに必要なだけでパッケージしたゲーム起動時には不要な機能になるからです。

モジュールの追加方法

今回は例としてMyProjectのエディターモジュールとしてMyProjectEditorモジュールを追加します。
1.フォルダ、ファイルの追加
最低限必要なのはモジュールルールとモジュールクラスのソースを追加してください。

MyProjectEditor.h
    #pragma once

    #include "ModuleManager.h"

    class FMyProjectEditorModule : public IModuleInterface
    {
    public:
        /** IModuleInterface implementation */
        virtual void StartupModule() override;
        virtual void ShutdownModule() override;
    };
MyProjectEditor.cpp
    #include "AdvC2016Editor.h"

    // 特にモジュールロード時に特にすることがないので空
    void FMyProjectEditorModule::StartupModule()
    {
    }

    void FMyProjectEditorModule::ShutdownModule()
    {
    }

    IMPLEMENT_MODULE(FMyProjectEditorModule, MyProjectEditor);

モジュールルールはMyProjectのMyProject.Build.csをコピーしMyProjectEditor.Build.csファイルを作り、MyProjectをMyProjectEditorに置換すればOKです。

2.モジュールルール C#ソースコードの編集
AnimGraphの宣言をもとに必要なモジュールをMyProjectEditor.Build.cs追加します。

MyProjectEditor.Build.cs
        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                "MyProject",
                "Core", 
                "CoreUObject", 
                "Engine", 
                "BlueprintGraph",
                "AnimGraph",
            }
        );

        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "UnrealEd",
                "GraphEditor",
                "PropertyEditor",
                "EditorStyle",
            }
        );

念のためモジュールの循環参照を許可するモジュールも追加します。

MyProjectEditor.Build.cs
        CircularlyReferencedDependentModules.AddRange(
            new string[]
            {
                "UnrealEd",
                "GraphEditor",
            }
        );

3.uprojectファイル(MyProject.uproject)にエディタモジュールの情報を追加

"Modules": [
{
        "Name": "MyProject",
        "Type": "Runtime",
        "LoadingPhase": "Default"
    },
    {
        "Name": "MyProjectEditor",
        "Type": "Editor",
        "LoadingPhase": "PreDefault"
    }
]

※エディタモジュールのLoadingPhaseはPreDefaultにしないと起動に失敗します。

アニメノードグラフに必要なもの

1つのアニメノードグラフを作るためにはAnim BehaviorとAnim Graph Nodeの2つを用意する必要があります。

Anim Behavior 構造体(AnimNode)

アニメーションの処理
アニメーションノード

struct FAnimNode_Base

スケルタルコントロール

struct FAnimNode_SkeletalControlBase

Anim Behaviorは構造体ですので「新規C++クラス」で親クラスに設定できないため、エンジンのソースコードを元に作る必要があります。

Anim Graph Node クラス

エディター上で操作するノード

主なAnim Graph Node

アニメーションノードグラフ基本クラス

UAnimGraphNode_Base

スケルタルコントロールグラフ基本クラス

UAnimGraphNode_SkeletalControlBase

ステートMachine

UAnimGraphNode_StateMachineBase

予めエディタモジュールを作成しておくと「新規C++クラス」で追加することができます。

機能実装

Anim Graph Node

AnimBehaviorノードを持つ必ず持つ必要があります。

FAnimNode BasePose;

UIの見た目を変更した場合は以下の処理をオーバーライドして変更しましょう
ノードのタイトルのテキスト

virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;

ToolTipのテキスト

virtual FText GetTooltipText() const override;

タイトルの色

virtual FLinearColor GetNodeTitleColor() const override;

AnimBehavior

ボーンの制御、アニメーションのプロパティや処理はこちらで定義します。

Anim Behavior 固有のプロパティ

ポーズノードピン

各アニメーションノードをつなげるピンを追加します。
ポーズ入力ピン

FPoseLink BasePose;

コンポーネントスペースポーズピン

FComponentSpacePoseLink ComponentPose;

ボーンリファレンス

ボーンの参照を行う構造体

FBoneReference

エディタ上でボーンを簡単に設定できます。
advc2016_boneref.png

ノードのピンやプロパティの表示非表示を制御

UPROPERTYマクロを使用して設定することが可能

UPROPERTY(Category=Settings, meta(PinShownByDefault))
mutable float Alpha;

ノードグラフのピンにするためには以下のUPROPERTYのメタデータを設定する必要がある

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Anim, meta=(PinShownByDefault))
metaデータ
NeverAsPin プロパティはデータピンとしてエクスポーズされない。[詳細] パネルのみで編集が可能です。
PinHiddenByDefault データピンとして使用可能になる。デフォルトでは非表示になっています。
PinShownByDefault デフォルトで可視化されています。
AlwaysAsPin プロパティは常に表示になる。

UPROPERTYその他の設定方法として、以下の設定でBool型プロパティの値により編集可能か不可能か切り替えることが可能です。
EditCondition="プロパティ名"

SkeletalControlBaseの処理

初期化

書式

virtual void Initialize(const FAnimationInitializeContext& Context)

親クラスFAnimNode_BaseとComponentPoseのInitialize関数を呼び出す。
他にポーズを参照するピンがあればoverrideして各ポーズのInitialize関数を呼び出す。
ポーズピンを追加していなければ、特に処理を書く必要は無いですが、初期化処理はここに

ノードの処理を行うかどうかの判定

書式

virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)

ブレンド率やLODレベルのチェック以外でこのノードの処理を行わない条件を付けたい場合はこれをoverride

ボーンの参照の初期化

書式

virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones)

ボーン参照の初期化処理をここで行います。
例:スケルタルコントロールノードのボーンリファレンスを初期化
初期化をしないとGetComponentSpaceTransform等ボーンを取得できずに失敗するので

Update時処理

書式

virtual void UpdateInternal(const FAnimationUpdateContext& Context)

Update処理が呼ばれた際に呼ばれる処理
注意する点として

  • ブレンド率が0のときや現在の表示されるLODレベルにより呼ばれない
  • FAnimationUpdateContextから1フレームの経過時間を取得するようにする
    ※アニメーションの処理がゲームスレッドで動作していないため、GetWorld()から時間の取得は使用しないほうが良いです。

ボーンのTransformの計算時の処理

書式

virtual void EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)

これをオーバーライドし、ボーンを制御するため処理を記述します。
ボーンの制御のために必要な処理をまとめておきます。

ボーンのインデックスを取得

const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();
FCompactPoseBoneIndex CompactPoseBoneToModify = ControlBone.GetCompactPoseIndex(BoneContainer);

制御するボーンの現状のTransformnの取得

FTransform NewBoneTM = MeshBases.GetComponentSpaceTransform(CompactPoseBoneToModify);`

各スペース座標への変換
計算するそれぞれのボーンスペースに変更するには

FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, BCS_BoneSpace);

コンポーネントスペースに戻すには

FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, BCS_BoneSpace);

例としてボーンスペース空間にしていますが、BCS_BoneSpaceを以下に変更することにより他の空間座標に変換されます。

Enum
BCS_WorldSpace ワールド空間座標
BCS_ComponentSpace コンポーネント空間座標
BCS_ParentBoneSpace 親ボーンスペース空間座標
BCS_BoneSpace ボーンスペース空間座標

変更するボーンのTransformの設定
OutBoneTransformsにどのボーンをどんなTransformにするかを追加するとボーンが変更されます。

OutBoneTransforms.Add( FBoneTransform(ControlBone.GetCompactPoseIndex(BoneContainer), NewBoneTM) );

とりあえずこれでできるはず…

以上のことを実装すればボーンを操作することができるアニメーショングラフノードを作ることができると思います。
といっても、今回は必要最低限しかここにまとめていません。
ほかにオレオレIKやオレオレ物理アニメーションを作ってみたいと思った方はGitHubからソースコードを落として参考に実装してみると良いと思います。

明日は@dgtanakaさん、裏は@yoshikataです

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした