Distance Matching Locomotion : Giving it a shot!

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