UE4 创建自定义动画节点

创建自定义动画节点需要两个类:

一个是您在编辑器中看到的图表节点

一个是真正在运行时工作的行为节点

 

动画图表节点,派生自:UAnimGraphNode_Base

例如:class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base

 

动画行为节点,派生自:FAnimNode_Base

例如:struct ENGINE_API FAnimNode_SequencePlayer : public FAnimNode_Base

 

两个节点的基类是不同的:一个基类是UObject(UAnimGraphNode_Base),另一个的基类是UStruct(FAnimNode_Base)

 

动画图表节点类的构建:

所有的图表节点包含了类似这样的对应行为节点:

 

class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base

{

    GENERATED_BODY()

    UPROPERTY(EditAnywhere, Category=Settings)

    FAnimNode_SequencePlayer Node;

}

 

这就是最简单的 动画图表节点,其中包含动作行为节点对象。 

动画行为节点的执行,是通过FPoseLink或FComponentSpacePoseLink进行串联的。

 

UE4 创建自定义动画节点_第1张图片

FPoseLink 是本地骨骼空间的(其实是相对于父节点来说的UE4 创建自定义动画节点_第2张图片), FComponetSpacePoseLink是控件空间的。

 

动画图表节点默认带有FPoseLink输出(也就是右侧的小人, 如果需要改变成FComponetSpacePoseLink, 需要重写CreateOutputPins,其中调用CreatePin。

 

virtual void CreateOutputPins() override;

void UAnimGraphNode_LocalToComponentSpace::CreateOutputPins()

{

       CreatePin(EGPD_Output, UAnimationGraphSchema::PC_Struct,  FComponentSpacePoseLink::StaticStruct(), TEXT("ComponentPose"));

}

 

 

动画行为节点类的构建

让我们看下FAnimNode_Base节点:

struct ENGINE_API FAnimNode_Base

{

    // Interface to implement,这就是你在你的动画行为节点中应该重写的几个函数

    virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) {}

    virtual void Update_AnyThread(const FAnimationUpdateContext& Context) {}

    virtual void Evaluate_AnyThread(FPoseContext& Output) { check(false); }

    virtual void EvaluateComponentSpace_AnyThread(FComponentSpacePoseContext& Output) { check(false); }

    virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) {}

    virtual void GatherDebugData(FNodeDebugData& DebugData){}

};

有三个决定了您的节点如何表现的主要函数。它们是Initialize_AnyThread、Update_AnyThread和Evaluate_AnyThread,这里是对它们应用的简单描述:

  • Initialize_AnyThread - 任何时候当您需要进行初始化或重新初始化时调用该函数(当修改实例的网格物体时)。

  • Update_AnyThread - 调用该函数来更新当前状态(比如更新播放时间或混合权重)。该函数取入一个FAnimationUpdateContext,它知道更新的DeltaTime和当前的节点混合权重。

  • Evaluate_AnyThread - 调用该函数来生成一个‘姿势’(一系列的骨骼变换)。//当动画图表节点的输出是FPoseLink时,执行的是该函数, 如果是FComponetSpacePoseLink,执行的应该是EvaluateComponentSpace_AnyThread

  • EvaluateComponentSpace_AnyThread- 调用该函数来生成一个‘姿势’(一系列的骨骼变换)

最重要的就是Evaluate_AnyThreadEvaluateComponentSpace_AnyThread,其中最终的操作是骨骼变换的设置。

void FAnimNode_MYAnimNode::Evaluate_AnyThread(FPoseContext & Output){

  
    FName BoneName(TEXT("boneName"));

    //Output.Pose.GetBoneContainer()返回的FBoneContainer中,包含有当前网格体用到的骨架的引用,以及当前网格体用到的骨架中的真正用到的骨骼的索引数组。索引数组包含骨架的部分或全部。

    //OutPut.Pose中包含用到的骨骼的变换数组,骨骼变换数组与FBoneContainer中的索引数组相互对应。

    //GetPoseBoneIndexForBoneName是根据骨骼名称,获取骨骼的索引

    int32 MeshIndex = Output.Pose.GetBoneContainer().GetPoseBoneIndexForBoneName(BoneName);

    if (MeshIndex != INDEX_NONE)

    {
        //MakeCompactPoseIndex 是根据骨骼的索引,找到骨骼索引在索引数组的位置,也就是OutPut.Pose中对应骨骼变化的变换数组的位置。

        FCompactPoseBoneIndex CPIndex = Output.Pose.GetBoneContainer().MakeCompactPoseIndex(FMeshPoseBoneIndex(MeshIndex));

        if (CPIndex != INDEX_NONE)

        {
            FTransform boneTransfrom(FRotator(0.0f, 0.0f, 90.0f));
            Output.Pose[CPIndex] = boneTransfrom;

        }

    }

    //注意这时候当output.IsNormalized()返回false, Output.ContainsNaN() 返回true,这时候表示没有正确设置。尤其是当该节点与使用AnimNode_SaveCachePose一起使用,output传进来时,所有骨骼都没有设置正确的变换,这时候可以使用Output.ResetToRefPose(),将所有骨骼变换设置为参考姿势。

}

 

 

 

在这些基本函数的基础上,您需要提供两个函数的实现,以确保您的节点可以正常同图表的其他部分协同工作

virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) {}

virtual void GatherDebugData(FNodeDebugData& DebugData){}

CacheBones_AnyThread用于刷新该节点所引用的骨骼索引,GatherDebugData用于使用"ShowDebug Animation"数据进行调试。为了保持到子项的连接,使用这些是很重要的。

FPoseLink 应该调用它下面的所有节点,以确保您的节点连接的任何姿势连接都会被调用。知道FPoseLink如何工作非常重要,因为任何时候当您调用任何动画函数时,您也必须调用该Pose函数。比如在您的Update_AnyThread函数中您应该调用BasePose->Update。同样,如果您有BasePose作为成员变量,您也应该在CacheBones_AnyThread函数中调用BasePose->CacheBones。(调用FPoseLink的对应函数,FPoseLink会找到它所连接的上一个节点,然后执行上一个节点的对应函数,节点对应函数调用的次序有点类似于递归,从最终动画姿势开始,一直到最开始,见下图)

UE4 创建自定义动画节点_第3张图片

请参照该示例:

 

void FAnimNode_BlendListBase::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)

{

    for(int32 ChildIndex=0; ChildIndex

    {

        BlendPose[ChildIndex].CacheBones(Context);

    }

}

 

 

 

void FAnimNode_BlendListBase::Evaluate_AnyThread(FPoseContext& Output)

{

。。。

              for (int32 i = 0; i < PosesToEvaluate.Num(); ++i)

              {

                     int32 PoseIndex = PosesToEvaluate[i];

                     FPoseContext EvaluateContext(Output);

                     FPoseLink& CurrentPose = BlendPose[PoseIndex];

                     CurrentPose.Evaluate(EvaluateContext);

                     FilteredPoses[i].MoveBonesFrom(EvaluateContext.Pose);

                     FilteredCurve[i].MoveFrom(EvaluateContext.Curve);

              }

。。。

}

 

  参见:https://www.unrealengine.com/zh-CN/blog/creating-custom-animation-nodes

 

你可能感兴趣的:(ue4,ue4,学习与实践)