关卡 动画 蓝图 运行_UE4自定义动画蓝图步骤

动画蓝图提供了运行特定操作的节点,比如基于alpha值混合多个节点或者播放一个动画。 这里,您可以找到关于基本动画蓝图节点的更多信息。

这些节点提供了您将需要的标准功能,但是通常您应用动画时,一般需要创建自定义节点。这比较简单,但是却需要您了解基础系统的设计原理。该链接提供了关于该系统的深入知识,但是这里我想着重强调一下基本系统的内容,因为我发现有些人经常会遇到问题。

正如上面链接中所述的,系统需要两个类:一个是您在编辑器中看到的图表节点,一个是真正在运行时工作的行为节点。我们出于优化目的将其分离开来。节点构建的性能消耗比较大,使用400个节点每30秒让20个角色死亡或生成将会给内存及CPU造成巨大负担。如果您使用动画蓝图生成了一个角色,那么该角色将不会具有任何图表节点,仅具有行为节点。

让我们比较一下动画图表节点和动画行为节点的代码:

动画图表节点

class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base

动画行为节点

struct ENGINE_API FAnimNode_SequencePlayer : public FAnimNode_Base

您将注意到这两个节点的基类是不同的:一个基类是UObject,另一个的基类是UStruct。在该博客中,我们将前一个作为动画图表节点,后一个作为动画行为节点。所有的图表节点包含了类似这样的对应行为节点:

class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base

{

GENERATED_UCLASS_BODY()

UPROPERTY(EditAnywhere, Category=Settings)

FAnimNode_SequencePlayer Node;

}

该动画图表节点知道另一个节点的存在,但反之则不能,这一个比较重要的区别。所有动画图表节点都是出于这个原因存在于编辑器中的,因为它不会随同游戏加载,仅存在于编辑器中。另一方面,行为节点存在于运行时代码中,而这正是真正发生混合的地方。请确保您的类指向正确的模块。

这对骨架控制节点来说也是一样的。

动画图表节点

class UAnimGraphNode_ModifyBone : public UAnimGraphNode_SkeletalControlBase

动画行为节点

struct ENGINE_API FAnimNode_ModifyBone : public FAnimNode_SkeletalControlBase

您将注意到骨架控制节点也具有不同的基类。

该系统如此设计,动画图表节点负责任何编辑器工作,比如显示节点名称、显示工具提示信息或创建自定义引脚。动画行为节点负责实际工作,比如混合、计算目标位置,及输出正确姿势。所以动画图表节点在编辑器中是重要的,而动画行为节点则在运行时是重要的。

再次说明,请参照该链接来查看变量的元数据是如何变为节点的输入或输出的。

比如,FPoseLink是传入骨骼变换数组的姿势连接,如果您像下面这样声明它,那么它将会像图片中那样显示出来。

UPROPERTY(Category=Links)

FPoseLink BasePose;

知道FPoseLink如何工作比较重要,因为任何时候当您调用任何动画函数时,您也必须调用该Pose函数。比如在您的Update函数中您应该调用BasePose->Update。同样,如果您有BasePose作为成员变量,您也应该在CacheBones函数中调用BasePose->CacheBones。

现在,我想谈下对于每种节点类型您应该关注的函数。我将不会集中介绍动画蓝图图表节点,因为它同任何其他蓝图节点的工作方式比较类似,但是我想集中介绍下这个真正工作的节点。

让我们看下FAnimNode_Base节点:

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){}

};

它不是这么简单,但我正在进行简化以仅集中介绍您应该关心的主要事情。

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

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

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

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

示例:

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设置为参考姿势。

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

virtual void CacheBones(const FAnimationCacheBonesContext& Context) {}

virtual void GatherDebugData(FNodeDebugData& DebugData){}

CacheBones用于刷新该节点所引用的骨骼索引,GatherDebugData用于使用"ShowDebug Animation"数据进行调试。为了保持到子项的连接,使用这些是很重要的。正如我之前所提到的,FPoseLink 应该调用它下面的所有节点,以确保您的节点连接的任何姿势连接都会被调用。

请参照该示例:

void FAnimNode_BlendListBase::CacheBones(const FAnimationCacheBonesContext& Context)

{

for(int32 ChildIndex=0; ChildIndex

{

BlendPose[ChildIndex].CacheBones(Context);

}

}

您需要实现它,以便这些事件不会被您的节点阻止。

同时注意我们有FAnimationRuntime,当进行动画时它提供了大量功能。

这里是一个关于骨架控制节点的示例:

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

}

这同动画节点类似但又有所不同,因为骨架控制节点在组件空间上工作。再次说明,查看FAnimationRuntime将向您展示一种在本地空间和组件空间之间转换的好方法。

您一般都使用EvaluateComponentSpace。这里是一个涉及到CopyBone节点的简单应用示例:

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中返回您想要的数据。您可以返回您需要的任何数量的骨骼变换,但请注意层次结构。保持父项到子项的顺序将能确保总是安全的。

我希望这向您进行了进一步介绍,以使得您开始创建自己的自定义节点变得更加容易。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值