Montage 与 AnimNotify

自动关联目录:Montage 与 AnimNotify

1. 定位

Montage 解决的是"在基础动画之上叠加一段一次性动作":挥剑、受击、翻滚、处决。它是 AnimBP 之外的独立轨道,通过 Slot 混入最终姿势。

一句话定位:Montage 管"播什么、怎么打断",AnimNotify 管"播到某一点时通知代码"。两者一起构成了动画与逻辑的时间轴接口。

2. Montage 结构

Montage
 ├── Slot(决定混在哪一层,如 UpperBody / FullBody)
 ├── Section(分段:Start / Loop / End)
 │      └── Section 之间可定义过渡
 ├── 若干动画片段(可来自多个 AnimSequence)
 ├── AnimNotify / AnimNotifyState(时间轴上的标记)
 └── 曲线(Curve)
概念作用
Slot混入的位置;AnimBP 里要有对应的 Slot 节点,否则不会生效
Section分段,支持"播放到某段循环""跳到下一段"
Blend In / Blend Out混入/混出时间
Curve随动画变化的值(可用于材质、移动修正)

AnimBP 里必须有对应的 Slot 节点——这是最常见的"Montage 播了但没效果"的原因。

3. 播放与控制

// 播放
UAnimInstance* Anim = Mesh->GetAnimInstance();
float Duration = Anim->Montage_Play(Montage, /*InPlayRate=*/1.f);

// 跳到某个 Section
Anim->Montage_JumpToSection(FName("Loop"), Montage);

// 停止
Anim->Montage_Stop(0.25f, Montage);

// 查询
Anim->Montage_IsPlaying(Montage);
Anim->GetCurrentActiveMontage();
API用途
Montage_Play播放,返回时长
Montage_Stop带混出时间停止
Montage_JumpToSection跳段
Montage_SetNextSection指定下一段(做"循环直到停止")
Montage_GetPosition / SetPosition定位
Montage_GetIsStopped是否已停

"蓄力循环直到松手"的经典写法:Montage_Play 后 Montage_SetNextSection("Start", "Loop"),松手时 Montage_SetNextSection("Loop", "End")。

4. 打断与混出

机制说明
Montage_Stop(BlendOutTime)主动停止,带混出
StopAllMontages全部停止
AnimBP 的 Slot 权重权重为 0 时 Montage 不生效
同 Slot 互斥同一 Slot 上的新 Montage 会覆盖旧的
Montage_SetPlayRate变速

同一 Slot 上同时只能有一个 Montage 生效——想让上半身挥剑、下半身跑步,就要分两个 Slot。

5. AnimNotify

类型时机用途
AnimNotify一个时间点播放音效、生成特效、判定命中帧
AnimNotifyState一段时间(有 Begin/Tick/End)开启武器碰撞、开启霸体、持续特效
// C++ 实现 Notify
UCLASS()
class UAnimNotify_MyEffect : public UAnimNotify
{
    GENERATED_BODY()
public:
    virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimBase* Animation,
                        const FAnimNotifyEventReference& EventReference) override;
};

// NotifyState
UCLASS()
class UAnimNotifyState_WeaponActive : public UAnimNotifyState
{
    virtual void NotifyBegin(...) override;   // 开启碰撞
    virtual void NotifyTick(...) override;
    virtual void NotifyEnd(...) override;     // 关闭碰撞
};

武器判定的标准做法是 NotifyState:Begin 开碰撞、End 关碰撞,而不是在 Notify 里做一次射线。

6. 网络与可靠性

要点说明
Notify 不保证复制它只在播 Montage 的那端触发
服务端要表现走 GameplayCue 或 RPC
Montage 复制通过 属性复制 或 RPC 在远端播
不要在 Notify 里做权威判定应发意图给服务端

最常见的错误:在 Notify 里直接扣血。Notify 在客户端和服务端各自触发,且时机可能不同——正确做法是 Notify 里发"命中帧到了",由服务端做判定。

7. 代价与权衡

设计收益代价什么时候不该用
Montage不侵入 AnimBP 状态机需要 Slot 配套常驻循环动作用状态机更合适
多 Slot上下半身独立层数多时混出复杂简单角色一个 FullBody 即可
Notify时间轴与代码解耦不保证网络同步权威逻辑不能放这里
NotifyState有明确起止,适合开/关碰撞需要正确配对一次性事件用 Notify
Section 跳转循环/分支播放逻辑复杂简单播放不必分段

8. 踩坑与排查

坑现象怎么验证
AnimBP 里没有对应 SlotMontage 播了没效果检查 AnimBP 的 Slot 节点名
两个 Montage 互相覆盖只看到后播的确认是否同 Slot;需要则分 Slot
忘了 Montage_Stop动画停不下来检查停止路径
在 Notify 里做权威判定两端不一致应发给服务端
Notify 在服务端没触发服务端没播该 Montage需要复制播放
NotifyState 没配对碰撞一直开着确认 End 被调用(打断时也会调)
打断时状态没清理霸体残留NotifyState 的 End 里清理
Montage 播不出来Anim Class 不对 / Montage 的骨架不匹配检查骨架
混出太生硬Blend Out 时间为 0给混出时间

9. 排查顺序

1. 完全没效果?→ AnimBP 里有没有对应 Slot
2. 被覆盖了?→ 是否同 Slot
3. 停不下来?→ Montage_Stop 是否被调用
4. Notify 没触发?→ 播 Montage 的那端才触发;服务端是否需要复制播放
5. 状态残留?→ NotifyState 的 End 是否清理
6. 判定不一致?→ 权威逻辑是否写在服务端

参考