AnimNotifyState 的 Trigger 核心是三个数组:
- ActiveAnimNotifyState: 记录上一帧在 Active 的 NotifyState(简称 Act)
- NotifyQueue.AnimNotifies: 记录这一帧 Montage 上所有 Notify(包括 Notify 和 NotifyState)
- NewActiveAnimNotifyState(局部变量): 通过 1 和 2 计算出有哪些是这一帧新的 NotifyState(简称 NewAct)
- NotifyStateBeginEvent(不太重要,局部):只用来触发新的 NotifyState 的 Begin
TriggerAnimNotifies
(在这里进行动画通知 AnimNotify、动画通知状态 AnimNotifyState 的 Begin、End、Tick 的触发)里的流程如下:
1. 创建 NewActiveAnimNotifyState
创建 NewActiveAnimNotifyState
并设为 NotifyQueue.AnimNotifies
的 大小(NotifyQueue.AnimNotifies
是每帧算这一帧动画上有哪些动画通知并存起来的)
// Array that will replace the 'ActiveAnimNotifyState' at the end of this function.
TArray<FAnimNotifyEvent> NewActiveAnimNotifyState;
NewActiveAnimNotifyState.Reserve(NotifyQueue.AnimNotifies.Num());
2. 添加 NotifyStateBeginEvent
// 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);
}
}
遍历这一帧所有的 Notify(从 NotifyQueue.AnimNotifies
中)
- 如果一个 NotifyState 在 Act 里(即上一帧在 Active),那就不会添加到 NotifyStateBeginEvent,反之则会添加,且把它从 Act 中移除;
- 把所有的 NotifyState 添加到 NewAct 里边
- 触发所有 non ‘state’ Notify
3. 触发 Act 里的所有 NotifyEnd
// 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. 触发 NotifyStateBeginEvent 里的所有 NotifyBegin
// 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);
把 NewAct 给 Act,因为 Act 是成员变量,NewAct 是局部变量
Note
由于在 NotifyBegin 和 NotifyEnd 之后才进行 Act 的刷新,那么如果在 NotifyBegin 和 NotifyEnd 的流程中,触发了 TriggerAnimNotifies
,即一帧内 TriggerAnimNotifies
递归调用了,那么 Act 没有刷新,还是原来的,那么原来的 Act 里的 Notify 就会再次 End,导致一个 Notify 一帧内一次 Begin,两次 End,即 End 的逻辑会触发两次。
发现这种问题出现的情况是,在 NotifyBegin里调用了:
if (GetMesh())
{
GetMesh()->TickPose(GetWorld()->GetDeltaSeconds(), false);
GetMesh()->RefreshBoneTransforms();
}
而这里会调用 TriggerAnimNotifies
导致递归调用。