• 您的位置:首頁 > 新聞動態(tài) > UE4

    UE4自定義動畫藍圖步驟

    2021/1/2??????點擊:

    動畫藍圖提供了運行特定操作的節(jié)點,比如基于alpha值混合多個節(jié)點或者播放一個動畫。 這里,您可以找到關于基本動畫藍圖節(jié)點的更多信息。

    這些節(jié)點提供了您將需要的標準功能,但是通常您應用動畫時,一般需要創(chuàng)建自定義節(jié)點。這比較簡單,但是卻需要您了解基礎系統(tǒng)的設計原理。該鏈接提供了關于該系統(tǒng)的深入知識,但是這里我想著重強調(diào)一下基本系統(tǒng)的內(nèi)容,因為我發(fā)現(xiàn)有些人經(jīng)常會遇到問題。

    正如上面鏈接中所述的,系統(tǒng)需要兩個類:一個是您在編輯器中看到的圖表節(jié)點,一個是真正在運行時工作的行為節(jié)點。我們出于優(yōu)化目的將其分離開來。節(jié)點構建的性能消耗比較大,使用400個節(jié)點每30秒讓20個角色死亡或生成將會給內(nèi)存及CPU造成巨大負擔。如果您使用動畫藍圖生成了一個角色,那么該角色將不會具有任何圖表節(jié)點,僅具有行為節(jié)點。

    讓我們比較一下動畫圖表節(jié)點和動畫行為節(jié)點的代碼:

    動畫圖表節(jié)點

    class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base

    動畫行為節(jié)點

    struct ENGINE_API FAnimNode_SequencePlayer : public FAnimNode_Base

    您將注意到這兩個節(jié)點的基類是不同的:一個基類是UObject,另一個的基類是UStruct。在該博客中,我們將前一個作為動畫圖表節(jié)點,后一個作為動畫行為節(jié)點。所有的圖表節(jié)點包含了類似這樣的對應行為節(jié)點:

    class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base
    {
        GENERATED_UCLASS_BODY()

        UPROPERTY(EditAnywhere, Category=Settings)
        FAnimNode_SequencePlayer Node;

    }

    該動畫圖表節(jié)點知道另一個節(jié)點的存在,但反之則不能,這一個比較重要的區(qū)別。所有動畫圖表節(jié)點都是出于這個原因存在于編輯器中的,因為它不會隨同游戲加載,僅存在于編輯器中。另一方面,行為節(jié)點存在于運行時代碼中,而這正是真正發(fā)生混合的地方。請確保您的類指向正確的模塊。

    這對骨架控制節(jié)點來說也是一樣的。

    動畫圖表節(jié)點

    class UAnimGraphNode_ModifyBone : public UAnimGraphNode_SkeletalControlBase

    動畫行為節(jié)點

    struct ENGINE_API FAnimNode_ModifyBone : public FAnimNode_SkeletalControlBase

    您將注意到骨架控制節(jié)點也具有不同的基類。

    該系統(tǒng)如此設計,動畫圖表節(jié)點負責任何編輯器工作,比如顯示節(jié)點名稱、顯示工具提示信息或創(chuàng)建自定義引腳。動畫行為節(jié)點負責實際工作,比如混合、計算目標位置,及輸出正確姿勢。所以動畫圖表節(jié)點在編輯器中是重要的,而動畫行為節(jié)點則在運行時是重要的。

    再次說明,請參照該鏈接來查看變量的元數(shù)據(jù)是如何變?yōu)楣?jié)點的輸入或輸出的。

    比如,F(xiàn)PoseLink是傳入骨骼變換數(shù)組的姿勢連接,如果您像下面這樣聲明它,那么它將會像圖片中那樣顯示出來。


    UPROPERTY(Category=Links)
    FPoseLink BasePose;


    知道FPoseLink如何工作比較重要,因為任何時候當您調(diào)用任何動畫函數(shù)時,您也必須調(diào)用該Pose函數(shù)。比如在您的Update函數(shù)中您應該調(diào)用BasePose->Update。同樣,如果您有BasePose作為成員變量,您也應該在CacheBones函數(shù)中調(diào)用BasePose->CacheBones。

    現(xiàn)在,我想談下對于每種節(jié)點類型您應該關注的函數(shù)。我將不會集中介紹動畫藍圖圖表節(jié)點,因為它同任何其他藍圖節(jié)點的工作方式比較類似,但是我想集中介紹下這個真正工作的節(jié)點。

    讓我們看下FAnimNode_Base節(jié)點:

    struct ENGINE_API FAnimNode_Base
    {
        // Interface to implement
        virtual void Initialize(const FAnimationInitializeContext& Context) {}
        virtual void Update(const FAnimationUpdateContext& Context) {}
        virtual void Evaluate(FPoseContext& Output) { check(false); }
        virtual void CacheBones(const FAnimationCacheBonesContext& Context) {}
        virtual void GatherDebugData(FNodeDebugData& DebugData){}

    };

    它不是這么簡單,但我正在進行簡化以僅集中介紹您應該關心的主要事情。

    有三個決定了您的節(jié)點如何表現(xiàn)的主要函數(shù)。它們是Initialize、Update和Evaluate,這里是對它們應用的簡單描述:

    • Initialize - 任何時候當您需要進行初始化或重新初始化時調(diào)用該函數(shù)(當修改實例的網(wǎng)格物體時)。
    • Update - 調(diào)用該函數(shù)來更新當前狀態(tài)(比如更新播放時間或混合權重)。該函數(shù)取入一個FAnimationUpdateContext,它知道更新的DeltaTime和當前的節(jié)點混合權重。
    • Evaluate - 調(diào)用該函數(shù)來生成一個‘姿勢’(一系列的骨骼變換)。
      • 示例:
        • void FAnimNode_SequenceEvaluator::Evaluate(FPoseContext& Output)
          {
              if ((Sequence != NULL) && (Output.AnimInstance->CurrentSkeleton->IsCompatible(Sequence->GetSkeleton())))
              {
                  Output.AnimInstance->SequenceEvaluatePose(Sequence, Output.Pose, FAnimExtractContext(ExplicitTime));
              }
              else
              {
                  Output.ResetToRefPose();
              }
          }

      • Evaluate 判斷是否設置了序列及它是否同當前骨架兼容。如果是,那么它將該骨骼變換填充到Output.Pose中。如果不是,則將Output設置為參考姿勢。


    在這些基本函數(shù)的基礎上,您需要提供兩個函數(shù)的實現(xiàn),以確保您的節(jié)點可以正常同圖表的其他部分協(xié)同工作:

    virtual void CacheBones(const FAnimationCacheBonesContext& Context) {}
    virtual void GatherDebugData(FNodeDebugData& DebugData){}

    CacheBones用于刷新該節(jié)點所引用的骨骼索引,GatherDebugData用于使用"ShowDebug Animation"數(shù)據(jù)進行調(diào)試。為了保持到子項的連接,使用這些是很重要的。正如我之前所提到的,F(xiàn)PoseLink 應該調(diào)用它下面的所有節(jié)點,以確保您的節(jié)點連接的任何姿勢連接都會被調(diào)用。

    請參照該示例:

    void FAnimNode_BlendListBase::CacheBones(const FAnimationCacheBonesContext& Context)
    {
        for(int32 ChildIndex=0; ChildIndex
        {
            BlendPose[ChildIndex].CacheBones(Context);
        }
    }

    您需要實現(xiàn)它,以便這些事件不會被您的節(jié)點阻止。

    同時注意我們有FAnimationRuntime,當進行動畫時它提供了大量功能。

    這里是一個關于骨架控制節(jié)點的示例:

    struct ENGINE_API FAnimNode_SkeletalControlBase : public FAnimNode_Base
    {
        // FAnimNode_Base interface
        virtual void Initialize(const FAnimationInitializeContext& Context) override;
        virtual void CacheBones(const FAnimationCacheBonesContext& Context)  override;
        virtual void Update(const FAnimationUpdateContext& Context) override;
        virtual void EvaluateComponentSpace(FComponentSpacePoseContext& Output) override;
        // End of FAnimNode_Base interface
    }

    這同動畫節(jié)點類似但又有所不同,因為骨架控制節(jié)點在組件空間上工作。再次說明,查看FAnimationRuntime將向您展示一種在本地空間和組件空間之間轉換的好方法。

    您一般都使用EvaluateComponentSpace。這里是一個涉及到CopyBone節(jié)點的簡單應用示例:

    void FAnimNode_CopyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose& MeshBases, TArray& OutBoneTransforms)
    {
        check(OutBoneTransforms.Num() == 0);
        // Pass through if we're not doing anything.
        if( !bCopyTranslation && !bCopyRotation && !bCopyScale )
        {
            return;
        }
        // Get component space transform for source and current bone.
        const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();
        FCompactPoseBoneIndex TargetBoneIndex = TargetBone.GetCompactPoseIndex(BoneContainer);
        const FTransform& SourceBoneTM = MeshBases.GetComponentSpaceTransform(SourceBone.GetCompactPoseIndex(BoneContainer));
        FTransform CurrentBoneTM = MeshBases.GetComponentSpaceTransform(TargetBoneIndex);
        // Copy individual components
        if (bCopyTranslation)
        {
            CurrentBoneTM.SetTranslation( SourceBoneTM.GetTranslation() );
        }
        if (bCopyRotation)
        {
            CurrentBoneTM.SetRotation( SourceBoneTM.GetRotation() );
        }
        if (bCopyScale)
        {
            CurrentBoneTM.SetScale3D( SourceBoneTM.GetScale3D() );
        }
        // Output new transform for current bone.
        OutBoneTransforms.Add(FBoneTransform(TargetBoneIndex, CurrentBoneTM));
    }

    其目的是在OutBoneTransforms中返回您想要的數(shù)據(jù)。您可以返回您需要的任何數(shù)量的骨骼變換,但請注意層次結構。保持父項到子項的順序?qū)⒛艽_保總是安全的。

    我希望這向您進行了進一步介紹,以使得您開始創(chuàng)建自己的自定義節(jié)點變得更加容易。