Added some distance matching functionalities.
This commit is contained in:
parent
da33f5e6a7
commit
33cfbb9b57
@ -3,6 +3,184 @@
|
|||||||
|
|
||||||
#include "Libraries/OLSLocomotionBPLibrary.h"
|
#include "Libraries/OLSLocomotionBPLibrary.h"
|
||||||
|
|
||||||
|
#include "SequencePlayerLibrary.h"
|
||||||
|
#include "Animation/AnimCurveCompressionCodec_UniformIndexable.h"
|
||||||
|
#include "Animation/AnimNode_SequencePlayer.h"
|
||||||
|
#include "AnimNodes/AnimNode_SequenceEvaluator.h"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY_STATIC(LogOLSLocomotionLibrary, Verbose, All);
|
||||||
|
|
||||||
|
float UOLSLocomotionBPLibrary::GetDistanceCurveValueAtTime(const UAnimSequenceBase* animSequence,
|
||||||
|
const float time,
|
||||||
|
const FName& curveName)
|
||||||
|
{
|
||||||
|
FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName);
|
||||||
|
if (bufferCurveAccess.IsValid())
|
||||||
|
{
|
||||||
|
const float clampedTime = FMath::Clamp(time, 0.f, animSequence->GetPlayLength());
|
||||||
|
if (animSequence->GetNumberOfSampledKeys() > 2)
|
||||||
|
{
|
||||||
|
return animSequence->EvaluateCurveData(curveName, clampedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float UOLSLocomotionBPLibrary::GetTimeAtDistance(const UAnimSequenceBase* animSequence,
|
||||||
|
const float& distance, FName curveName)
|
||||||
|
{
|
||||||
|
FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName);
|
||||||
|
if (bufferCurveAccess.IsValid())
|
||||||
|
{
|
||||||
|
const int32 numKeys = bufferCurveAccess.GetNumSamples();
|
||||||
|
if (numKeys < 2)
|
||||||
|
{
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 first = 1;
|
||||||
|
int32 last = numKeys - 1;
|
||||||
|
int32 count = last - first;
|
||||||
|
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
int32 step = count / 2;
|
||||||
|
int32 middle = first + step;
|
||||||
|
|
||||||
|
if (distance > bufferCurveAccess.GetValue(middle))
|
||||||
|
{
|
||||||
|
first = middle + 1;
|
||||||
|
count -= step + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float keyAValue = bufferCurveAccess.GetValue(first - 1);
|
||||||
|
const float keyBValue = bufferCurveAccess.GetValue(first);
|
||||||
|
const float diff = keyBValue - keyAValue;
|
||||||
|
const float alpha = !FMath::IsNearlyZero(diff) ? ((distance - keyAValue) / diff) : 0.f;
|
||||||
|
|
||||||
|
const float keyATime = bufferCurveAccess.GetTime(first - 1);
|
||||||
|
const float keyBTime = bufferCurveAccess.GetTime(first);
|
||||||
|
return FMath::Lerp(keyATime, keyBTime, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSequenceEvaluatorReference UOLSLocomotionBPLibrary::DistanceMatchToTarget(const FAnimUpdateContext& updateContext,
|
||||||
|
const FSequenceEvaluatorReference&
|
||||||
|
sequenceEvaluator,
|
||||||
|
float distanceToTarget,
|
||||||
|
bool shouldDistanceMatchStop,
|
||||||
|
float stopDistanceThreshHold,
|
||||||
|
float animEndTime,
|
||||||
|
FName curveName)
|
||||||
|
{
|
||||||
|
sequenceEvaluator.CallAnimNodeFunction<FAnimNode_SequenceEvaluator>(
|
||||||
|
TEXT("DistanceMatchToTarget"),
|
||||||
|
[updateContext,sequenceEvaluator,distanceToTarget, shouldDistanceMatchStop,stopDistanceThreshHold,animEndTime,
|
||||||
|
curveName](
|
||||||
|
FAnimNode_SequenceEvaluator& InSequenceEvaluator)
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT(
|
||||||
|
"Could not set explicit time on sequence evaluator, value is not dynamic. Set it as Always Dynamic."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
USequenceEvaluatorLibrary::AdvanceTime(updateContext, sequenceEvaluator, 1.0f);
|
||||||
|
if (animEndTime > 0)
|
||||||
|
{
|
||||||
|
const float desiredTime = FMath::Clamp(
|
||||||
|
USequenceEvaluatorLibrary::GetAccumulatedTime(sequenceEvaluator), 0, animEndTime);
|
||||||
|
USequenceEvaluatorLibrary::SetExplicitTime(sequenceEvaluator, desiredTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT("Sequence evaluator does not have an anim sequence to play."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sequenceEvaluator;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSequencePlayerReference UOLSLocomotionBPLibrary::SetPlayRateToMatchSpeed(const FSequencePlayerReference& sequencePlayer,
|
||||||
|
float speedToMatch,
|
||||||
|
FVector2D playRateClamp)
|
||||||
|
{
|
||||||
|
sequencePlayer.CallAnimNodeFunction<FAnimNode_SequencePlayer>(
|
||||||
|
TEXT("SetPlayrateToMatchSpeed"),
|
||||||
|
[speedToMatch, playRateClamp](FAnimNode_SequencePlayer& sequencePlayer)
|
||||||
|
{
|
||||||
|
if (const UAnimSequence* animSequence = Cast<UAnimSequence>(sequencePlayer.GetSequence()))
|
||||||
|
{
|
||||||
|
const float animLength = animSequence->GetPlayLength();
|
||||||
|
if (!FMath::IsNearlyZero(animLength))
|
||||||
|
{
|
||||||
|
// Calculate the speed as: (distance traveled by the animation) / (length of the animation)
|
||||||
|
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 (!sequencePlayer.SetPlayRate(desiredPlayRate))
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT(
|
||||||
|
"Could not set play rate on sequence player, value is not dynamic. Set it as Always Dynamic."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT("Unable to adjust playrate for animation with no root motion delta (%s)."),
|
||||||
|
*GetNameSafe(animSequence));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT("Unable to adjust playrate for zero length animation (%s)."),
|
||||||
|
*GetNameSafe(animSequence));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogOLSLocomotionLibrary, Warning,
|
||||||
|
TEXT("Sequence player does not have an anim sequence to play."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sequencePlayer;
|
||||||
|
}
|
||||||
|
|
||||||
FVector UOLSLocomotionBPLibrary::PredictGroundMovementStopLocation(const FVector& velocity,
|
FVector UOLSLocomotionBPLibrary::PredictGroundMovementStopLocation(const FVector& velocity,
|
||||||
bool shouldUseSeparateBrakingFriction,
|
bool shouldUseSeparateBrakingFriction,
|
||||||
float brakingFriction, float groundFriction,
|
float brakingFriction, float groundFriction,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "SequenceEvaluatorLibrary.h"
|
||||||
#include "Data/OLSEnumLibrary.h"
|
#include "Data/OLSEnumLibrary.h"
|
||||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
#include "OLSLocomotionBPLibrary.generated.h"
|
#include "OLSLocomotionBPLibrary.generated.h"
|
||||||
@ -15,8 +16,53 @@ class OLSANIMATION_API UOLSLocomotionBPLibrary : public UBlueprintFunctionLibrar
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static float GetDistanceCurveValueAtTime(const UAnimSequenceBase* animSequence, const float time, const FName& curveName);
|
||||||
|
static float GetTimeAtDistance(const UAnimSequenceBase* animSequence, const float& distance, FName curveName);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the playback time of an animation sequence to match a specified distance to a target.
|
||||||
|
*
|
||||||
|
* This function ensures that the animation sequence progresses or adjusts its time based on the distance to a target.
|
||||||
|
* It uses a distance curve to determine the appropriate time within the animation sequence that corresponds to the desired distance.
|
||||||
|
*
|
||||||
|
* @param updateContext The context for the current animation update, providing necessary time and state information.
|
||||||
|
* @param sequenceEvaluator A reference to the sequence evaluator, which controls and tracks the animation sequence being played.
|
||||||
|
* @param distanceToTarget The distance to the target that the animation should match.
|
||||||
|
* Typically, negative values indicate distance curves storing negative distance.
|
||||||
|
* @param shouldDistanceMatchStop If true, the distance matching stops once the character reaches the target or passes the threshold.
|
||||||
|
* @param stopDistanceThreshHold The distance threshold at which distance matching should stop.
|
||||||
|
* If the evaluated distance curve value exceeds this threshold, distance matching will halt.
|
||||||
|
* @param animEndTime The end time of the animation sequence. If greater than 0, the sequence will not advance past this time.
|
||||||
|
* @param curveName The name of the curve within the animation sequence that stores distance information.
|
||||||
|
* This curve is evaluated to determine the time corresponding to the distance to the target.
|
||||||
|
*
|
||||||
|
* @return An updated sequence evaluator reference, reflecting the adjusted or advanced animation time.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe))
|
||||||
|
static FSequenceEvaluatorReference DistanceMatchToTarget(const FAnimUpdateContext& updateContext,
|
||||||
|
const FSequenceEvaluatorReference& sequenceEvaluator,
|
||||||
|
float distanceToTarget,
|
||||||
|
bool shouldDistanceMatchStop, float stopDistanceThreshHold,
|
||||||
|
float animEndTime,
|
||||||
|
FName curveName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the play rate of the sequence player so that the speed of the animation matches in-game movement speed.
|
||||||
|
* While distance matching is commonly used for transition animations, cycle animations (walk, jog, etc) typically just adjust their play rate to match
|
||||||
|
* the in-game movement speed.
|
||||||
|
* This function assumes that the animation has a constant speed.
|
||||||
|
* @param sequencePlayer - The sequence player node to operate on.
|
||||||
|
* @param speedToMatch - The in-game movement speed to match. This is usually the current speed of the movement component.
|
||||||
|
* @param playRateClamp - A clamp on how much the animation's play rate can change to match the in-game movement speed. Set to (0,0) for no clamping.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe))
|
||||||
|
static FSequencePlayerReference SetPlayRateToMatchSpeed(const FSequencePlayerReference& sequencePlayer,
|
||||||
|
float speedToMatch, FVector2D playRateClamp = FVector2D(0.75f, 1.25f));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Predict where the character will stop based on its current movement properties and parameters from the movement component.
|
* Predict where the character will stop based on its current movement properties and parameters from the movement component.
|
||||||
* This uses prediction logic that is heavily tied to the UCharacterMovementComponent.
|
* This uses prediction logic that is heavily tied to the UCharacterMovementComponent.
|
||||||
@ -30,12 +76,12 @@ public:
|
|||||||
float brakingFrictionFactor, float brakingDecelerationWalking);
|
float brakingFrictionFactor, float brakingDecelerationWalking);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Predict where the character will change direction during a pivot based on its current movement properties and parameters from the movement component.
|
* Predict where the character will change direction during a pivot based on its current movement properties and parameters from the movement component.
|
||||||
* This uses prediction logic that is heavily tied to the UCharacterMovementComponent.
|
* This uses prediction logic that is heavily tied to the UCharacterMovementComponent.
|
||||||
* Each parameter corresponds to a value from the UCharacterMovementComponent with the same name.
|
* Each parameter corresponds to a value from the UCharacterMovementComponent with the same name.
|
||||||
* Because this is a thread safe function, it's recommended to populate these fields via the Property Access system.
|
* Because this is a thread safe function, it's recommended to populate these fields via the Property Access system.
|
||||||
* @return The predicted pivot position in local space to the character. The size of this vector will be the distance to the pivot.
|
* @return The predicted pivot position in local space to the character. The size of this vector will be the distance to the pivot.
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "OLS|Function Library", meta=(BlueprintThreadSafe))
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "OLS|Function Library", meta=(BlueprintThreadSafe))
|
||||||
static FVector PredictGroundMovementPivotLocation(const FVector& acceleration, const FVector& velocity,
|
static FVector PredictGroundMovementPivotLocation(const FVector& acceleration, const FVector& velocity,
|
||||||
float groundFriction);
|
float groundFriction);
|
||||||
|
Loading…
Reference in New Issue
Block a user