距离匹配(Distance Matching)
距离匹配是一种避免胶囊体速度与动画根运动速度不匹配造成的滑步等现象的技术;我们人类在运动的时候几乎不会是匀速运动或者匀加速运动,因此动捕出来的动画数据的加速度也不是匀速的,而由程序驱动的胶囊体的加速是匀速的,就会造成速度与动画不匹配,产生滑步现象:
在UE中距离匹配是一个大概念,其中包括了三个具体的函数:Advance Time by Distance Matching,Distance Match to Target,Set Playrate to Match Speed;
接下来我们逐一进行分析学习;
Advance Time by Distance Matching
Advance Time by Distance Matching(根据距离匹配前进时间),看名字我们就可以看出这个函数的作用是前进时间,也就是直接跳到该播放的位置开始播放。下面是AdvanceTimeByDistanceMatching函数源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| FSequenceEvaluatorReference UAnimDistanceMatchingLibrary::AdvanceTimeByDistanceMatching(...) {
const float CurrentTime = InSequenceEvaluator.GetExplicitTime(); const float CurrentAssetLength = InSequenceEvaluator.GetCurrentAssetLength(); const bool bAllowLooping = InSequenceEvaluator.GetShouldLoop();
const USkeleton::AnimCurveUID CurveUID = UE::Anim::DistanceMatchingUtility::GetCurveUID(AnimSequence, DistanceCurveName); float TimeAfterDistanceTraveled = UE::Anim::DistanceMatchingUtility::GetTimeAfterDistanceTraveled(AnimSequence, CurrentTime, DistanceTraveled, CurveUID, bAllowLooping);
if (TimeAfterDistanceTraveled < CurrentTime) { TimeAfterDistanceTraveled += CurrentAssetLength; } float EffectivePlayRate = (TimeAfterDistanceTraveled - CurrentTime) / DeltaTime; if (PlayRateClamp.X >= 0.0f && PlayRateClamp.X < PlayRateClamp.Y) { EffectivePlayRate = FMath::Clamp(EffectivePlayRate, PlayRateClamp.X, PlayRateClamp.Y); }
float NewTime = CurrentTime; FAnimationRuntime::AdvanceTime(bAllowLooping, EffectivePlayRate * DeltaTime, NewTime, CurrentAssetLength); }
|
通过阅读以上源码可以看出函数思路还是较为简单,要注意的是,函数是正在计算当前应该播放那一帧动画,也就是说函数中的CurrentTime其实是已经播放完的动画的上一帧,根据胶囊体在当前帧(实际也是上一帧,胶囊体已经移动过了)移动的距离,在根运动曲线中找到要移动到对应距离应该走到那一帧:
其中得到应该前进到哪一帧的关键函数GetTimeAfterDistanceTraveled的核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| float NewTime = CurrentTime; const float StepTime = 1.f / 30.f;
while ((AccumulatedDistance < DistanceTraveled) && (bAllowLooping || (NewTime + StepTime < SequenceLength))) { const float CurrentDistance = AnimSequence->EvaluateCurveData(CurveUID, NewTime); const float DistanceAfterStep = AnimSequence->EvaluateCurveData(CurveUID, NewTime + StepTime); const float AnimationDistanceThisStep = DistanceAfterStep - CurrentDistance;
if (!FMath::IsNearlyZero(AnimationDistanceThisStep)) { if (AccumulatedDistance + AnimationDistanceThisStep < DistanceTraveled) { FAnimationRuntime::AdvanceTime(bAllowLooping, StepTime, NewTime, SequenceLength); AccumulatedDistance += AnimationDistanceThisStep; } else { const float DistanceAlpha = (DistanceTraveled - AccumulatedDistance) / AnimationDistanceThisStep; FAnimationRuntime::AdvanceTime(bAllowLooping, DistanceAlpha * StepTime, NewTime, SequenceLength); AccumulatedDistance = DistanceTraveled; break; }
StuckLoopCounter = 0; } } return NewTime;
|
该函数一般用于起步动画:
Distance Match to Target
Distance Match to Target(距离匹配到目标点),一般用于停步动画,避免总是完整播放停步动画(角色已经停止还在播放剩余的停步动画)从而产生的滑步现象,根据真实停止所需的距离从动画中匹配要开始播放的帧;
核心代码:
1 2
| const float NewTime = UE::Anim::DistanceMatchingUtility::GetAnimPositionFromDistance(AnimSequence, -DistanceToTarget, CurveUID);
|
在使用该函数时,提供的 Stop Location(停止所需的距离) 可以通过自带的节点根据CharacterMovementComponent中的制动相关参数计算得到:
Set Playrate to Match Speed
Set Playrate to Match Speed(根据匹配速度设置播放速率),一般用在跑步走路等循环动画,用来根据真实移动速度调整动画播放速率,避免滑步;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const float AnimLength = AnimSequence->GetPlayLength(); if (!FMath::IsNearlyZero(AnimLength)) { const FVector RootMotionTranslation = AnimSequence->ExtractRootMotionFromRange(0.0f, AnimLength).GetTranslation(); const float RootMotionDistance = RootMotionTranslation.Size2D(); if (!FMath::IsNearlyZero(RootMotionDistance)) { const float AnimationSpeed = RootMotionDistance / AnimLength; float DesiredPlayRate = SpeedToMatch / AnimationSpeed; if (PlayRateClamp.X >= 0.0f && PlayRateClamp.X < PlayRateClamp.Y) { DesiredPlayRate = FMath::Clamp(DesiredPlayRate, PlayRateClamp.X, PlayRateClamp.Y); }
if (!InSequencePlayer.SetPlayRate(DesiredPlayRate)) { } } }
|
这里要注意,不要使用CharacterMovementComp中的速度值作为真实移动速度,经测试,在加速和减速过程中速度值和真实移动速度不同:
因此要重新根据位置计算一个位移速度。
跨步扭曲(Stride Warping)
朝向扭曲(Orientation Warping)
参考
UE5 骨骼动画 Lyra 距离匹配 速度匹配
Distance Matching in UE5
官方文档