Sequencer Skeletal Animation Track with 0 weight stops and recreates dynamic montage

Hello, when we have a skeletal animation track in sequencer with a frame keyed as zero weight (such as at the start of the sequence where we are blending into the animation associated with this track), we notice that the dynamic montage is terminated. This appears to happen because of this code in AnimMontage.cpp:

void FAnimMontageInstance::Advance(float DeltaTime, struct FRootMotionMovementParams* OutRootMotionParams, bool bBlendRootMotion) { ... // If this Montage has no weight, it should be terminated. if (IsStopped() && (Blend.IsComplete())) { // nothing else to do Terminate(); return; }IsStopped is defined in AnimMontage.h

bool IsStopped() const { return Blend.GetDesiredValue() == 0.f; }

When the MovieSceneSkeletalAnimationSystem evaluates next, it attempts to set the sequencer montage position and reaches the following block of code:

`UAnimMontage* FAnimMontageInstance::SetSequencerMontagePosition(FName SlotName, UAnimInstance* AnimInstance, int32& InOutInstanceId, UAnimSequenceBase* InAnimSequence, float InFromPosition, float InToPosition, float Weight, bool bLooping, bool bInPlaying)
{
UAnimInstance* AnimInst = AnimInstance;
if (AnimInst)
{
UAnimMontage* PlayingMontage = nullptr;
FAnimMontageInstance* MontageInstanceToUpdate = AnimInst->GetMontageInstanceForID(InOutInstanceId);

if (!MontageInstanceToUpdate)
{
…`At this point the MontageInstanceToUpdate hasn’t been cleaned up yet, so it’s still != nullptr even though the underlying montage has been destroyed due to termination, so sequencer performs its updates to the MontageInstanceToUpdate (effectively not updating the montage for a frame). The next time we arrive at this function, the MontageInstanceToUpdate has been cleaned up so this code properly detects that we no longer have a valid montage and creates a new DynamicMontage in order to continue updates.

So a couple things that we see as a problem:

  • Does it make sense to terminate montages just based on a zero blend weight? Especially in the sequencer case this seems less than ideal given that it forces another dynamic montage to be created. We could ‘work around’ this by adding a slightly greater than 0 weight on our level sequence’s skeletalAnimationTracks but ideally a 0 weight would be valid.
  • In the above case, sequencer shouldn’t attempt to update a Montage Instance if its montage isn’t valid.

To get around the second issue we could change the conditional check in SetSequencerMontagePosition to be:

if (!MontageInstanceToUpdate || !MontageInstanceToUpdate->IsValid())This would ensure the new dynamic montage is created immediately instead of wasting an update. But ideally we don’t arrive at this state in the first place.

Steps to Reproduce

  • Create a level sequence
  • Add a character to the sequence that is using an Anim BP
  • Add a skeletal animation track with an animation to the character’s skeletal mesh
  • Key the weight on the first frame of the animation to 0, key it to 1 a few frames later
  • Notice in FAnimMontageInstance::Advance that the montage is terminated and that in the following FAnimMontageInstance::SetSequencerMontagePosition, the MontageInstanceToUpdate is incorrectly updated even though it lacks a valid montage

Yes I am not sure why we terminate the montage at zero blend weight. It may be best for now to unfortunately use a very small value to avoid that. Going to research why this is happening.

Runtime experts said "Possibly introducing an additional check on BlendTimeRemaining might work here, but it will be a large behavior change with fallout So I think just fudging it to 0.0001 or something may be best, maybe directly inside SetSequencerMontagePosition , add a 0.0 check to bump it to non-zero.

I am not sure, we are moving towards the new Anim Mixer system with the new Unreal Animation Framework (as talked about today at UnrealFest) so that will be the focus of development. Probably only change we would make could be to SetSequencerMontagePosition to make sure the Blend value isn’t 0.0.

Thanks, Mike - I will look at options of how we want to handle this on our side. Do you think this will be something that gets looked into as a more comprehensive change in the future? If so, would it be possible to get a ticket to track it?