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;
|
||||
}
|
||||
|
||||
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,
|
||||
const FSequenceEvaluatorReference&
|
||||
sequenceEvaluator,
|
||||
const FSequenceEvaluatorReference& sequenceEvaluator,
|
||||
float distanceToTarget,
|
||||
bool shouldDistanceMatchStop,
|
||||
float stopDistanceThreshHold,
|
||||
@ -85,16 +239,16 @@ FSequenceEvaluatorReference UOLSLocomotionBPLibrary::DistanceMatchToTarget(const
|
||||
TEXT("DistanceMatchToTarget"),
|
||||
[updateContext,sequenceEvaluator,distanceToTarget, shouldDistanceMatchStop,stopDistanceThreshHold,animEndTime,
|
||||
curveName](
|
||||
FAnimNode_SequenceEvaluator& InSequenceEvaluator)
|
||||
FAnimNode_SequenceEvaluator& inSequenceEvaluator)
|
||||
{
|
||||
if (const UAnimSequenceBase* animSequence = InSequenceEvaluator.GetSequence())
|
||||
if (const UAnimSequenceBase* animSequence = inSequenceEvaluator.GetSequence())
|
||||
{
|
||||
if (GetDistanceCurveValueAtTime(animSequence,
|
||||
USequenceEvaluatorLibrary::GetAccumulatedTime(sequenceEvaluator),
|
||||
curveName) > stopDistanceThreshHold && !shouldDistanceMatchStop)
|
||||
{
|
||||
const float newTime = GetTimeAtDistance(animSequence, -distanceToTarget, curveName);
|
||||
if (!InSequenceEvaluator.SetExplicitTime(newTime))
|
||||
if (!inSequenceEvaluator.SetExplicitTime(newTime))
|
||||
{
|
||||
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||
TEXT(
|
||||
@ -219,7 +373,8 @@ FVector UOLSLocomotionBPLibrary::PredictGroundMovementStopLocation(const FVector
|
||||
}
|
||||
|
||||
FVector UOLSLocomotionBPLibrary::PredictGroundMovementPivotLocation(const FVector& acceleration,
|
||||
const FVector& velocity, float groundFriction)
|
||||
const FVector& velocity,
|
||||
float groundFriction)
|
||||
{
|
||||
FVector predictedPivotLocation = FVector::ZeroVector;
|
||||
|
||||
|
@ -20,9 +20,20 @@ private:
|
||||
|
||||
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 GetDistanceRange(const UAnimSequenceBase* animSequence, const FName& curveName);
|
||||
static float GetTimeAfterDistanceTraveled(const UAnimSequenceBase* animSequence, float currentTime, float distanceTraveled, FName curveName, const bool shouldAllowLooping);
|
||||
|
||||
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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user