概述
在角色处于趴姿或倒地状态时,为避免模型与斜坡、台阶等不平整表面发生穿模,通常采用程序化动画(Procedural Animation)方案进行适配。本项目通过多重射线检测(Trace)与骨骼旋转计算,实现角色身体对地形的自适应贴合。
效果展示
旋转策略:确定受影响的骨骼
根据角色前后左右与场景的接触情况,需对角色的 Roll(水平倾斜)和 Pitch(上下俯仰)进行调整。类比芭比娃娃的关节活动,我们将自适应旋转抽象为以下三个部分的骨骼控制:
-
上半身:旋转脊柱骨骼【spine_01】,以调整躯干俯仰角。 -
下半身:旋转左右大腿骨骼【thigh_l】和【thigh_r】,以调整腿部姿态。 -
整体倾斜:旋转根节点【root】,以调整整体的左右平衡。
具体实现上,通过 Transform (Modify) Bone 节点,根据计算出的角度对相应骨骼进行旋转。
核心算法:分步计算旋转角度
第一步:获取基础接触点
基于角色胶囊体获取当前地面的初始接触点 theCurrentFloorImpactPoint。
FVector theCurrentFloorImpactPoint = theMovementComp->GetActorFeetLocation();
第二步:向下 Trace 确定中心接触点
从基础接触点向下进行射线检测,精确找到胶囊体与当前地面的实际接触点。若未检测到碰撞,则默认使用移动组件提供的足部位置。
// Trace Down Capsule
FHitResult CapsuleHitResult;
//从theCurrentFloorImpactPoint向下Trace,找到地面接触点
//如果Hit不到,默认使用移动组件的ACtorFeetLoacation
FVector theCapsuleHitPoint = theMovementComp->GetActorFeetLocation();
FVector theCapsuleStart = theCurrentFloorImpactPoint - FVector::DownVector * TraceUpExtent;
FVector theCapsuleEnd = theCurrentFloorImpactPoint + FVector::DownVector * TraceDownExtent;
bool bCapsuleHit = World->SweepSingleByChannel(CapsuleHitResult, theCapsuleStart,
theCapsuleEnd, FQuat::Identity, CollisionChannel, FCollisionShape::MakeSphere(TraceSphereRadius),
QueryParams, ResponseParams);
第三步:左右延伸,计算 Root Roll 夹角
从中心接触点向左右两侧延伸并进行向下 Trace,以获取地面的横向坡度,从而旋转 Root 保持水平。
-
若左右两侧均检测到地面,利用左右 Hit 点的连线计算夹角。 -
若仅单侧检测到地面,利用该侧 Hit 点与中心接触点的连线计算夹角。
// Trace Left and Right
FHitResult LeftHitResult;
FVector theLeftStart = theCurrentFloorImpactPoint - theMovementComp->UpdatedComponent->
GetRightVector() * 5.f - FVector::DownVector * 25.f;
FVector theLeftEnd = theCurrentFloorImpactPoint - theMovementComp->UpdatedComponent->
GetRightVector() * 5.f + FVector::DownVector * 25.f;
bool bUpperLeftHit = World->SweepSingleByChannel(LeftHitResult, theLeftStart,
theLeftEnd, FQuat::Identity, CollisionChannel, FCollisionShape::MakeSphere(3.f), QueryParams, ResponseParams);
FHitResult RightHitResult;
FVector theRightStart = theCurrentFloorImpactPoint + theMovementComp->UpdatedComponent->
GetRightVector() * 5.f - FVector::DownVector * 25.f;
FVector theRightEnd = theCurrentFloorImpactPoint + theMovementComp->UpdatedComponent->
GetRightVector() * 5.f + FVector::DownVector * 25.f;
bool bUpperRightHit = World->SweepSingleByChannel(RightHitResult, theRightStart,
theRightEnd,FQuat::Identity, CollisionChannel, FCollisionShape::MakeSphere(3.f), QueryParams, ResponseParams);
第四步:向前延伸,计算上半身 Pitch 夹角
向前延伸至手臂支撑区域附近进行向下 Trace。需注意,此方法计算的角度旨在保证手臂支撑点位于平面上,但由于趴姿下 Spine_01 到手臂之间存在骨骼链,直接应用该角度至 Spine_01 存在一定偏差,但在可接受范围内。
// Trace From Upper
FHitResult UpperHitResult;
FVector theUpperTracePoint = theCurrentFloorImpactPoint + TraceDirection * UpperExtent;
FVector theHeadStart = theUpperTracePoint - FVector::DownVector * TraceUpExtent;
FVector theHeadEnd = theUpperTracePoint + FVector::DownVector * TraceDownExtent;
bool bUpperHit = World->SweepSingleByChannel(UpperHitResult, theHeadStart, theHeadEnd, FQuat::Identity,
CollisionChannel, FCollisionShape::MakeSphere(TraceSphereRadius),
QueryParams, ResponseParams);
第五步:向后延伸,计算下半身 Pitch 夹角
同理,向后延伸至脚部附近进行向下 Trace,计算脚部平面与胶囊体中心 Hit 位置的夹角,用于调整大腿骨骼旋转。
第六步:处理尖角屋顶导致的穿插
判断胶囊体与下半身所在平面的法线。若法线延长线不相交(例如处于尖角房顶下方),意味着存在几何穿插风险,需将下半身进一步上抬。
上抬逻辑推导:
目标:求从 LowerImpactPoint 沿方向 (0,0,1) 延伸,与由“点1 (CapsuleHitPoint)”和“法线 (CapsuleHitNormal)”确定的平面相交的交点。
原理:平面上任意两点连线垂直于法线(点乘为0)。
已知:
-
点1: theCapsuleHitPoint -
点2(延伸交点): LowerImpactPoint + t * Direction(t 为未知数)
由于两点均在目标平面上,其连线必与 CapsuleHitNormal 垂直,据此可解出 t 值并确定最终位置。
第七步:处理下半身悬空问题
关于是否需判断 CapsuleHitPoint 和 LowerHitPoint 连线是否有阻挡以规避穿插,经分析认为简单的 Z 轴最大值对比不符合运动规律。项目策略倾向于优先保证上半身贴合胶囊体所在平面。在此规则下,允许下半身出现适当悬空。
由于趴姿移动前已进行严格的碰撞预检测,若下半身悬空过大导致不符合趴姿条件,系统会自动切换至蹲姿。因此,自适应旋转模块无需过度处理因极端地形导致的下半身严重悬空问题。
第八步:应用旋转与限制
利用 LookAt 函数计算各 HitPoint 对应的实际角度差,并将其作为骨骼 Rotation 的增量。同时,遵循人体工程学原理并避免破坏原有动画表现,需对各关节的旋转角度设置合理的上限阈值。
第九步:参数配置化
代码中的 Trace 距离、半径等数值均为经验值。为适配不同地形结构、角色身高、胶囊体尺寸及多种姿态(趴姿、倒地、滑铲等),需将这些魔法数字提取为可配置参数,以便灵活调整。
待优化与结论
问题:仅靠旋转能否完全满足贴地要求?
分析:胶囊体触地并不意味着 Mesh 最低点触地。经过旋转修正后,角色可能与平面平行但仍存在悬空。若强制向下平移 Mesh 以贴合 Trace 到的平面,在不平整地面上极易导致其他部位(如膝盖、手肘)发生穿模。
结论:在实际项目中,结合严格的进入趴姿条件限制,仅通过 Mesh 旋转已能满足绝大多数场景的视觉需求。进一步调整 Mesh 位移的开发成本较高且收益有限,故本项目不再深入优化位移贴合部分。

