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進行串聯的。

 

FPoseLink 是本地骨骼空間的(其實是相對於父節點來說的), 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會找到它所連接的上一個節點,然後執行上一個節點的對應函數,節點對應函數調用的次序有點類似於遞歸,從最終動畫姿勢開始,一直到最開始,見下圖)

請參照該示例:

 

void FAnimNode_BlendListBase::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)

{

    for(int32 ChildIndex=0; ChildIndex<BlendPose.Num(); 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章