首页 / 功能模块 / 关卡序列专栏

关卡序列专栏

关卡序列(Level Sequence / Sequencer)在本项目里不只用来「播一段动画」——过场、结算演出、传送落地的角色表现都由它驱动。 序列一旦驱动角色,骨架动画的输出权会被整段借走、再归还,借和还这两端各有各的坑。 专栏按接管与归还两段组织:先讲清序列怎么拿到 Mesh 的姿态输出权,再讲收尾归还时为什么不能手写复位。

案例来源 「归还」一节来自一起线上 bug:传送过场播完后角色三段跳表现异常。 现象、根因链与三态验证见 传送过场收尾复位把角色链接动画层清掉了。
接管

序列接管骨骼动画

序列驱动骨骼动画,做法是把 Mesh 的动画输出权整段借走,而不是在原 AnimBP 之上再叠一层。

接管发生在序列的骨骼动画轨上,链路是 NamiLevelSequenceFunctionLibrary::ApplyAnimationPoseToMesh。四步依次做完,Mesh 的姿态输出就完全交给序列:

1
切到 AnimationCustomMode
序列驱动的骨架动画走自定义模式(枚举值 2)。这个模式下引擎不再让 AnimBP 产出姿态,姿态输出改由外部注入的实例负责。
2
注入 UAnimSequencerInstance,顶掉 AnimBP 实例
创建序列自己的动画实例并替换 AnimScriptInstance——原来的 AnimBP 实例被踢出去,不再参与姿态输出。 实例首次创建时先 SavePose() 存一份当前姿态快照,这份快照就是收尾归还时的依据;随后 ResetNodes() 把各节点权重清零,从零开始按序列驱动。
3
按序列时间显式求值
序列动画轨把时间直接喂给播放节点(SetExplicitTime)。动画推进不再由 Tick 决定,而由时间轴决定——所以它能被暂停、跳过、反向播。
4
手动推一次骨骼变换
求值完主动 Tick(且不发放动画通知)、刷新骨骼变换与从属组件,再把组件的渲染数据标脏。整条链路不依赖引擎常规的动画 Tick 循环。
这一段的结论 序列播放期间,角色身上那套 AnimBP(含所有链接动画层)是离线的:不是被关闭,而是被替换掉了。 所以「等序列播完,AnimBP 自己会恢复」这个假设不成立——恢复必须由序列的收尾机制显式完成。
归还

收尾归还与链接动画层

序列结束要把输出权还回去。业务侧手写复位会和引擎的归还机制打架,代价落在「链接动画层」上。

现象

传送过场(Seq3)播完、角色落地之后,三段跳的 loco 表现异常:动作不是完全不动,而是没走该走的那套链接动画层。 现场日志里 AnimationMode 是 0(蓝图模式,看着正常)、AnimScriptInstance 也在,三个链接层(三段跳 / 骨骼后处理 / 坐姿层)对象都列得出来——「层还在」不等于「层在工作」。

根因链

问题是收尾时多写了一次动画模式复位:

步骤发生的事为什么是问题
① 业务侧手写复位 序列绑定器在归还 Mesh 相对变换的同一段里,又调了一次 SetAnimationMode(AnimationBlueprint),用来「把模式还回去」。 职责和引擎的归还机制重复——序列自己本来就会还。
② 第二参默认 true SetAnimationMode(Mode, bForceInitAnimScriptInstance) 的第二参默认值是 true,含义是即使新模式和当前模式相同,也要强制重新初始化。 看着是「同值调用、应该没影响」,实际每次都会真的重建动画实例——这一步是整条链的关键。
③ 重建顺带注销链接层 重新初始化会重置链接动画实例,对由动画图表创建的那些层调用 UninitializeAnimation()。 层对象还在(所以日志上三个层都「在」),但已经不再产出姿态。
④ 没人再补挂 Locomotion 组件只在「动画层未挂载」标志为 false 时才尝试补挂;该标志在 BeginPlay 置位后不会因为这次注销而被置回。 层被注销了,补挂逻辑却认为「已经挂好了」,于是不补——异常就此固化到下一次相关重置为止。
一句话根因 序列收尾时业务侧又手写了一次 SetAnimationMode,它默认强制重新初始化动画实例,把角色链接的动画层一并注销;而补挂逻辑只在挂载标志为 false 时才生效,没人把层装回来。

正确修法

把归还交给序列本身,业务侧不要插手:删掉收尾处那次手写的 SetAnimationMode,并把序列动画节的「完成时」设为「恢复状态」。 设置完成后,引擎用接管阶段第 2 步存下的姿态快照(SavePose)把姿态还回去,链接动画层不受影响。

这两件事必须成对,三态实测对照如下:

业务侧 SetAnimationMode动画节「完成时」结果
保留保持状态(默认)链接动画层被清掉,loco 表现异常 —— 原 bug
删掉保持状态跳跃没有动作 —— 没人负责还姿态
删掉恢复状态表现正常

为什么必须改「完成时」,改播放设置没用

想通过序列的播放设置(MovieSceneSequencePlaybackSettings.FinishCompletionStateOverride)来统一控制归还行为,在这里是无效的,两点原因:

原因说明
覆盖范围只到「仍激活的 section」 播放设置里的完成状态覆盖,只作用于序列结束时仍然激活的 section;在序列结束前就已经播完的 section,完成状态由该 section 自己的设置决定。传送的动画节在序列结尾之前就结束了,所以它归自己管。
这个覆盖本来就没开 传送流程复制播放设置时已经把它显式置为 None,也就是说覆盖一直是关着的——即使它的语义合适,也不会生效。

完成状态的三个取值:KeepState(保持状态,0)/ RestoreState(恢复状态,1)/ ProjectDefault(项目默认,2)。需要归还姿态时选「恢复状态」。

速查

落地检查表

序列驱动的角色表现出问题时,按这个顺序查,先排除最上面两条。

检查项怎么看期望
动画节的「完成时」打开序列资产,选中骨骼动画节需要还姿态的选「恢复状态」,不要停在默认的「保持状态」
收尾代码有没有手写动画模式复位在序列绑定 / 收尾的代码里搜 SetAnimationMode不写。第二参默认强制初始化,同值调用也会重建实例、注销链接层
播放设置的完成状态覆盖看流程构造播放设置的地方只影响序列结束时仍激活的 section,替代不了 section 自身的设置
链接动画层补挂条件看 Locomotion 组件里补挂层的判断依据只在「未挂载」标志为 false 时补挂,不要指望它兜底被注销的层
日志上的「层都在」对比链接层清单与角色实际表现层对象存在不代表在工作;被注销的层仍会留在清单里