AnimNotify
This article summarizes the source code analysis and problem summary of animation notifications (animation notification status AnimNotifyState
) in UE4 .
1. Introduction to animated notifications
Animation notifications are notifications that are mainly configured on animation ( Animation
) or animation montage ( ) in UE4 . The notification is triggered by the running of the animation. AnimMontage
There are two types of notifications:
AnimNotify
(animated notification) – no duration; onlyNotify
method; see detailsAnimNotify.h
AnimNotifyState
(Animation notification status) – has a duration (TotalDuration
); hasNotifyBegin
,NotifyTick
,NotifyEnd
method; see for detailsAnimNotifyState.h
For AnimNotifyState
, the official description says:
- It is guaranteed to start from Notify Begin Event
- It is guaranteed to end from Notify End Event
- It is guaranteed that the Notify Tick is between the Notify Begin and Notify End events.
- The order of Anim Notifies (normal or state) cannot be guaranteed . If two AnimNotifyStates are connected end to end, there is no guarantee that the End of the previous Notify is before the Begin of the next one. Only separate operations that are not related to other Notify should be performed here. original:
The order between different Anim Notifies (normal or state) is not guaranteed.
If you put two Anim Notify States next to each other, the first one is not guaranteed to end before the next one starts.
Only use this for individual actions which do not rely on other Notifies.
That is, the two AnimNotifyStates, A and B, cannot guarantee that the execution order is:
A.Begin --> A.End --> B.Begin --> B.End
Instead it might be:
A.Begin --> B.Begin --> A.End --> B.End
Additionally, it’s worth noting:
-
Animation notifications will be executed by default on the DedicateServer, the main control terminal, and the simulation segment. If you don't want to execute it on DS, you can uncheck the option below in the options.
-
For notifications on the same Montage or Animation, the pointer is the same , so if data is stored in the notification, and two identical roles release the same Montage at the same time, two notifications will modify the same piece of data;
-
Multiple notifications of the same type on the same Montage have different pointers. What comes out of GetName() is xxx_C_1, xxx_C_2 like this
2. Triggering of animated notifications
The triggering of the above methods of animation notification is mainly triggered UAnimInstance::TriggerAnimNotifies
from (there are others that will be discussed later).
2.1 TriggerAnimNotifies source code analysis
In Tick, the ( type) AnimInstance
in will add the animation notifications (AnimNotify, AnimNotifyState) of this frame , and then execute all the animation notifications of this frame in TriggerAnimNotifies. The execution order is:NotifyQueue
AnimNotifies
FAnimNotifyQueue
- all
AnimNotify
ofNotify
- All the previous ones are still there, but this frame is no longer
AnimNotifyState
thereNotifyEnd
- The previous frame is not there, it is newly added in this frame
AnimNotifyState
.NotifyBegin
AnimNotifyState
All of this frameNotifyTick
(that is, the same frame of Begin will Tick)
For AnimNotify
, there is only Notify
, so it is relatively simple and will not be discussed. Next, we AnimNotifyState
will focus on the analysis:
AnimNotifyState
the trigger core is four arrays:
ActiveAnimNotifyState
: Record the previous frame in ActiveAnimNotifyState
(referred to as Act)NotifyQueue.AnimNotifies
: Record all Notify on this frame Montage (including Notify and NotifyState)NewActiveAnimNotifyState
(Partial): Calculate which are the new NotifyStates (NewAct for short) of this frame through 1 and 2NotifyStateBeginEvent
(Partial): Only used to trigger new NotifyStateNotifyBegin
2.2 TriggerAnimNotifies complete process analysis
1. Create NewActiveAnimNotifyState
// Array that will replace the 'ActiveAnimNotifyState' at the end of this function.
TArray<FAnimNotifyEvent> NewActiveAnimNotifyState;
NewActiveAnimNotifyState.Reserve(NotifyQueue.AnimNotifies.Num());
2. The most critical step
Delete ActiveAnimNotifyState
the ones that are still there in this frame, and the remaining ones are those that were in the previous frame and are not in this frame, and are used for subsequent triggers NotifyEnd
;
put the ones that are not in the previous frame and are in this frame, NotifyStateBeginEvent
and are used in the future. Trigger NotifyBegin
;
trigger everything AnimNotify
;
// AnimNotifyState freshly added that need their 'NotifyBegin' event called.
TArray<const FAnimNotifyEvent *> NotifyStateBeginEvent;
for (int32 Index=0; Index<NotifyQueue.AnimNotifies.Num(); Index++)
{
if(const FAnimNotifyEvent* AnimNotifyEvent = NotifyQueue.AnimNotifies[Index].GetNotify())
{
// AnimNotifyState
if (AnimNotifyEvent->NotifyStateClass)
{
if (!ActiveAnimNotifyState.RemoveSingleSwap(*AnimNotifyEvent, false))
{
// Queue up calls to 'NotifyBegin', so they happen after 'NotifyEnd'.
NotifyStateBeginEvent.Add(AnimNotifyEvent);
}
NewActiveAnimNotifyState.Add(*AnimNotifyEvent);
continue;
}
// Trigger non 'state' AnimNotifies
TriggerSingleAnimNotify(AnimNotifyEvent);
}
}
3. Trigger all NotifyEnds in Act
// Send end notification to AnimNotifyState not active anymore.
for (int32 Index = 0; Index < ActiveAnimNotifyState.Num(); ++Index)
{
const FAnimNotifyEvent& AnimNotifyEvent = ActiveAnimNotifyState[Index];
if (AnimNotifyEvent.NotifyStateClass && ShouldTriggerAnimNotifyState(AnimNotifyEvent.NotifyStateClass))
{
TRACE_ANIM_NOTIFY(this, AnimNotifyEvent, End);
AnimNotifyEvent.NotifyStateClass->NotifyEnd(
SkelMeshComp,
Cast<UAnimSequenceBase>(AnimNotifyEvent.NotifyStateClass->GetOuter()));
}
// blabla ActiveAnimNotifyState 是否已经失效检查
}
4. Trigger all NotifyBegin in NotifyStateBeginEvent
// Call 'NotifyBegin' event on freshly added AnimNotifyState.
for (const FAnimNotifyEvent* AnimNotifyEvent : NotifyStateBeginEvent)
{
if (ShouldTriggerAnimNotifyState(AnimNotifyEvent->NotifyStateClass))
{
TRACE_ANIM_NOTIFY(this, *AnimNotifyEvent, Begin);
AnimNotifyEvent->NotifyStateClass->NotifyBegin(
SkelMeshComp,
Cast<UAnimSequenceBase>(AnimNotifyEvent->NotifyStateClass->GetOuter()),
AnimNotifyEvent->GetDuration());
}
}
5. MoveTemp
// Switch our arrays.
ActiveAnimNotifyState = MoveTemp(NewActiveAnimNotifyState);
Give NewAct to Act, because Act is a member variable and NewAct is a local variable.
6. Trigger all NotifyTicks in the Act
// Tick currently active AnimNotifyState
for(const FAnimNotifyEvent& AnimNotifyEvent : ActiveAnimNotifyState)
{
if (ShouldTriggerAnimNotifyState(AnimNotifyEvent.NotifyStateClass))
{
TRACE_ANIM_NOTIFY(this, AnimNotifyEvent, Tick);
AnimNotifyEvent.NotifyStateClass->NotifyTick(
SkelMeshComp,
Cast<UAnimSequenceBase>(AnimNotifyEvent.NotifyStateClass->GetOuter()),
DeltaSeconds);
}
}
Additionally, it’s worth noting:
- Since
NotifyEnd
the method of judging whether to trigger is to judge whether the previous frame of A notification is still there and this frame is not there , it will not be triggered when it looks like the last frame of the notification.NotifyEnd
That is, two notifications connected end to end will definitely The following ones Begin first, and the ones in front of the next frame are Ended. They don't even need to be connected end to end, but the beginning and the end are in the same frame (as shown below). Similarly, the following ones Begin first, and the ones in front of the next frame are Ended.
3. Summary of issues with animated notifications
See details