Removed all override functions from Epic
This commit is contained in:
parent
13a40a643d
commit
049cf53980
Binary file not shown.
@ -1,13 +1,13 @@
|
|||||||
/Engine=C:/Program Files/Epic Games/UE_5.5/Engine/Shaders
|
/Engine=C:/Program Files/Epic Games/UE_5.5/Engine/Shaders
|
||||||
/ShaderAutogen=H:/Projects/OLS/Intermediate/ShaderAutogen
|
/ShaderAutogen=H:/Projects/OLS/Intermediate/ShaderAutogen
|
||||||
/NFORDenoise=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Experimental/NFORDenoise/Shaders
|
/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
|
/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/GLTFExporter=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Enterprise/GLTFExporter/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/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/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
|
/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/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/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
|
/Plugin/Experimental/ChaosNiagara=C:/Program Files/Epic Games/UE_5.5/Engine/Plugins/Experimental/ChaosNiagara/Shaders
|
||||||
|
Binary file not shown.
@ -3,501 +3,8 @@
|
|||||||
|
|
||||||
#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);
|
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<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 = 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<FAnimNode_SequenceEvaluator>(
|
|
||||||
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<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,
|
|
||||||
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,
|
EOLSCardinalDirection UOLSLocomotionBPLibrary::SelectCardinalDirectionFromAngle(float angle,
|
||||||
float deadZone,
|
float deadZone,
|
||||||
EOLSCardinalDirection currentDirection,
|
EOLSCardinalDirection currentDirection,
|
||||||
|
@ -17,176 +17,6 @@ class OLSANIMATION_API UOLSLocomotionBPLibrary : public UBlueprintFunctionLibrar
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
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:
|
public:
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable,BlueprintPure,Category = "OLS|Function Library",meta=(BlueprintThreadSafe))
|
UFUNCTION(BlueprintCallable,BlueprintPure,Category = "OLS|Function Library",meta=(BlueprintThreadSafe))
|
||||||
|
Loading…
Reference in New Issue
Block a user