All notifies firing on animation loop

Hi, thanks for reporting this. As far as I’m aware, this isn’t a bug that we’ve run into previously. There are multiple issues going on with this, which is why it’s taken a while to dig into. I can see that this is a behaviour change since 4.27 as you mentioned. And it does seem to be because Sequencer will now perform two updates to the dynamic montage when the section loops which exposes pre-existing issues with the montage code.

The specific reason that all the notifies are being sampled is because we aren’t accounting for ForcedNextFromPosition in the FAnimMontageInstance::Advance code when we sample the notifies. And this bug also appears to affect sampling of root motion. In that method, the previous update time is stored in PreviousSubStepPosition. That is always set to the update time from the previous call to FAnimMontageInstance::Advance. And although the Sequencer code is calling SetNextPositionWithEvents twice, it isn’t advancing the montage twice. So we end up in a situation where PreviousSubStepPosition is the previous update from the last frame the montage was advanced (prior to the loop) and the new update time (Position) is ForcedNextToPosition.

Unfortunately, the Montage code isn’t designed to deal with previous and current position times that have looped like this. There’s an assumption (which is true with regular montage playback code) that when the montage loops, the playback will be clamped on the final frame of the section. And then playback will start from the first frame of the section the next time that Advance is called. What Sequencer is doing (and really any calls to SetNextPositionWithEvents) can break this assumption. That causes code like FAnimMontageInstance::HandleEvents to think the montage is being played backwards (since the current position is smaller than the previous position). And that’s why you end up with all the notifies being sampled.

My current thinking is that the proper way to fix this is to have PreviousSubStepPosition in FAnimMontageInstance::Advance be set to ForcedNextFromPosition. As you’ve seen, with the two calls that Sequencer makes to SetNextPositionWithEvents when looping, that will set PreviousSubStepPosition to 0.0 when FAnimMontageInstance::Advance is called later in the frame. And that will mean that only the notifies from the start of the animation to ForcedNextToPosition are sampled.

Obviously, that still leaves a problem in that notifies at the end of the animation (the section of the delta time prior to the loop) can be missed. After some testing of the other Sequencer code path that plays animations directly on the mesh (rather than using dynamic montages) that also seems to suffer from this same problem where notifies at the end of the animation can be missed. So I think we need to do a larger piece of work at the Sequencer level to resolve that.

I’ve attached a patch with the changes that I described to change how PreviousSubStepPosition is set. Could you try integrating them to see if that at least prevents all of the notifies from being triggered?