相关数据包
乳摇动画有两种做法:
弹簧控制器物理模拟通常来说使用物理模拟的情况会多一些
弹簧控制器使用骨骼中需要有胸部骨骼:
在动画蓝图中分别对胸部骨骼添加添加Spring Controller控制节点即可
可配置选项如下:
核心参数:
SpringBone:指定要应用弹簧效果的骨骼SpringStiffness:弹簧刚度,值越大恢复越快SpringDamping:弹簧阻尼,控制运动衰减速度ErrorResetThresh:误差重置阈值,防止过度拉伸其中Filter Channels是设置可位移和旋转的通道。
工作原理初始化:节点获取初始骨骼位置和旋转物理模拟: 计算弹簧力(基于胡克定律(F = -kx))应用阻尼力(与速度成比例(F = -bv))考虑重力、风力等外力数值积分:使用Verlet积分等方法更新位置约束应用:确保运动在限定范围内结果输出:将计算的位置/旋转应用到骨骼源码分析源码在Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_SpringBone.cpp文件中
主要逻辑在FAnimNode_SpringBone::EvaluateSkeletalControl_AnyThread中,这个函数的主要作用是根据物理弹簧模型计算骨骼的新位置和旋转,并将结果输出到OutBoneTransforms数组中。下面解析其工作原理:
1. 初始检查和设置DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(EvaluateSkeletalControl_AnyThread)
ANIM_MT_SCOPE_CYCLE_COUNTER_VERBOSE(SpringBone, !IsInGameThread());
check(OutBoneTransforms.Num() == 0);
const bool bNoOffset = !bTranslateX && !bTranslateY && !bTranslateZ;
if (bNoOffset)
{
return;
}
声明性能计数器和多线程安全范围检查输出数组是否为空如果没有启用任何平移轴,则直接返回2. 获取骨骼初始位置const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
const FCompactPoseBoneIndex SpringBoneIndex = SpringBone.GetCompactPoseIndex(BoneContainer);
const FTransform& SpaceBase = Output.Pose.GetComponentSpaceTransform(SpringBoneIndex);
FTransform BoneTransformInWorldSpace = SpaceBase * Output.AnimInstanceProxy->GetComponentTransform();
FVector const TargetPos = BoneTransformInWorldSpace.GetLocation();
获取骨骼容器和骨骼索引计算骨骼在世界空间中的初始位置(TargetPos)3. 初始化物理状态if (RemainingTime == 0.0f)
{
BoneLocation = TargetPos;
BoneVelocity = FVector::ZeroVector;
}
如果是第一次运行,初始化骨骼位置和速度4. 固定时间步长物理模拟if(!FMath::IsNearlyZero(FixedTimeStep, KINDA_SMALL_NUMBER))
{
while (RemainingTime > FixedTimeStep)
{
// 物理模拟循环
}
}
使用固定时间步长进行稳定物理模拟4.1 基础移动和重置检查FVector const BaseTranslation = (OwnerVelocity * FixedTimeStep);
BoneLocation += BaseTranslation;
if (((TargetPos - BoneLocation).SizeSquared() > (ErrorResetThresh*ErrorResetThresh)))
{
BoneLocation = TargetPos;
BoneVelocity = FVector::ZeroVector;
}
根据所有者速度更新骨骼位置如果偏离目标位置超过阈值,则重置状态4.2 弹簧力计算FVector const Error = (TargetPos - BoneLocation);
FVector const DampingForce = SpringDamping * BoneVelocity;
FVector const SpringForce = SpringStiffness * Error;
FVector const Acceleration = SpringForce - DampingForce;
计算弹簧力(胡克定律)和阻尼力得到净加速度4.3 速度积分// 防止阻尼过大导致不稳定
if (SpringDamping > CutOffDampingValue)
{
double const SafetyScale = CutOffDampingValue / SpringDamping;
BoneVelocity += SafetyScale * (Acceleration * FixedTimeStep);
}
else
{
BoneVelocity += (Acceleration * FixedTimeStep);
}
积分计算新速度,包含安全机制防止数值不稳定4.4 位置积分和限制FVector const DeltaMove = (BoneVelocity * FixedTimeStep);
BoneLocation += DeltaMove;
// 应用位移限制
if (bLimitDisplacement)
{
// 限制最大位移
}
积分计算新位置应用位移限制确保不会过度拉伸5. 旋转计算if (bUseRotation)
{
// 计算基于位移的旋转
FQuat AdditionalRotation = FQuat::FindBetweenNormals(ParentToTarget, ParentToCurrent);
// 应用旋转轴过滤
OutBoneTM.SetRotation(FQuat::MakeFromEuler(EularRot) * OutBoneTM.GetRotation());
}
如果启用了旋转,计算骨骼因位移产生的旋转应用旋转轴过滤6. 输出结果OutBoneTransforms.Add(FBoneTransform(SpringBoneIndex, OutBoneTM));
将最终变换添加到输出数组物理模拟使用骨骼中需要有胸部骨骼:
步骤如下:
打开角色Physics Asset旋转胸部骨骼创建Sphere Body设置球形大小设置物理类型为模拟打开约束设置约束->锁住角度设置约束->线性限制设置约束->线性力度两边胸都做上面操做即可
ReferenceJiggle physics tutorial for Unreal Engine 5How to Setup Jiggle Physics in Unreal Engine 5