关卡序列专栏
关卡序列(Level Sequence / Sequencer)在本项目里不只用来「播一段动画」——过场、结算演出、传送落地的角色表现都由它驱动。 序列一旦驱动角色,骨架动画的输出权会被整段借走、再归还,借和还这两端各有各的坑。 专栏按接管与归还两段组织:先讲清序列怎么拿到 Mesh 的姿态输出权,再讲收尾归还时为什么不能手写复位。
序列接管骨骼动画
序列驱动骨骼动画,做法是把 Mesh 的动画输出权整段借走,而不是在原 AnimBP 之上再叠一层。
接管发生在序列的骨骼动画轨上,链路是 NamiLevelSequenceFunctionLibrary::ApplyAnimationPoseToMesh。四步依次做完,Mesh 的姿态输出就完全交给序列:
AnimationCustomModeUAnimSequencerInstance,顶掉 AnimBP 实例AnimScriptInstance——原来的 AnimBP 实例被踢出去,不再参与姿态输出。
实例首次创建时先 SavePose() 存一份当前姿态快照,这份快照就是收尾归还时的依据;随后 ResetNodes() 把各节点权重清零,从零开始按序列驱动。SetExplicitTime)。动画推进不再由 Tick 决定,而由时间轴决定——所以它能被暂停、跳过、反向播。收尾归还与链接动画层
序列结束要把输出权还回去。业务侧手写复位会和引擎的归还机制打架,代价落在「链接动画层」上。
现象
传送过场(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 时补挂,不要指望它兜底被注销的层 |
| 日志上的「层都在」 | 对比链接层清单与角色实际表现 | 层对象存在不代表在工作;被注销的层仍会留在清单里 |