Added helper functions to improve searching for key frames based on distance.
This commit is contained in:
parent
33cfbb9b57
commit
f4aa7efde7
@ -72,9 +72,163 @@ float UOLSLocomotionBPLibrary::GetTimeAtDistance(const UAnimSequenceBase* animSe
|
|||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float UOLSLocomotionBPLibrary::GetDistanceRange(const UAnimSequenceBase* animSequence, const FName& curveName)
|
||||||
|
{
|
||||||
|
FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName);
|
||||||
|
if (bufferCurveAccess.IsValid())
|
||||||
|
{
|
||||||
|
const int32 numSamples = bufferCurveAccess.GetNumSamples();
|
||||||
|
if (numSamples >= 2)
|
||||||
|
{
|
||||||
|
return (bufferCurveAccess.GetValue(numSamples - 1) - bufferCurveAccess.GetValue(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float UOLSLocomotionBPLibrary::GetTimeAfterDistanceTraveled(const UAnimSequenceBase* animSequence,
|
||||||
|
float currentTime,
|
||||||
|
float distanceTraveled, FName curveName,
|
||||||
|
const bool shouldAllowLooping)
|
||||||
|
{
|
||||||
|
float newTime = currentTime;
|
||||||
|
if (!animSequence)
|
||||||
|
{
|
||||||
|
// Avoid infinite loops if the animation doesn't cover any distance.
|
||||||
|
if (!FMath::IsNearlyZero(GetDistanceRange(animSequence, curveName)))
|
||||||
|
{
|
||||||
|
float accumulatedDistance = 0.f;
|
||||||
|
|
||||||
|
const float sequenceLength = animSequence->GetPlayLength();
|
||||||
|
constexpr float stepTime = 1.f / 30.f;
|
||||||
|
|
||||||
|
// Distance Matching expects the distance curve on the animation to increase monotonically. If the curve fails to increase in value
|
||||||
|
// after a certain number of iterations, we abandon the algorithm to avoid an infinite loop.
|
||||||
|
|
||||||
|
// Traverse the distance curve, accumulating animated distance until the desired distance is reached.
|
||||||
|
while ((accumulatedDistance < distanceTraveled) && (shouldAllowLooping || (newTime + stepTime < sequenceLength)))
|
||||||
|
{
|
||||||
|
const float currentDistance = GetDistanceCurveValueAtTime(animSequence, newTime, curveName);
|
||||||
|
const float distanceAfterStep = GetDistanceCurveValueAtTime(animSequence, newTime + stepTime, curveName);
|
||||||
|
const float animationDistanceThisStep = distanceAfterStep - currentDistance;
|
||||||
|
|
||||||
|
if (!FMath::IsNearlyZero(animationDistanceThisStep))
|
||||||
|
{
|
||||||
|
// Keep advancing if the desired distance hasn't been reached.
|
||||||
|
if (accumulatedDistance + animationDistanceThisStep < distanceTraveled)
|
||||||
|
{
|
||||||
|
FAnimationRuntime::AdvanceTime(shouldAllowLooping, stepTime, newTime, sequenceLength);
|
||||||
|
accumulatedDistance += animationDistanceThisStep;
|
||||||
|
}
|
||||||
|
// Once the desired distance is passed, find the approximate time between samples where the distance will be reached.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const float DistanceAlpha = (distanceTraveled - accumulatedDistance) / animationDistanceThisStep;
|
||||||
|
FAnimationRuntime::AdvanceTime(shouldAllowLooping, DistanceAlpha * stepTime, newTime, sequenceLength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAnimationRuntime::AdvanceTime(shouldAllowLooping, stepTime, newTime, sequenceLength);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT(
|
||||||
|
"Anim sequence (%s) is missing a distance curve or doesn't cover enough distance for GetTimeAfterDistanceTraveled."
|
||||||
|
), *GetNameSafe(animSequence));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning, TEXT("Invalid AnimSequence passed to GetTimeAfterDistanceTraveled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSequenceEvaluatorReference UOLSLocomotionBPLibrary::AdvanceTimeByDistanceMatching(float& outDesiredPlayRate,
|
||||||
|
const FAnimUpdateContext& updateContext,
|
||||||
|
const FSequenceEvaluatorReference& sequenceEvaluator,
|
||||||
|
const float distanceTraveled,
|
||||||
|
const FName curveName,
|
||||||
|
const FVector2D playRateClamp /* = FVector2D(0.75f, 1.25f)*/)
|
||||||
|
{
|
||||||
|
sequenceEvaluator.CallAnimNodeFunction<FAnimNode_SequenceEvaluator>(
|
||||||
|
TEXT("AdvanceTimeByDistanceMatching"),
|
||||||
|
[&outDesiredPlayRate, updateContext, distanceTraveled, curveName, playRateClamp](
|
||||||
|
FAnimNode_SequenceEvaluator& inSequenceEvaluator)
|
||||||
|
{
|
||||||
|
if (const FAnimationUpdateContext* animationUpdateContext = updateContext.GetContext())
|
||||||
|
{
|
||||||
|
const float deltaTime = animationUpdateContext->GetDeltaTime();
|
||||||
|
|
||||||
|
if (deltaTime > 0 && distanceTraveled > 0)
|
||||||
|
{
|
||||||
|
if (const UAnimSequenceBase* animSequence = Cast<UAnimSequence>(inSequenceEvaluator.GetSequence()))
|
||||||
|
{
|
||||||
|
const float currentTime = inSequenceEvaluator.GetExplicitTime();
|
||||||
|
const float currentAssetLength = inSequenceEvaluator.GetCurrentAssetLength();
|
||||||
|
const bool shouldAllowLooping = inSequenceEvaluator.IsLooping();
|
||||||
|
float timeAfterDistanceTraveled = GetTimeAfterDistanceTraveled(
|
||||||
|
animSequence,
|
||||||
|
currentTime,
|
||||||
|
distanceTraveled,
|
||||||
|
curveName,
|
||||||
|
shouldAllowLooping);
|
||||||
|
|
||||||
|
// Calculate the effective playrate that would result from advancing the animation by the distance traveled.
|
||||||
|
// // Account for the animation looping.
|
||||||
|
if (timeAfterDistanceTraveled < currentTime)
|
||||||
|
{
|
||||||
|
timeAfterDistanceTraveled += currentAssetLength;
|
||||||
|
}
|
||||||
|
float effectivePlayRate = (timeAfterDistanceTraveled - currentTime) / deltaTime;
|
||||||
|
|
||||||
|
outDesiredPlayRate = effectivePlayRate;
|
||||||
|
// Clamp the effective play rate.
|
||||||
|
if (playRateClamp.X >= 0.f && playRateClamp.X < playRateClamp.Y)
|
||||||
|
{
|
||||||
|
effectivePlayRate = FMath::Clamp(effectivePlayRate, playRateClamp.X, playRateClamp.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance animation time by the effective play rate.
|
||||||
|
float newTime = currentTime;
|
||||||
|
FAnimationRuntime::AdvanceTime(false, effectivePlayRate * deltaTime, newTime,
|
||||||
|
currentAssetLength);
|
||||||
|
|
||||||
|
if (!inSequenceEvaluator.SetExplicitTime(newTime))
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT(
|
||||||
|
"Could not set explicit time on sequence evaluator, value is not dynamic. Set it as Always Dynamic."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning, TEXT("Sequence evaluator does not have an anim sequence to play."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT("AdvanceTimeByDistanceMatching called with invalid context"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sequenceEvaluator;
|
||||||
|
}
|
||||||
|
|
||||||
FSequenceEvaluatorReference UOLSLocomotionBPLibrary::DistanceMatchToTarget(const FAnimUpdateContext& updateContext,
|
FSequenceEvaluatorReference UOLSLocomotionBPLibrary::DistanceMatchToTarget(const FAnimUpdateContext& updateContext,
|
||||||
const FSequenceEvaluatorReference&
|
const FSequenceEvaluatorReference& sequenceEvaluator,
|
||||||
sequenceEvaluator,
|
|
||||||
float distanceToTarget,
|
float distanceToTarget,
|
||||||
bool shouldDistanceMatchStop,
|
bool shouldDistanceMatchStop,
|
||||||
float stopDistanceThreshHold,
|
float stopDistanceThreshHold,
|
||||||
@ -85,16 +239,16 @@ FSequenceEvaluatorReference UOLSLocomotionBPLibrary::DistanceMatchToTarget(const
|
|||||||
TEXT("DistanceMatchToTarget"),
|
TEXT("DistanceMatchToTarget"),
|
||||||
[updateContext,sequenceEvaluator,distanceToTarget, shouldDistanceMatchStop,stopDistanceThreshHold,animEndTime,
|
[updateContext,sequenceEvaluator,distanceToTarget, shouldDistanceMatchStop,stopDistanceThreshHold,animEndTime,
|
||||||
curveName](
|
curveName](
|
||||||
FAnimNode_SequenceEvaluator& InSequenceEvaluator)
|
FAnimNode_SequenceEvaluator& inSequenceEvaluator)
|
||||||
{
|
{
|
||||||
if (const UAnimSequenceBase* animSequence = InSequenceEvaluator.GetSequence())
|
if (const UAnimSequenceBase* animSequence = inSequenceEvaluator.GetSequence())
|
||||||
{
|
{
|
||||||
if (GetDistanceCurveValueAtTime(animSequence,
|
if (GetDistanceCurveValueAtTime(animSequence,
|
||||||
USequenceEvaluatorLibrary::GetAccumulatedTime(sequenceEvaluator),
|
USequenceEvaluatorLibrary::GetAccumulatedTime(sequenceEvaluator),
|
||||||
curveName) > stopDistanceThreshHold && !shouldDistanceMatchStop)
|
curveName) > stopDistanceThreshHold && !shouldDistanceMatchStop)
|
||||||
{
|
{
|
||||||
const float newTime = GetTimeAtDistance(animSequence, -distanceToTarget, curveName);
|
const float newTime = GetTimeAtDistance(animSequence, -distanceToTarget, curveName);
|
||||||
if (!InSequenceEvaluator.SetExplicitTime(newTime))
|
if (!inSequenceEvaluator.SetExplicitTime(newTime))
|
||||||
{
|
{
|
||||||
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
TEXT(
|
TEXT(
|
||||||
@ -219,7 +373,8 @@ FVector UOLSLocomotionBPLibrary::PredictGroundMovementStopLocation(const FVector
|
|||||||
}
|
}
|
||||||
|
|
||||||
FVector UOLSLocomotionBPLibrary::PredictGroundMovementPivotLocation(const FVector& acceleration,
|
FVector UOLSLocomotionBPLibrary::PredictGroundMovementPivotLocation(const FVector& acceleration,
|
||||||
const FVector& velocity, float groundFriction)
|
const FVector& velocity,
|
||||||
|
float groundFriction)
|
||||||
{
|
{
|
||||||
FVector predictedPivotLocation = FVector::ZeroVector;
|
FVector predictedPivotLocation = FVector::ZeroVector;
|
||||||
|
|
||||||
|
@ -20,9 +20,20 @@ private:
|
|||||||
|
|
||||||
static float GetDistanceCurveValueAtTime(const UAnimSequenceBase* animSequence, const float time, const FName& curveName);
|
static float GetDistanceCurveValueAtTime(const UAnimSequenceBase* animSequence, const float time, const FName& curveName);
|
||||||
static float GetTimeAtDistance(const UAnimSequenceBase* animSequence, const float& distance, FName curveName);
|
static float GetTimeAtDistance(const UAnimSequenceBase* animSequence, const float& distance, FName curveName);
|
||||||
|
static float GetDistanceRange(const UAnimSequenceBase* animSequence, const FName& curveName);
|
||||||
|
static float GetTimeAfterDistanceTraveled(const UAnimSequenceBase* animSequence, float currentTime, float distanceTraveled, FName curveName, const bool shouldAllowLooping);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe))
|
||||||
|
static FSequenceEvaluatorReference AdvanceTimeByDistanceMatching(float& outDesiredPlayRate,
|
||||||
|
const FAnimUpdateContext& updateContext,
|
||||||
|
const FSequenceEvaluatorReference& sequenceEvaluator,
|
||||||
|
const float distanceTraveled,
|
||||||
|
const FName curveName,
|
||||||
|
const FVector2D playRateClamp = FVector2D(0.75f, 1.25f));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the playback time of an animation sequence to match a specified distance to a target.
|
* Adjusts the playback time of an animation sequence to match a specified distance to a target.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user