Retrieve linked anim instance from which anim notify was triggered

Hi there,

We have the following setup: an anim instance containing multiple linked anim graphs, each of which plays animations that can trigger anim notifies. Is it possible to retrieve the linked anim instance playing the animation that triggered the notify from the notify itself?

In particular, I would like to know which linked anim instance triggered the anim notify inside UAnimNotify::Notify.

Thanks,

Damiano

Hi, no, there isn’t really an easy way to do that with the current notify API unfortunately. You can get the owning animation, and the owning mesh, but not the anim instance. What you could do is get the owning animation and then iterate over all the active sequence players in all of the anim instances on the mesh to find the one that’s playing your animation. But that’s not going to be a performant solution.

What did you want to do with the notifies that you identify are from a linked anim instance? It’s possible there’s another way to do what you want but I’d need to know more about the use case.

Hi, thanks for the quick response. So this is mainly for convenience, and there are other ways, but I’ll explain our use case anyway:

  • Our character can perform several actions, think of it as Fortnite emotes
  • Each action usually only influences a single part of the body
  • We have a linked anim instance for each body part containing a small state machine that plays an animation. We reuse the same anim instance for all the body parts. We filter the body parts using a blend mask.
  • Now here is the problem: our artists prefer to put all the emotes in the same animations (easier to reimport, etc) since the body parts (whose animations can be played simultaneously) are already filtered by the blend mask.
  • This worked fine until we decided to add some notifies to play sounds during these animations. As you can imagine, the sound for say the leg animation now also plays when the arm animation is being played since the blend mask does not filter anim notifies.

This is why I wanted to know the originating linked anim instance: it contains some data that would allow me to filter out notifies when they should not be triggered. I know it is convoluted, and we would probably end up splitting the animation, but we welcome any suggestions.

Thanks again,

Damiano

I see, so it sounds like although you’ve filtered out the transforms for the parts of the mesh that you aren’t interested in you still get the notifies for all of the parts of the mesh. Is that correct?

If you can make engine code changes, you could do what you want by implementing an IAnimEventsFilterContext and then injecting that into all the sampled notifies each frame via a custom IGraphMessage. If you look in FAnimNotifyEventReference, you’ll see that contains an array of IAnimNotifyEventContextDataInterface. The idea is that for each sampled notify, we can inject some form of custom data that can then be pulled out of the notify later. And you can use an IGraphMessage implementation to add the context data to the notify when it’s sampled.

A good example of this would be FBlendStackAnimEventsFilterScope and FBlendStackAnimEventsFilterContext. FBlendStackAnimEventsFilterScope is used within FAnimNode_BlendStack_Standalone::UpdateAssetPlayer so that any notify that is sampled under that point in the graph has an FBlendStackAnimEventsFilterContext added to it.

You would want to do similar, but for every notify sampled within the graph. And we do something like that with UE::Anim::FAnimSyncGroupScope which is added before the root node of the graph is updated in FAnimInstanceProxy::UpdateAnimation_WithRoot.

This code is a bit obscure so I wanted to test this out. I got something working as follows:

`class FOwningAnimGraphAnimEventsFilterContext : public IAnimEventsFilterContext
{
public:

FOwningAnimGraphAnimEventsFilterContext() {}
FOwningAnimGraphAnimEventsFilterContext(const FString& InOwningAnimInstanceName);

virtual bool ShouldFilterNotify(const FAnimNotifyEventReference& InNotifyEventRef) const override;

const FString OwningAnimInstanceName;
};

class FOwningAnimGraphAnimEventsFilterScope : public IGraphMessage
{
DECLARE_ANIMGRAPH_MESSAGE(FOwningAnimGraphAnimEventsFilterScope);

public:

FOwningAnimGraphAnimEventsFilterScope(const FString& InOwningAnimInstanceName);

virtual TUniquePtr MakeUniqueEventContextData() const override;

private:
const FString OwningAnimInstanceName;
};`And then you can use the filter scope in FAnimInstanceProxy::UpdateAnimation_WithRoot:

`// Anything syncing within this scope is subject to sync groups.
// We only enable syncing here for the main instance or post process instance
// We also fall back to enabling this sync scope if there is not one already enabled (there must always be one)
const bool bEnableSyncScope = GetAnimInstanceObject() == GetSkelMeshComponent()->GetAnimInstance() ||
GetAnimInstanceObject() == GetSkelMeshComponent()->GetPostProcessInstance() ||
InContext.GetMessageUE::Anim::FAnimSyncGroupScope() == nullptr;
UE::Anim::TOptionalScopedGraphMessageUE::Anim::FAnimSyncGroupScope Message(bEnableSyncScope, InContext, InContext);

// New code start
UE::Anim::TOptionalScopedGraphMessageUE::Anim::FOwningAnimGraphAnimEventsFilterScope OwningAnimGraphMessage(true, InContext, GetAnimInstanceName());

// update all nodes
if(InRootNode == RootNode)
{
// Call the correct override point if this is the root node
UpdateAnimationNode(InContext);`Then, if you have a natively implemented Notify you should be able to grab the context data from the FAnimNotifyEventReference that’s passed into your Received_Notify method. It’s a bit clunky, but I think this would give you what you need. And it’s a better option than iterating over all the sequence player nodes on all the anim instances to find the correct one.

Hey, thanks for the detailed reply! I’ll try that in the following days and I’ll let you know.

Kind regards,

Damiano

Tested it and it works! Thanks again for the very clear explanation

Great, thanks for letting me know it worked!