目的:根据曲线值获得当前动作帧。用于实现各种通过曲线同步的功能。
方法:继承FAnimNode_Base创建自定义动画节点。重写Evaluate部分。创建相应的AnimGraphNode。可参考前一篇http://blog.csdn.net/u010831746/article/details/50733287
Evaluate : 1. 根据曲线Value(Y轴)值获得Time(X轴)值。
曲线KeyArray所在位置。AnimSequenceBase : RawCurveData.
类型AnimCurveTypes.h:FRawCurveTracks
曲线记录是TArray<FRichCurveKey>。每个key中存有Time和Value。根据输入的CurveValue,遍历Array查找Value最接近的Key,返回Time。
2.根据Time值获得动画POS
AnimSequence->GetAnimationPose(…, Time, …)输出Pose。
主要代码:
AnimNode_EvaluatePose.h
/*! * \file AnimNode_EvaluatePose.h * \date 2016/03/29 17:45 * * \author: Jia Zhipeng * Contact: jiazhipeng@pwrd.com * * \brief: 根据曲线获得当前Pose. Get Pose based on curve value. * * \note: */ #pragma once #include "Animation/AnimNodeBase.h" //#include "AnimNode_SkeletalControlBase.h" #include "AnimNode_EvaluatePose.generated.h" USTRUCT() struct FAnimNode_EvaluatePose :public FAnimNode_Base { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Settings, meta = (PinShownByDefault)) UAnimSequenceBase* Sequence; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Settings, meta = (PinShownByDefault)) FName CurveName; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Settings, meta = (PinShownByDefault)) float CurveValue; UAnimInstance* MyInstance; public: // Constructor FAnimNode_EvaluatePose(); // FAnimNode_Base interface virtual void Initialize(const FAnimationInitializeContext& Context) override; virtual void CacheBones(const FAnimationCacheBonesContext& Context) override; virtual void Evaluate(FPoseContext& Output) override; virtual void OverrideAsset(UAnimationAsset* NewAsset) override; virtual void GatherDebugData(FNodeDebugData& DebugData) override; // End of FAnimNode_Base interface// };
AnimNode_EvaluatePose.cpp
//Iterate Animation's curve's keys to get current time void FAnimNode_EvaluatePose::Evaluate(FPoseContext& Output) { //CurveValue is connected to AnimInstance’s variable in AnimBlueprint, but it is not updated when executing this node. So how to get variable of AnimInstance? 2 ways. //1. EvaluateGraphExposedInputs.Execute(Output); Cause crash. Explore this method in the future. //2. Get property from AnimInstance. But in this way, the variable’s name has to be specified. Temporarily use this method. UFloatProperty* FloatProp = Cast<UFloatProperty>(PW_PropertyTools::FindProperty(MyInstance, TEXT("Horiz Movement Speed"))); if (!FloatProp) { UE_LOG(LogTemp, Warning, TEXT("Horiz Movement Speed Not found")); return; } float HorzSpeed = FloatProp->GetPropertyValue_InContainer(MyInstance); CurveValue = HorzSpeed; USkeleton* Skeleton = Sequence->GetSkeleton(); if (!Skeleton) { UE_LOG(LogTemp, Warning, TEXT("FAnimNode_EvaluatePose::Evaluate fail to find skeleton")); return; } FSmartNameMapping* NameMapping = Skeleton->SmartNames.GetContainer(USkeleton::AnimCurveMappingName); // retrieve curve USkeleton::AnimCurveUID Uid; if (!NameMapping->Exists(CurveName)) { UE_LOG(LogTemp, Warning, TEXT("FAnimNode_EvaluatePose::Evaluate fail to find curve %s"), *CurveName.ToString()); return; } Uid = *NameMapping->FindUID(CurveName); FFloatCurve * CurveToCompute = static_cast<FFloatCurve*>(Sequence->RawCurveData.GetCurveData(Uid)); if (!CurveToCompute) { UE_LOG(LogTemp, Warning, TEXT("FAnimNode_EvaluatePose::Evaluate fail to find curve %s"), *CurveName.ToString()); return; } auto FloatCurve = CurveToCompute->FloatCurve; float OutTime = 0.0f; BinarySeach(FloatCurve, CurveValue, OutTime); if ((Sequence != NULL) && (Output.AnimInstance->CurrentSkeleton->IsCompatible(Sequence->GetSkeleton()))) { Sequence->GetAnimationPose(Output.Pose, Output.Curve, FAnimExtractContext(OutTime, Output.AnimInstance->ShouldExtractRootMotion())); } else { Output.ResetToRefPose(); } }