Hey there,
While using Looped montage with `LayeredMove_AnimRootMotion`, I have noticed that the Root motion is not being applied to the character, and the LayeredMove itself has been removed. After some investigation, it seems like this LayeredMove isn’t supporting Looped montages at all.
Even though we can provide `->DurationMs = -1` for a looped animation, the inside of the `FLayeredMove_AnimRootMotion::GenerateMove` won’t wrap `ExtractionStartPosition` and ExtractionEndPosition`, so the LocalRootMotion is always equal to Identity after the first loop.
// Note that Montage 'position' equates to seconds when PlayRate is 1
const double SecondsSinceMontageStarted = (TimeStep.BaseSimTimeMs - StartSimTimeMs) / 1000.0;
const double ScaledSecondsSinceMontageStarted = SecondsSinceMontageStarted * MontageState.PlayRate * MontageRateScale;
const float ExtractionStartPosition = MontageState.StartingMontagePosition + ScaledSecondsSinceMontageStarted;
const float ExtractionEndPosition = ExtractionStartPosition + (DeltaSeconds * MontageState.PlayRate * MontageRateScale);
// Read the local transform directly from the montage
const FTransform LocalRootMotion = MontageState.Montage ? UMotionWarpingUtilities::ExtractRootMotionFromAnimation(MontageState.Montage, ExtractionStartPosition, ExtractionEndPosition) : FTransform::Identity;
Is there a plan to fix this LayeredMove, or should we fix it locally? I have managed to fix this issue by simply wrapping the extracted positions like so:
const float ExtractionStartPosition = MontageState.StartingMontagePosition + ScaledSecondsSinceMontageStarted;
const float ExtractionEndPosition = ExtractionStartPosition + (DeltaSeconds * MontageState.PlayRate * MontageRateScale);
const float PlayLength = MontageState.Montage ? MontageState.Montage->GetPlayLength() : 0.f;
float SampledStartPos = ExtractionStartPosition;
float SampledEndPos = ExtractionEndPosition;
if (PlayLength > UE_KINDA_SMALL_NUMBER && ExtractionStartPosition >= PlayLength)
{
SampledStartPos = FMath::Fmod(ExtractionStartPosition, PlayLength);
SampledEndPos = SampledStartPos + (DeltaSeconds * MontageState.PlayRate * MontageRateScale);
}
FTransform LocalRootMotion = FTransform::Identity;
if (MontageState.Montage)
{
if (SampledEndPos > PlayLength && PlayLength > UE_KINDA_SMALL_NUMBER)
{
// Window straddles the loop boundary: extract each half and accumulate.
const FTransform Pre = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(MontageState.Montage, SampledStartPos, PlayLength);
const FTransform Post = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(MontageState.Montage, 0.f, SampledEndPos - PlayLength);
LocalRootMotion = FTransform(Post.GetRotation() * Pre.GetRotation(), Pre.GetTranslation() + Post.GetTranslation());
}
else
{
LocalRootMotion = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(MontageState.Montage, SampledStartPos, SampledEndPos);
}
}
FMotionWarpingUpdateContext WarpingContext;
WarpingContext.Animation = MontageState.Montage;
WarpingContext.CurrentPosition = SampledEndPos;
WarpingContext.PreviousPosition = SampledStartPos;
WarpingContext.PlayRate = MontageState.PlayRate * MontageRateScale;
WarpingContext.Weight = 1.f;