diff --git a/Content/Characters/UEFN_Mannequin/AnimationLayers/ABP_UEFN_FullBody_Base_New.uasset b/Content/Characters/UEFN_Mannequin/AnimationLayers/ABP_UEFN_FullBody_Base_New.uasset index 0e18e02..60e97f6 100644 Binary files a/Content/Characters/UEFN_Mannequin/AnimationLayers/ABP_UEFN_FullBody_Base_New.uasset and b/Content/Characters/UEFN_Mannequin/AnimationLayers/ABP_UEFN_FullBody_Base_New.uasset differ diff --git a/Plugins/Developer/RiderLink/Intermediate/FileSystemMappings.ini b/Plugins/Developer/RiderLink/Intermediate/FileSystemMappings.ini index 1dc6098..9e7418e 100644 --- a/Plugins/Developer/RiderLink/Intermediate/FileSystemMappings.ini +++ b/Plugins/Developer/RiderLink/Intermediate/FileSystemMappings.ini @@ -1,13 +1,13 @@ /Engine=C:/Program Files/Epic Games/UE_5.5/Engine/Shaders /ShaderAutogen=H:/Projects/OLS/Intermediate/ShaderAutogen /NFORDenoise=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Experimental/NFORDenoise/Shaders -/Plugin/ComputeFramework=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Runtime/ComputeFramework/Shaders -/Plugin/Runtime/HairStrands=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Runtime/HairStrands/Shaders +/NNEDenoiserShaders=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/NNE/NNEDenoiser/Shaders /Plugin/GLTFExporter=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Enterprise/GLTFExporter/Shaders /Plugin/FX/Niagara=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/FX/Niagara/Shaders /Plugin/ExrReaderShaders=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Media/ImgMedia/Shaders /Plugin/WmfMedia=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Media/WmfMedia/Shaders -/NNEDenoiserShaders=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/NNE/NNEDenoiser/Shaders +/Plugin/Runtime/HairStrands=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Runtime/HairStrands/Shaders +/Plugin/ComputeFramework=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Runtime/ComputeFramework/Shaders /Plugin/HoldoutComposite=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Experimental/Compositing/HoldoutComposite/Shaders /Plugin/Optimus=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Animation/DeformerGraph/Shaders /Plugin/Experimental/ChaosNiagara=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Experimental/ChaosNiagara/Shaders diff --git a/Saved/Autosaves/PackageRestoreData.json b/Saved/Autosaves/PackageRestoreData.json index c036c07..59996c2 100644 Binary files a/Saved/Autosaves/PackageRestoreData.json and b/Saved/Autosaves/PackageRestoreData.json differ diff --git a/Source/OLSAnimation/Private/Libraries/OLSLocomotionBPLibrary.cpp b/Source/OLSAnimation/Private/Libraries/OLSLocomotionBPLibrary.cpp index 63807b9..617625a 100644 --- a/Source/OLSAnimation/Private/Libraries/OLSLocomotionBPLibrary.cpp +++ b/Source/OLSAnimation/Private/Libraries/OLSLocomotionBPLibrary.cpp @@ -3,501 +3,8 @@ #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::FindPivotTime(const UAnimSequenceBase* animSequence, const float sampleRate) -{ - if (animSequence) - { - const float animLength = animSequence->GetPlayLength(); // Get the total duration of the animation sequence. - const float sampleDeltaTime = 1 / sampleRate; // Calculate the time interval between each sample based on the sample rate. - - float currentAnimTime = 0.f; // Initialize the current animation time. - float lastTime = 0.f; // Store the last sampled time. - float nextTime = currentAnimTime + sampleDeltaTime; // Calculate the next time point for sampling. - - // Extract and normalize the initial root motion translation vector. - FVector currentLocation = animSequence->ExtractRootMotionFromRange(currentAnimTime, nextTime) - .GetTranslation().GetSafeNormal2D(); - - while (nextTime < animLength) // Loop through the animation until the end. - { - // Extract the current rotation based on the root motion from the start to the current time. - const FRotator currentRotation = animSequence->ExtractRootMotionFromRange(0.0f, currentAnimTime) - .GetRotation().Rotator(); - - // Apply the current rotation to the translation vector and normalize. - const FVector lastLocation = currentRotation.RotateVector( - animSequence->ExtractRootMotionFromRange(currentAnimTime, nextTime) - .GetTranslation().GetSafeNormal2D()); - - // Detect a pivot point if the dot product is negative (indicating a direction change). - if ((currentLocation.Dot(lastLocation) < 0 && currentLocation.SquaredLength() > 0) || - (FMath::IsNearlyZero(lastLocation.SquaredLength()) && currentLocation.SquaredLength() > 0)) - { - return currentAnimTime; // Return the detected pivot time. - } - - // Handle the case where the current location length is nearly zero. - if (FMath::IsNearlyZero(currentLocation.Length())) - { - currentLocation = lastLocation; // Update the current location for the next iteration. - } - - // Advance to the next sample time, clamping to ensure it doesn't exceed the animation length. - lastTime = FMath::Clamp(lastTime + sampleDeltaTime, 0.0f, animLength); - currentAnimTime = FMath::Clamp(currentAnimTime + sampleDeltaTime, 0.0f, animLength); - nextTime = FMath::Clamp(nextTime + sampleDeltaTime, 0.0f, animLength); - } - } - - return 0.f; // Return 0 if no pivot is detected or if the input animation sequence is invalid. -} - -float UOLSLocomotionBPLibrary::GetCurveValueAtTime(const UAnimSequenceBase* animSequence, - const float time, - const FName& curveName) -{ - // Initialize buffer access for the specified curve in the given animation sequence. - FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName); - - // Validate that the curve data is accessible. - if (bufferCurveAccess.IsValid()) - { - // Clamp the time to ensure it's within the valid range of the animation length. - const float clampedTime = FMath::Clamp(time, 0.f, animSequence->GetPlayLength()); - - // Ensure the animation has at least 3 sampled keys for evaluation (2 keys are needed for interpolation). - if (animSequence->GetNumberOfSampledKeys() > 2) - { - // Evaluate the curve data at the specified time and return the result. - return animSequence->EvaluateCurveData(curveName, clampedTime); - } - } - - // Return 0 if the curve is invalid or the animation has insufficient sampled keys. - return 0.f; -} - -float UOLSLocomotionBPLibrary::GetTimeAtCurveValue(const UAnimSequenceBase* animSequence, - const float& curveValue, FName curveName) -{ - // Initialize buffer access for the specified curve in the given animation sequence. - FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName); - - // Validate the curve data. - if (bufferCurveAccess.IsValid()) - { - const int32 numKeys = bufferCurveAccess.GetNumSamples(); // Retrieve the total number of keyframes/samples. - - // Ensure there are at least two keyframes for interpolation. - if (numKeys < 2) - { - return 0.f; // Return 0 if not enough data points. - } - - // Initialize binary search variables. - int32 first = 1; // Start at the second keyframe. - int32 last = numKeys - 1; // Index of the last keyframe. - int32 count = last - first; // Number of keyframes to search through. - - // Perform a binary search to locate the interval containing the curve value. - while (count > 0) - { - int32 step = count / 2; // Calculate the midpoint step. - int32 middle = first + step; // Determine the middle keyframe. - - // Adjust the search range based on the target curve value. - if (curveValue > bufferCurveAccess.GetValue(middle)) - { - first = middle + 1; // Move the search to the right half. - count -= step + 1; // Update the remaining count. - } - else - { - count = step; // Narrow the search to the left half. - } - } - - // Retrieve values at the keyframes surrounding the target value. - const float keyAValue = bufferCurveAccess.GetValue(first - 1); - const float keyBValue = bufferCurveAccess.GetValue(first); - const float diff = keyBValue - keyAValue; // Calculate the difference between the values. - - // Calculate the interpolation factor (alpha) based on the target value. - const float alpha = !FMath::IsNearlyZero(diff) ? ((curveValue - keyAValue) / diff) : 0.f; - - // Retrieve the corresponding times for the surrounding keyframes. - const float keyATime = bufferCurveAccess.GetTime(first - 1); - const float keyBTime = bufferCurveAccess.GetTime(first); - - // Linearly interpolate between the keyframe times to estimate the target time. - return FMath::Lerp(keyATime, keyBTime, alpha); - } - - return 0.f; // Return 0 if the curve is invalid or the target value is not found. -} - -float UOLSLocomotionBPLibrary::GetCurveValuesRange(const UAnimSequenceBase* animSequence, const FName& curveName) -{ - // Initialize a buffer to access the curve data within the specified animation sequence. - FAnimCurveBufferAccess bufferCurveAccess(animSequence, curveName); - - // Check if the curve data is valid and accessible. - if (bufferCurveAccess.IsValid()) - { - const int32 numSamples = bufferCurveAccess.GetNumSamples(); // Get the total number of samples in the curve. - - // Ensure there are at least two samples to calculate a meaningful range. - if (numSamples >= 2) - { - // Calculate the range by subtracting the first sample value from the last sample value. - return (bufferCurveAccess.GetValue(numSamples - 1) - bufferCurveAccess.GetValue(0)); - } - } - - return 0.f; // Return 0 if the curve is invalid or does not have enough data points. -} - -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(GetCurveValuesRange(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 = GetCurveValueAtTime(animSequence, newTime, curveName); - const float distanceAfterStep = GetCurveValueAtTime(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( - 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 = 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, - float distanceToTarget, - bool shouldDistanceMatchStop, - float stopDistanceThreshHold, - float animEndTime, - FName curveName) -{ - sequenceEvaluator.CallAnimNodeFunction( - TEXT("DistanceMatchToTarget"), - [updateContext,sequenceEvaluator,distanceToTarget, shouldDistanceMatchStop,stopDistanceThreshHold,animEndTime, - curveName]( - FAnimNode_SequenceEvaluator& inSequenceEvaluator) - { - if (const UAnimSequenceBase* animSequence = inSequenceEvaluator.GetSequence()) - { - if (GetCurveValueAtTime(animSequence, - USequenceEvaluatorLibrary::GetAccumulatedTime(sequenceEvaluator), - curveName) > stopDistanceThreshHold && !shouldDistanceMatchStop) - { - const float newTime = GetTimeAtCurveValue(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( - TEXT("SetPlayrateToMatchSpeed"), - [speedToMatch, playRateClamp](FAnimNode_SequencePlayer& sequencePlayer) - { - if (const UAnimSequence* animSequence = Cast(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, - bool shouldUseSeparateBrakingFriction, - float brakingFriction, float groundFriction, - float brakingFrictionFactor, - float brakingDecelerationWalking) -{ - FVector predictedStopLocation = FVector::ZeroVector; - - // Determine the actual braking friction - float actualBrakingFriction = shouldUseSeparateBrakingFriction ? brakingFriction : groundFriction; - actualBrakingFriction = FMath::Max(0.f, actualBrakingFriction * FMath::Max(0.f, brakingFrictionFactor)); - - // Calculate 2D velocity and speed - const FVector velocity2D = FVector(velocity.X, velocity.Y, 0.f); - const float speed2D = velocity2D.Size(); - - // Check if there's movement to stop - if (speed2D > 0.f) - { - // Calculate braking divisor - const float divisor = actualBrakingFriction * speed2D + FMath::Max(0.f, brakingDecelerationWalking); - - // Check if stopping is possible - if (divisor > 0.f) - { - // Calculate time to stop - const float timeToStop = speed2D / divisor; - - // Calculate predicted stop location - predictedStopLocation = velocity2D * timeToStop + 0.5f * ((-actualBrakingFriction) * velocity2D - - brakingDecelerationWalking * velocity2D.GetSafeNormal()) * FMath::Square(timeToStop); - } - } - - return predictedStopLocation; -} - -FVector UOLSLocomotionBPLibrary::PredictGroundMovementPivotLocation(const FVector& acceleration, - const FVector& velocity, - float groundFriction) -{ - FVector predictedPivotLocation = FVector::ZeroVector; - - const FVector acceleration2D = acceleration * FVector(1.f, 1.f, 0.f); - - const float accelerationSize2D = acceleration2D.Size(); - - // Check if velocity is along the opposite direction of acceleration - if ((velocity | acceleration2D) < 0.0f) - { - const float speedAlongAcceleration = -(velocity | acceleration2D); - const float divisor = accelerationSize2D + 2.f * speedAlongAcceleration * groundFriction; - - // Check if stopping is possible - if (divisor > 0.f) - { - const float timeToDirectionChange = speedAlongAcceleration / divisor; - - // Calculate the acceleration force - const FVector accelerationForce = acceleration - (velocity - acceleration2D * velocity.Size2D()) * groundFriction; - - // Calculate the predicted pivot location - predictedPivotLocation = velocity * timeToDirectionChange + 0.5f * accelerationForce * timeToDirectionChange * timeToDirectionChange; - } - } - - return predictedPivotLocation; -} -// -// float UOLSLocomotionBPLibrary::RotationMatching(const float deltaTime, const float interpSpeed, -// const float animRotAlpha, const FVector& acceleration, -// const float targetAngle, -// FOLSRotationMatchingData& outRotationMatchingData, -// float& outTargetRotationYaw) -// { -// const float animDesiredRotation = FRotator::NormalizeAxis(targetAngle * animRotAlpha); -// const float currentAccelDir = acceleration.GetSafeNormal2D().Rotation().Yaw; -// -// outRotationMatchingData.CurrentAccelDir = FRotator::NormalizeAxis() -// const float desiredRotationChange = FRotator::NormalizeAxis(FMath::RInterpTo( -// FRotator{0.0f, 0.f, 0.0f}, -// FRotator{0.0f, AnimDesiredRotation, 0.0f}, -// DeltaTime, InterpSpeed).Yaw); -// } - EOLSCardinalDirection UOLSLocomotionBPLibrary::SelectCardinalDirectionFromAngle(float angle, float deadZone, EOLSCardinalDirection currentDirection, diff --git a/Source/OLSAnimation/Public/Libraries/OLSLocomotionBPLibrary.h b/Source/OLSAnimation/Public/Libraries/OLSLocomotionBPLibrary.h index b1d94d8..49e6876 100644 --- a/Source/OLSAnimation/Public/Libraries/OLSLocomotionBPLibrary.h +++ b/Source/OLSAnimation/Public/Libraries/OLSLocomotionBPLibrary.h @@ -16,176 +16,6 @@ UCLASS() class OLSANIMATION_API UOLSLocomotionBPLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() - -public: // ~ Helpers ~ // - - /** - * Finds the time within an animation sequence when a character's root motion changes direction, commonly known as a "pivot." - * - * @param animSequence Pointer to the animation sequence being analyzed. The function extracts root motion data from this sequence. - * @param sampleRate The frequency (in Hz) at which the animation is sampled. Higher values increase accuracy but may impact performance. - * - * @return The time (in seconds) within the animation sequence when a pivot occurs. Returns 0 if no pivot is detected or the input is invalid. - * - * @note This function is useful for identifying key moments in animations where directional changes occur, - * such as during character turns or sharp movements, ensuring smooth transitions or special handling. - */ - UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe)) - static float FindPivotTime(const UAnimSequenceBase* animSequence, const float sampleRate); - - /** - * Retrieves the value of a specified curve at a given time within an animation sequence. - * - * @param animSequence Pointer to the animation sequence containing the curve data. - * @param time The time (in seconds) at which to evaluate the curve value. - * @param curveName The name of the curve to evaluate. - * - * @return The curve value at the specified time. Returns 0 if the curve is invalid or the time is out of range. - * - * @note The time is clamped to ensure it falls within the animation's playback range, and the function requires at least 2 sampled keys in the animation for curve evaluation. - */ - static float GetCurveValueAtTime(const UAnimSequenceBase* animSequence, const float time, const FName& curveName); - - /** - * Retrieves the time within an animation sequence at which a specified curve reaches a given value. - * - * @param animSequence Pointer to the animation sequence containing the curve data. - * @param curveValue The target value to locate within the curve. - * @param curveName The name of the curve being evaluated. - * - * @return The interpolated time at which the curve reaches the specified value. Returns 0 if the curve is invalid or the value cannot be found. - * - * @note This function uses binary search to efficiently locate the curve value and linearly interpolates between keyframes for precision. - */ - UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe)) - static float GetTimeAtCurveValue(const UAnimSequenceBase* animSequence, const float& curveValue, FName curveName); - - /** - * Calculates the range of values for a specified animation curve within an animation sequence. - * - * @param animSequence Pointer to the animation sequence containing the curve. This sequence provides the curve data. - * @param curveName The name of the curve whose value range is to be calculated. - * - * @return The difference between the first and last values of the specified curve. Returns 0 if the curve is invalid or has insufficient data. - * - * @note This function is useful for determining the total change in a curve over the duration of an animation, - * which can be critical for understanding motion characteristics or driving procedural animations. - */ - static float GetCurveValuesRange(const UAnimSequenceBase* animSequence, const FName& curveName); - -public: - - /** - * Advances the animation time from the current position to a new time, ensuring the root motion covers the specified distance traveled. - * - * @param animSequence Pointer to the animation sequence being evaluated. Contains the root motion and curve data. - * @param currentTime The current time within the animation sequence, serving as the starting point for advancement. - * @param distanceTraveled The desired distance to advance within the animation, calculated based on the root motion curve. - * @param curveName The name of the curve representing root motion distance in the animation sequence. - * @param shouldAllowLooping Specifies whether the animation should loop if the new time exceeds the sequence length. - * - * @return The new animation time after advancing the desired distance along the root motion curve. - * - * @note This function ensures that the visual progression of the animation matches the actual distance traveled, - * which is particularly useful for root motion-based locomotion systems. - */ - UFUNCTION(BlueprintCallable, Category = "OLS|Function Library", meta=(BlueprintThreadSafe)) - static float GetTimeAfterDistanceTraveled(const UAnimSequenceBase* animSequence, - float currentTime, - float distanceTraveled, - FName curveName, - const bool shouldAllowLooping); - - /** - * Advances the animation time based on the distance traveled, adjusting the play rate to synchronize the animation with movement. - * - * @param outDesiredPlayRate Output parameter that will contain the calculated play rate needed to match the distance traveled. - * @param updateContext Provides context for the current animation update, including delta time and other relevant information. - * @param sequenceEvaluator Reference to the sequence evaluator managing the current animation sequence. - * @param distanceTraveled The distance covered since the last frame. This value determines how much the animation time should advance. - * @param curveName The name of the curve used for distance matching. This curve defines how the animation corresponds to distance traveled. - * @param playRateClamp Optional parameter defining the minimum and maximum play rates. Clamps the effective play rate to prevent unrealistic values. - * Default value: FVector2D(0.75f, 1.25f). - * - * @return The updated sequence evaluator with the new animation state. - */ - 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. - * - * 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. - * This uses prediction logic that is heavily tied to the UCharacterMovementComponent. - * 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. - * @return The predicted stop position in local space to the character. The size of this vector will be the distance to the stop location. - */ - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "OLS|Function Library", meta=(BlueprintThreadSafe)) - static FVector PredictGroundMovementStopLocation(const FVector& velocity, bool shouldUseSeparateBrakingFriction, - float brakingFriction, float groundFriction, - 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. - * This uses prediction logic that is heavily tied to the UCharacterMovementComponent. - * 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. - * @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)) - static FVector PredictGroundMovementPivotLocation(const FVector& acceleration, const FVector& velocity, - float groundFriction); - - // UFUNCTION(BlueprintCallable,Category = "OLS|Function Library",meta=(BlueprintThreadSafe)) - // static float RotationMatching(const float deltaTime, const float interpSpeed, const float animRotAlpha, - // const FVector& acceleration, const float targetAngle, UPARAM(ref) FOLSRotationMatchingData& outRotationMatchingData, - // UPARAM(ref) float& outTargetRotationYaw); - public: