Been a long time coming, but here is my distance matching code. The way I used it was I put it in a BP Lib and made calls in the animBP to trigger the functions based of off anim state or state transition notifies. Use it as you will, I am not officially supporting it. The plan was to add this to my AnimWarp+IK plugin but life took a turn and I don’t have time to develop it any further for the foreseeable future.
// Copyright Patrick Stancu 2021
#include "BP_Lib_AnimWarpIK.h"
#include "Engine/Engine.h"
#include "EngineUtils.h"
void UBP_Lib_AnimWarpIK::CalcWarpFactorAndPlayRate(float CurrentMovementSpeed, const float AnimAuthordMoveSpeed, float& WarpFactor, float& PlayRate, const float WarpFactorCap, const float PlayRateRatioFaster, const float PlayRateRatioSlower)
{
float SpeedChangeRatio = FMath::GetMappedRangeValueUnclamped(FVector2D(0.0f, AnimAuthordMoveSpeed), FVector2D(0.0f, 1.0f), CurrentMovementSpeed);
float SpeedChangeReductionRatio = FMath::Abs(1.0f - SpeedChangeRatio);
//if (SpeedChangeRatio > 0)
//{
// //SpeedChangeRatio = FMath::Clamp(SpeedChangeRatio, 0.0f, WarpFactorCap);
// WarpFactor = FMath::Clamp(FMath::Clamp(SpeedChangeRatio, 0.0f, WarpFactorCap), 0.0f, WarpFactorCap);
//}
//else
//{
// //SpeedChangeRatio = FMath::Clamp(1.0f - SpeedChangeRatio - 1.0f, 0.0f, WarpFactorCap);
// WarpFactor = FMath::Clamp(1.0f - (FMath::Clamp(1.0f - SpeedChangeRatio - 1.0f, 0.0f, WarpFactorCap)) - 1.0f, 0.0f, WarpFactorCap);
//}
// if (FMath::IsNearlyEqual(WarpFactor, WarpFactorCap, 0.001f))
//{
// PlayRate = (SpeedChangeRatio - WarpFactor + 1.0f);
//}
//else
//{
// PlayRate = 1.0f;
//}
if (SpeedChangeRatio > 1.0f)
{
WarpFactor = ((SpeedChangeReductionRatio * (1.0f - PlayRateRatioFaster)) + 1.0f);
if (WarpFactor > WarpFactorCap)
{
PlayRate = ((SpeedChangeReductionRatio * PlayRateRatioFaster) + 1.0f) + (WarpFactor - WarpFactorCap);
WarpFactor = WarpFactorCap;
}
else
{
PlayRate = (SpeedChangeReductionRatio * PlayRateRatioFaster) + 1.0f;
}
}
else
{
PlayRate = 1.0f - (SpeedChangeReductionRatio * PlayRateRatioSlower);
WarpFactor = (1.0f + (SpeedChangeReductionRatio * PlayRateRatioSlower)) * SpeedChangeRatio;
}
}
void UBP_Lib_AnimWarpIK::DetermineStopLocation(FVector& StopLocation, FVector InitPos, FVector InitVel, float Friction, FVector BrakeAccel, float DeltaTime)
{
FVector PredicVel = InitVel;
FVector PredicLoc = InitPos;
float PredictTime = 0;
while (PredictTime < 2.0f)
{
PredicVel -= (Friction * PredicVel + BrakeAccel) * DeltaTime;
if (FVector::DotProduct(PredicVel, InitVel) <= 0.0f)
{
break;
}
PredicLoc += PredicVel * DeltaTime;
PredictTime += DeltaTime;
}
StopLocation = PredicLoc;
}
void UBP_Lib_AnimWarpIK::GetAnimCurveValue(float& FrameTime, UAnimSequence* AnimationSequence, FName CurveName, float CurveValue)
{
float time = 0;
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
const FName ContainerName = RetrieveContainerNameForCurve(AnimationSequence, CurveName);
if (ContainerName != NAME_None)
{
const FSmartName CurveSmartName = RetrieveSmartNameForCurve(AnimationSequence, CurveName, ContainerName);
time = AnimationSequence->EvaluateCurveData(CurveSmartName.UID, CurveValue, false);
}
FrameTime = time;
}
void UBP_Lib_AnimWarpIK::DetermineStartLocation(FVector& StartLocation, FVector InitPos, FVector InitVel, float Friction, FVector Accel, float DeltaTime)
{
FVector PredicVel = FVector::ZeroVector;
FVector CurrentVel = InitVel;
FVector PredicLoc = FVector::ZeroVector;
FVector CurrentLoc = InitPos;
float PredictTime = 0;
while (PredictTime < 2.0f)
{
PredicVel = PredicVel - (PredicVel - Accel.GetSafeNormal() * PredicVel.Size()) * FMath::Min(DeltaTime * Friction, 1.f);
PredicVel += Accel * DeltaTime;
if (PredicVel.Size() >= InitVel.Size())
{
StartLocation = InitPos + (InitVel.GetSafeNormal() * -1.0f * FVector::Dist(FVector::ZeroVector, PredicLoc));
return;
}
PredicLoc += PredicVel * DeltaTime;
PredictTime += DeltaTime;
}
StartLocation = PredicLoc;
}
void UBP_Lib_AnimWarpIK::DeterminePivotLocation(FVector& PivotLocation, FVector InitPos, FVector InitVel, float Friction, FVector Accel, float DeltaTime)
{
FVector PredicVel = InitVel;
FVector PredicLoc = InitPos;
float PredictTime = 0;
while (PredictTime < 2.0f)
{
/*Velocity = Velocity - (Velocity - AccelDir * VelSize) * FMath::Min(DeltaTime * Friction, 1.f);
Velocity += Acceleration * DeltaTime;*/
PredicVel = PredicVel - (PredicVel - Accel.GetSafeNormal() * PredicVel.Size()) * FMath::Min(DeltaTime * Friction, 1.f);
PredicVel += Accel * DeltaTime;
if (FVector::DotProduct(PredicVel, InitVel) <= 0.0f)
{
break;
}
PredicLoc += PredicVel * DeltaTime;
PredictTime += DeltaTime;
}
PivotLocation = PredicLoc;
}
//CODE TAKEN FROM: AnimationBlueprintLibrary.h
FName UBP_Lib_AnimWarpIK::RetrieveContainerNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName)
{
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
for (int32 Index = 0; Index < (int32)Enum_SmartNameContainerType::SNCT_MAX; ++Index)
{
const FSmartNameMapping* CurveMapping = AnimationSequence->GetSkeleton()->GetSmartNameContainer(SmartContainerNames[Index]);
if (CurveMapping && CurveMapping->Exists(CurveName))
{
return SmartContainerNames[Index];
}
}
return NAME_None;
}
const FName UBP_Lib_AnimWarpIK::SmartContainerNames[(int32)Enum_SmartNameContainerType::SNCT_MAX] = { USkeleton::AnimCurveMappingName, USkeleton::AnimTrackCurveMappingName };
bool UBP_Lib_AnimWarpIK::RetrieveSmartNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName, FSmartName& SmartName)
{
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
return AnimationSequence->GetSkeleton()->GetSmartNameByName(ContainerName, CurveName, SmartName);
}
FSmartName UBP_Lib_AnimWarpIK::RetrieveSmartNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName)
{
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
FSmartName SmartCurveName;
AnimationSequence->GetSkeleton()->GetSmartNameByName(ContainerName, CurveName, SmartCurveName);
return SmartCurveName;
}
//CODE TAKEN FROM:AnimationBlueprintLibrary.h