HD2 飞扑与卧姿还原(下):UE5 状态执行、方向瞄准与上半身约束

角色向不同方向飞扑,触地时可以呈腹卧、侧卧或仰卧;进入地面移动后转为匍匐,停止后仍可随观察方向切换卧姿。右键瞄准改变持枪和镜头,身体仍需要保留这些方向变化。实现时,移动组件、动画状态、骨骼约束和相机分别保存自己的输入与结果,更新顺序决定了它们能否协调。

上篇《动画资源、骨架坐标与方向姿态重建》分析了飞扑与落地资源、普通卧姿的方向取样,以及坐标、根锁和上半身遮罩。本篇依据冻结的独立 UE 原型源码,解释输入处理、CharacterMovement、AnimInstance 和组件空间约束,再用当前项目的 Manny 适配录像观察另一条显示链的输出。独立原型提供具体函数与参数;当前工程则通过 DFM 姿态与握点链、重定向和显示贴地处理驱动 Manny,两者属于不同实现与构建。这项研究延续整个 AI 辅助还原系列,已有 Lyra 武器、无人机与炮台工作提供战斗接入背景,角色与这些模块的完整整合仍未完成。

源动作展示采用已确认的原始 HD2 模型 Blender 素材。UE 部分保留独立观察机位录像,并加入实际玩家 TPS 画面、同步相机记录和射击日志。观察录像用于检查动作形态,TPS 录像用于核对构图、发射与特定静态碰撞,其中仍可见上半身、接地与瞄准构图问题。后续落地持枪/瞄准偏移接续已有获认可的专项录像,修复方法在后文单独说明;其认可范围限于对应的落地过渡。

现有 Manny 材料覆盖四向飞扑、匍匐和普通/瞄准卧姿转向。部分侧卧、仰卧仍有支撑不足与躯干离地感,上半身融合和普通卧倒完整进入过渡也未通过最终验收。原有观察批次没有开火,补充 TPS 批次的实际发射与静态碰撞在后文单列。原版参考、独立原型源码与当前工程实录分别支持外观、实现和运行结果的分析,局部几何对齐与完整角色系统验收仍是不同层次的结果。

源动作展示与当前 UE 适配的区别

普通卧倒、匍匐进入与稳定匍匐的源片段均使用原始 HD2 网格和蒙皮,在 Blender 中离线播放,采用已说明的 PBR 展示材质与混凝土地面。画面未叠加最终武器层、瞄准约束或地形 IK,保留了源数据中的局部接触缺陷。

普通卧倒片段包含身体由较高姿态降低到腹卧的过程。后文独立原型只取其末端作为待机,因此不能把原型中的姿态接口等同于这里完整的进入动作。

普通卧倒源片段|原始 HD2 角色的 Blender 离线演示,1 倍速、单次播放;展示材质,保留源根,未叠加武器层与地形 IK。

匍匐进入资源与稳定循环承担不同职责。视频按非循环进入片段、稳定匍匐状态中的正向资源排列,分别展示两份资源的内容,不包含完整状态机在游戏中的连续转移。

匍匐进入源片段|原始 HD2 角色的 Blender 离线演示,1 倍速;源状态非循环,重复播放不等价于稳定匍匐状态。

稳定匍匐的正向源片段|原始 HD2 角色的 Blender 离线演示,1 倍速单次播放;所属源状态可循环,本视频未执行完整多方向混合和状态交接。

当前 Manny 适配的观察画面

当前工程的前向飞扑录像记录了 Manny 离地、触地后保留低姿,再继续匍匐并停止的过程。移动流程已经执行,身体各部位的支撑与姿态仍需分别检查。后文结合这一画面解释当前适配结果与独立原型源码的关系。

Manny 前向飞扑|当前工程的真实 UE 观察画面,随后继续匍匐并停止。视频属于 DFM→Manny 适配链,不是后文独立原型 580/330 cm/s 参数的运行验证。原速时间关系与实际采样停顿均保留。

实现版本与运行画面的对应

本文的材料来自不同实现阶段。原始 HD2 模型的 Blender 演示说明源动作,原版录像提供外观参考;独立原型源码解释状态、角度、位移和约束的具体设计;Manny 视频与实际 AnimGraph 截图展示当前项目的接入路径。它们围绕同一动作问题展开,但使用的骨架、完整代码与输入记录并不相同,也没有逐帧同步。

除标明“当前 Manny 工程”的章节与图注外,下文 Character、AnimInstance、程序瞄准节点的具体函数和数值均指冻结独立原型。尤其不能把原型的 0.4 秒保持、0.667 秒落地门,以及固定肩部偏移和胸头拟合,直接用作解释当前 Manny 每一帧的实际内部参数。

四段独立观察机位的飞扑展示剪去了开头恢复站姿的准备画面,后续起扑、接地和移动保持原速连续;原片与输入记录留存,裁切对应关系另记。其他正式卧倒、匍匐与 TPS 射击片段未作这项裁切。

原有八段 Manny 动作录像来自此次落地修复之前,由正在运行的 PIE 中独立 SceneCapture2D 观察机位采集。机位随角色平移、保持固定世界观察角度,不改变玩家相机或瞄准目标。文件为 1280×720 可变帧率,实际采样约 20—24 fps,少量停顿原样保留;它不是桌面全帧录屏,也不构成实时性能测试。

原有观察批次的移动、姿态、飞扑和 RMB 由脚本经现有 PlayerController 输入路由发送,转向由 SetControlRotation API 驱动。瞄准分组有输入事件与状态记录,不根据准星猜测。后补 TPS 批次直接采集玩家窗口,并另附最终相机数据;两者都不构成硬件鼠标映射验收。

输入、身体姿态与动作阶段的状态分工

仅用 Standing、Prone、Aiming 表示全部行为,无法清楚区分飞扑中的空中保持、落地过渡和普通匍匐。独立原型分别保存站蹲趴姿态、飞扑阶段与瞄准状态,再由动画实例读取它们的组合结果。

姿态回答角色当前采用哪种基础身体形式;飞扑阶段回答是否正在执行一次有进入和结束的动作;瞄准回答是否收到当前举枪意图。移动请求与实测速率则共同决定普通卧姿下是否进入匍匐。

状态或数据 当前持有者 下游用途
站立、蹲姿、趴姿 Character 胶囊尺寸、移动速度与基础姿态
飞扑阶段、阶段时间、触地标记 Character 起跳、空中保持、落地与结束
最近世界移动方向与输入时间 输入入口、Character 第一帧移动判定与飞扑方向
位移、重力与碰撞结果 CharacterMovement 实际位置和速度
参考朝向、过滤角度、方向权重 AnimInstance 静止侧卧/仰卧姿态取样
动作骨骼、程序胸头和手臂修正 动画评估链 最终组件空间姿态
观察输入 Controller Actor 朝向策略与相机相对方向
最终握点和相机 Pivot 骨骼完成后的表现回调 武器与镜头呈现

这些状态可以在同一帧关联,却有不同生命周期。鼠标持续右转不需要创建一项新的飞扑;飞扑落地也不应重置 Controller 的观察方向。身体姿态与瞄准正交,才能让普通卧姿和右键瞄准都使用同样的方向切换语义。

文章目录
输入、位移、姿态与相机的所有者
图 1|当前独立原型中的职责。位置由 CharacterMovement 推进,姿态由动画评估,观察输入由 Controller 持有。图不表示正式工程已经完成迁移或网络预测。

最近移动输入与实际速度的不同用途

按键刚按下时,角色可能尚未获得明显速度。仅检查实测速率会将第一帧判为静止,使方向卧姿继续参与混合。当前代码将“有最近移动请求”与“水平速率超过 3 cm/s”共同用于匍匐判定,使动画能够在速度建立之前响应移动意图。

这让状态能够响应输入意图,同时继续使用真实速度检查正在发生的运动。它不意味着两者完全等价。角色被墙挡住时可能有输入而速度接近零;松开后也可能还有残余速度。动画状态与实际位移必须分别观察,枚举值 Crawling 本身不包含本步位移结果。

输入完成入口清除请求;飞扑另外读取限定时间内的最近水平方向。这个历史状态有明确消费者,不应由每次动画更新从当前速度反推,也不应跨任意长时间保存为下一次飞扑方向。

Enhanced Input 到角色入口的数据传递

输入绑定只是入口,动作是否生效由角色函数决定。移动的 Triggered 读取二维轴,再交给 DoMove;Completed 和 Canceled 都调用移动结束入口,将两个轴清零。这样释放按键或输入被取消,都能明确撤掉最近移动请求。若只绑定持续触发而没有结束处理,最后一次方向可能一直留着,角色停止以后仍被判断为刚收到移动输入。

DoMove 先取 Controller 的 Yaw,构建水平前向和右向。这里不用 Controller 的完整俯仰,因此抬头不会让移动向量带上向上的分量。世界方向可以写成水平基向量的线性组合:

desired = controllerForwardXY * ForwardAxis
        + controllerRightXY * RightAxis

合成向量非零时,代码保存归一化后的水平世界方向,并刷新输入时间。飞扑关心这个方向,而不直接把速度向量当作按键意图。角色刚开始反向、尚未克服旧速度时,这两个量可以不同;如果从速度反推飞扑方向,按键要求后扑,实际却可能继续沿旧速度前扑。

接下来才检查飞扑阶段。如果阶段不为空,DoMove 直接返回,不向 CharacterMovement 继续 AddMovementInput;但前面保存方向与时间的步骤已经发生。这个先后关系说明“飞扑期间不能普通移动”与“完全不读取移动输入”不同。前者是本原型实际行为,后者不是。结束输入时,则直接把输入时间设为很早的无效值,后续新鲜度检查随即失效。

输入时间有两个消费者。普通卧姿下 HasRecentMoveInput 使用严格小于 0.1 秒的窗口;TryStartDive 使用严格小于 0.15 秒的窗口,再检查保存方向是否非零。这两个值不能合并成一个所谓“输入缓冲时间”。一次按住移动产生的连续刷新、松开产生的显式清除、没有再调用入口而自然超时,是不同过程。

例如,脚本最后一次非零请求后经过 0.12 秒,又未调用清零,普通卧姿的新鲜输入条件已经不成立,而飞扑方向条件仍可能成立。这只是解释两个窗口的教学工况,不意味着正常键盘释放会故意保留 0.12 秒;实际 Completed 会主动清除。自动脚本是否调用了结束入口,因而也是录像说明必须记录的内容。

瞄准入口也没有强行改变身体姿态。SetAiming 先保存新的布尔值;若当前处于飞扑阶段,就返回,不立即刷新朝向策略。动作结束时再由落地退出路径恢复对应模式。这样允许空中记录瞄准意图,但不能据此声称空中持枪、开火和所有姿态层已经接通。这些下游还需要真实的动画图与武器链来消费。

动画状态与运行数据的生命周期

动画枚举由采集条件推导,不独立推进全功能任务树。Crawling 表示动画逻辑选中了匍匐分支,并不保存路径或拥有物理位移。Lying 表示当前目标权重符合方向姿态分支的选择条件,而当前混合权重可能仍未达到一。调试记录应同时保存枚举与最终 Alpha,区分分支选择和实际混合进度。

Stance=Prone 与 DivePhase=Landing 在转换附近可能出现不同组合。普通卧姿资格还要求飞扑阶段为空,避免身体字段先变为卧姿时提前跳过落地动作。若统一成单个 Prone=true,调用点仍需额外判断飞扑阶段,状态关系反而会分散到更多位置。

上篇资源与本篇运行时的逐项对应

上篇展示了动作资源的内容,下篇需要继续说明资源进入了哪个播放器、是否经过修改,以及由什么条件选择输出。下表限定于本文分析的旧独立原型快照,不将其配置视为后续 Manny 工程的现状。相同动作名称只提供查找线索;资源已经存在、工作副本已经生成和运行时完整采用原版交接,是不同结果。

上篇的动作与素材 旧独立原型快照怎样使用 本篇对应说明 尚未闭合的部分
飞扑空中与落地:分别有源动画、方向资源图 空中与落地分别使用 BS_Dive、BS_LandDive,读取同一个飞扑方向参数 飞扑阶段、LaunchCharacter、空中保持与触地退出 原版精确混合权重、滑行和落地动力学;Manny 接地与上半身仍需验收
普通卧倒进入:身体降低到腹卧的非循环片段 构建器从源末端取样,冻结为普通卧姿 Idle,放在速度为零的样本行 SetStance 的胶囊与 Mesh 基准,以及普通 Prone 的选择 不能称整段卧倒进入动作已在运行时播放
匍匐进入:上篇的非循环正向进入片段 原型构建器的速度行包含已确认的进入资源工作副本 近期移动输入、实际速度和 BlendSpace 选择 原版进入动作到稳定循环的事件交接没有等价接通
稳定匍匐:上篇单独展示了循环状态中的源片段 不能仅凭速度行中的资源名或播放器循环设置认定已采用同一片段 本文保留原型速度混合的实际做法 旧快照采用的进入资源与上篇稳定循环不能互换身份
侧卧/仰卧:同一方向资源的时间扫描与姿态取样 SequenceEvaluator 读取方向参数对应的显式时间,再与基础姿态混合 ReferenceYaw、周期角差、ExplicitTime 和 Alpha 最终持枪与分层约束的 UE 画面对照仍待补
上篇资源、UE工作副本与运行时用途的逐项对应
图 2|实线连接已确认的资源用途,虚线标出尚未闭合的运行时关系。普通卧倒的末端冻结、匍匐进入资源与稳定循环分别保留身份;同名或同样能循环播放不作为等价证据。

普通卧倒进入与末端 Idle 的区别

上篇的普通 prone 片段包含身体降低过程。当前构建器对它执行末端冻结,为每根骨写入保持姿态;普通移动 BlendSpace 的零速行使用的是这个派生 Idle。因此源码可以解释角色处于卧姿后怎样保持腹卧,却不能证明按下卧倒指令时完整播放了上篇的进入动作。

SetStance 负责切换姿态字段、胶囊半高、Mesh 高度和朝向策略,飞扑则有单独的 Diving / Landing 阶段。飞扑触地后的落地片段不能代替普通站立或蹲姿进入腹卧的动作。这两条进入路径应分别记录;后续若补普通卧倒过渡,需要另行定义进入、完成、取消以及原有移动输入如何交接。

匍匐进入资源与稳定循环的对应缺口

这一处已有明确的资源身份可对照。上篇已核对的正向进入片段是非循环状态的资源;当前普通卧姿构建器采用的一个正向工作片段,正是由这份进入资源派生。对应关系由完整资源清单到 UE 资产路径建立,不仅依据文件名末尾字符。

上篇稳定循环演示使用另一份已确认资源。因此,即使原型在 150 cm/s 样本行反复播放并显示爬行动作,这种重复播放仍不足以确认原版稳定循环已恢复。需要继续核对原型速度行的每个源资源、非循环进入状态的结束事件,以及后续循环播放器。本文保留旧独立原型实际采用的方式,不改变上篇对原资源的分类,旧快照中的问题与后续目标工程分别核对。

后续 Manny 工程的配置修正

后续交接已核对 Manny/DFM 目标工程的资源表及 BlendSpace 样本:基础与步枪匍匐分别引用稳定匍匐资源组。这与上面旧独立原型把进入片段放入速度行的做法不同,当前主工程的配置以这份核对结果为准。

新增 Manny 实录已经保留持续匍匐、移动停止及方向姿态变化,实际图也明确了重定向资产和显示节点。内部逐帧样本权重仍未导出,普通卧倒的完整进入过渡也尚未确认。源资源、作者配置、可见行为和内部取样各自说明不同范围,不用其中一项替代其他检查。

飞扑阶段与 CharacterMovement 位移执行

TryStartDive 首先检查当前没有飞扑、移动组件认为角色在地面,而且姿态不是 Prone。已经趴着或空中重复进入都会被拒绝。通过检查后,若 0.15 秒内存在有效水平移动输入,就取最近方向;否则使用 Actor 当前前向。

方向先转成相对 Actor 的水平角,再映射到混合参数:

relativeYaw = shortestAngle(actorYaw, directionYaw)
diveDirection = (relativeYaw + 180 degrees) / 360 degrees

这组原型坐标中,后方落在 0/1 接缝,左方为 0.25,前方为 0.5,右方为 0.75。它是该飞扑混合输入的约定,更换骨架时需要重新核对前向轴。

进入 Diving 时清阶段时间和触地标记,关闭普通的朝移动方向转向与 ControllerDesiredRotation,再调用 LaunchCharacter。当前默认初始水平速度 580 cm/s,向上速度 330 cm/s,XY 与 Z 都采用覆盖方式。之后位置由 CharacterMovement 的重力和碰撞更新。

动画根位移不再作为第二个世界位移来源,但骨骼局部运动仍保留。身体可以在胶囊内部前倾、伸展和收腿,角色世界位置由 CharacterMovement 推进。如果同时累加同一段根轨道,飞扑距离会被重复计算,碰撞位置也会偏离可见落点。

飞扑进入、空中保持、触地与落地
图 3|原型飞扑阶段及参数。轨迹只作教学示意,不根据 580/330 推导原版飞行距离,也不代表原游戏逐帧动力学。

空中动画保持与持续运动

动画实例检查飞扑阶段的计时,达到约 0.4 秒的配置门限后,将 DivePosePlayRate 设为零以保持姿势。门限消费的是角色阶段时间,不是对源片段精确查询第 0.4 秒。这个动作只停止对应播放器的时间,胶囊速度、重力和场景碰撞仍继续更新。

保持空中姿态是为避免片段继续播放到不适合悬空的末段,并不等于原版采用了同一时间门。0.4 秒是当前原型配置。场景重力、边缘落空和更长滞空都可能改变合适的动作组织,不能只靠延长定格时间宣布解决全部空中问题。

落地从 Landed 事件或阶段内的地面条件进入。后者要求 Diving 已经过 0.1 秒且移动组件报告在地面,用来处理起跳或接触阶段的边界。进入 Landing 时重置阶段时间;确认触地则 StopMovementImmediately。

Landing 至少持续当前配置约 0.667 秒,并确认地面接触后,才结束飞扑阶段并转为 Prone。这里直接停止移动是独立原型的简化,不是已经恢复原版滑行摩擦、接地冲量或全部地形响应。飞扑位移能运行与原版落地动力学等价,需要分开评价。

飞扑请求、阶段转换与退出条件

TryStartDive 返回 true 时,完成的是这一次启动请求的资格检查与状态写入。它没有等待本次腾空结束,更不保证一定落在同高平地。进入时先记录方向参数、重置阶段时间和接触标记,再关闭普通朝向策略,最后提交带覆盖标志的 LaunchCharacter。XY 覆盖意味着旧水平速度不会被直接加到这次 580 cm/s 上;Z 覆盖也不会把旧上升速度与 330 cm/s 简单相加。

LaunchCharacter 之后,位移进入 CharacterMovement 的执行流程。动画只读取当前飞扑阶段与方向,不按片段进度再给 Actor 叠加同一份平移。若在资源中保留了骨盆局部前倾,身体相对胶囊仍会变化,这与世界位移只有一个拥有者并不冲突。需要分别检查 Actor 轨迹和骨骼相对轨迹,两类运动需要分别配置,不能由同一个“根不动”选项一并删除。

角色 Tick 对阶段时间累加实际 DeltaSeconds。Diving 中的地面后备检查采用严格的 elapsed > 0.1;Landed 回调则可直接触发 Landing。两条入口最终都调用 BeginDiveLanding,该函数先确认当前仍为 Diving,避免接触被重复报告时再次重置 Landing 计时。

Landing 的时间从转换时重新开始计算,而不是继续沿用离地后的总时间。触地标记为真时,当前原型立即清速度;后续若仍未确认触地,又遇到地面条件,也会补上接触标记和清速度。只有接触成立且 Landing 时间达到配置时长,才把飞扑阶段清空、设置 Prone 并刷新朝向模式。不能仅用动画播放完毕替代这两个条件。

这也决定了帧步对边界的影响。若配置门为 0.667 秒,在离散更新中,实际退出发生在第一次检查到时间已经达到门的那一轮,而不一定存在一个恰好 0.667 秒的渲染帧。动画画面、角色 Tick、移动接触和截图请求又可能处于不同阶段,所以测量时需要看事件记录和对应输入,不能只从视频帧号反推出精确逻辑门。

空中 0.4 秒保持只由动画实例把对应播放速率改成零,并未创建一个“物理冻结”状态。可以做一个最小验证:时间超过保持门后,记录动画取样时间与 Actor Z。前者不再按原速推进,后者仍随运动更新变化,才能证明这两个消费者分开。若测试只看身体定格,无法排除胶囊也被错误暂停的情况。

角色始终未触地时,现有源码没有通过最大飞扑总时长自动结束动作。这份原型面向平地可控场景,深坑、碰撞缺失、传送或非正常移动模式的恢复尚未完整处理。正式接入需要为这些情况定义退出原因,目前它们仍属于待补设计。

当前 Manny 工程中的方向飞扑

前向飞扑之后,后向与左右侧扑的录像分别展示离地、触地后的身体朝向,以及继续移动时进入腹卧匍匐的过程。各片段均来自当前项目的实际输入与移动流程,起扑到后续移动保持连续,没有由静态姿态拼接。

Manny 后向飞扑|后向输入与飞扑请求经现有输入路由触发。接地后仍保留仰卧方向姿态,再通过移动进入腹卧匍匐;这段后扑并未先强制转为腹卧。

Manny 左向飞扑|观察侧向展开、接地姿态及随后匍匐。部分躯干支撑与手臂、枪械关系仍需修正,材料用于说明当前行为,不作为最终接地或握枪验收。

Manny 右向飞扑|与左向分别录制,保留当前落地与移动接续。画面边缘的测试构件未遮住主要动作,不将其用作复杂障碍场景验收。

对应运行记录确实出现了移动系统的飞扑阶段和地面状态变化,可用于检查角色移动状态经历了腾空与接地阶段。地面状态属于移动组件,画面中的皮肤、肩部和躯干则可能仍悬空或支撑不足。二者不能互相替代,尤其胶囊接地结果没有覆盖全身接触。

胶囊半高与 Mesh 高度的共同基准

站立与卧姿切换改变的不只是动画权重。SetStance 根据姿态选择目标胶囊半高,并用胶囊半径作为下限。以构造值半径 42 cm 为例,即使某个姿态配置了更小半高,实际也不会得到一根比半径还矮的胶囊。这里半径是几何约束,不是身体卧倒厚度的完整物理模型。

目标半高改变后,代码计算新旧差值,将 Actor 的世界 Z 移动同样的量,再把 Mesh 相对 Z 设为负的新半高。可以用底部基准检查这个设计:胶囊中心减去半高表示底部,中心与半高同时减少相同值,底部在理想平地条件下保持。Mesh 的相对偏移则继续以这个底部作为可见身体的放置基准。

deltaHeight = newHalfHeight - oldHalfHeight
actorZ_new = actorZ_old + deltaHeight
meshRelativeZ_new = -newHalfHeight
bottom_new = actorZ_new - newHalfHeight

这个算式解释基准保持,不表示原型已经执行完整净空检测。当前切换是开阔地直接调整,没有先对起身高度做阻挡查询。角色在低顶下能不能站起来、身体横向展开会不会穿墙,也没有由一个竖直胶囊自动解决。后面的脚部整套抬升又属于骨骼修正,它也未提供胶囊调整失败后的通用恢复路径。

胶囊半高变化时的共同底部基准
图 4|中心与半高同步改变时,理想平地上的底部位置保持。图中仍是竖直胶囊;人体横向展开后的碰撞与起身净空需要另外处理。

移动、静止方向姿态与瞄准状态的组合

普通卧姿条件是 Stance=Prone 且飞扑阶段为空。随后移动请求或实际水平速度决定 Crawling。无移动时,方向姿态根据观察角产生权重;右键只使动画枚举进入相应 Aim 变体。

因此同样的侧卧可以存在于 Lying 与 LyingAim,匍匐也可以存在于 Crawling 与 CrawlingAim。这样松开瞄准不会突然要求身体翻回某个固定姿势,按下瞄准也不会把所有趴姿锁成仰卧。

动作状态仍需按优先级判断:飞扑和落地先于普通卧姿,趴姿外回到非活动分支;趴姿内先看移动,再看静止方向姿态是否需要参与,最后再区分瞄准。仅在图中添加一条“右键→仰卧”虽然容易演示,语义却已经偏离这里的目标。

当前 Manny 的匍匐与移动停止

普通姿态切换录像包含 Z 输入进入卧姿、持续向前匍匐和松开移动后的停止。当前姿态变化较快,可确认的是输入入口与移动/停止交接,完整的普通卧倒进入动画仍未恢复。

Manny 卧姿与持续匍匐|普通 Z 输入切换姿态,随后向前移动并停止。没有用脚本 SetStance 或传送伪造动作;当前快速姿态变化仍不作为完整卧倒进入或起身过渡的证据。

方向匍匐录像分别保留前、后、左、右输入、身体的实际位移,以及每次松开后的停止。这些画面与输入记录共同用于核对移动结果,动画状态名称本身只说明所选分支。

Manny 方向匍匐|逐项输入 W/S/A/D 并释放,保留各方向移动与停止。展示范围为这些独立方向和当前平地场景,不扩大为全部方向、复杂地形或原版完整进入/退出状态机。

当前资源表与 BlendSpace 样本已核对到稳定匍匐资源组;视频也出现持续动作和真实位移。但本轮没有逐帧导出 NativeDFM 节点内部的实际样本权重,仍不能用“资源已加载”替代内部每帧消费检查。旧 AnimInstance 的部分诊断字段为 None,是因为当前路径由自定义节点读取数据,不能仅据此判断整个动作没有加载。

静止卧姿的参考朝向与 Actor 跟转

若水平瞄准角始终按 ControllerYaw−ActorYaw 计算,而 Actor 持续跟随 Controller,这个角差会不断趋近零。结果是观察方向已经明显改变,动画仍在正前方附近取样,难以稳定经过侧卧和仰卧。

当前实现为静止卧姿保存 ProneReferenceYaw。它描述本次静止方向姿态采用的进入参考,不随 Actor 每一步追视角立即重写。观察方向先转换到该参考下,再求水平和垂直角。

localAim = inverse(referenceYawBasis) × controlForward
yaw   = atan2(localAim.y, localAim.x)
pitch = asin(clamp(localAim.z, -1, 1))

这里的乘法是方向基变换说明,源码采用 FRotator 的 UnrotateVector。水平和垂直角属于姿态输入;武器使用的空间目标点还会由相机射线另行取得。

静止参考、Actor 朝向与方向采样
图 5|固定进入参考避免 Actor 跟转抵消相对方向角。Actor、参考朝向与已包含转体的源姿态分别处理,不能再按头部方向把整套 Mesh 转一遍。

移动混出与停止后的参考重建

第一次进入卧姿会建立参考。移动时方向姿态目标权重转为零,待混出后可以跟随当前 Actor 朝向更新参考;从匍匐转回静止时,也使用当前朝向重设。这样下一次停止从新的身体方向开始,旧静止姿态的参考不会跨越整段匍匐永久保留。

这里需要保留参考重建的时机。过早重设参考,会让方向姿态在还未混出时突然变回中性;永不重设,则会让人物爬行到别处后还围绕第一次趴下的朝向翻身。源码保留了“开始移动并混出”与“移动结束”两个时机,状态测试应分别覆盖。

动画实例还根据 Actor 与参考的角差计算 PoseYaw 补偿,并乘当前方向姿态权重。其目的在于让源片段内已经编码的转体仍在合适参考下显示,而非重新添加一次鼠标转角。源动画、Actor 追转和组件空间补偿同时存在,不能只看最终头向就反推是哪一层正确。

普通卧姿转向需要同时检查参考朝向、方向时间与最终姿态。上篇的 Blender 方向资源扫描展示了同一片段中的腹卧、侧卧和仰卧,用于辨认方向参数对应的源姿态。扫描没有运行 Actor 跟转、参考重建或持枪约束,这些运行时关系仍由后文源码和 UE 记录分别说明。

方向资源取样|原始 HD2 网格与蒙皮,使用已确认的展示材质。正向、反向扫描和端点停留展示源时间与姿态的对应;无持枪层、程序约束或地形 IK,不用于测量 UE 响应速度。

同一源资源的仰卧、侧卧与腹卧采样
图 6|同一片段在 0、0.25、0.5 秒的离线取样。沿用上篇源根与统一地面,局部穿地仍保留;这组图用于对照姿态形态,不是运行时参考朝向实验。

当前 Manny 的普通与瞄准转向

非瞄准录像中,角色连续经过不同卧姿方向,再反向转回,随后移动退出、停止并再次转向。它记录当前适配输出;前面的 Blender 扫描记录源片段形态,两者的用途不同,也未同步输入。

Manny 非瞄准卧姿转向|控制器方向通过 API 连续改变,随后移动、停止并再次转向。未按 RMB;部分侧卧与仰卧仍有离地感,不能称地面支撑已经修好。

瞄准录像持续按住 RMB,末段包含向上和向下的观察方向变化。它来自另一场 PIE,瞄准条件由输入与状态记录确认,与非瞄准录像属于独立用例。

Manny 瞄准卧姿转向|RMB 经输入路由触发,控制器转向后接移动、停止及俯仰变化。只展示持枪/瞄准姿态,没有按下开火;不能据此声明枪口精确约束、弹道或伤害通过。

两段都采用独立观察机位。玩家相机的距离、跟随和肩侧偏没有在此直接展示,因此后文的相机源码关系仍应作为独立设计分析,不能用这两段旁观录像验证玩家最终构图。

前一状态对参考朝向更新的影响

NativeUpdateAnimation 中,ProneReferenceYaw 不是在每帧直接覆盖。第一次没有保留卧姿参考时,它会根据此前的动画状态选择 Actor 或控制方向;已进入后,移动时要等方向姿态 Alpha 混到很小才重新跟随 Actor,或者在从匍匐回到静止的那个边界重建。这里的“此前”必须保留,因为本轮新的动画枚举是在后面才计算和写入。

因此更新顺序会影响进入基准。若重构时先把新状态写入 ProneAnimState,再执行原来读取旧状态的条件,看似只是整理代码,实际可能改变首次进入时保存哪一个角。记录日志时最好同时保存前一状态和本轮目标状态,而不是更新完只留一个最终枚举。

可以用一个教学转向过程理解参考的价值。假定参考朝向保持为世界 0°,Controller 已转到 90°,Actor 也已追到 80°。以 Actor 为参考只能得到 10°,很快又会收敛到零;以保留的进入参考计算则仍得到 90°。方向资源因此能稳定取到侧向姿态,而 Actor 的真实朝向仍服务碰撞、移动和其他组件。

接下来 PoseYawDegrees 读取 Actor 到参考的角差,再乘当前方向 Alpha。它补偿的是显示所需的参考关系,不是再做一次 ControllerYaw 的累积。将它误写成“把鼠标转角乘权重再加到整套身体”,会与源片段内部转体叠加。若更换 Mesh 的参考轴,还要重新检查这一角差通过组件空间 Up 轴应用后,是否仍维持相同世界参考。

移动混出期间,动画状态可能已经变为 Crawling,而方向姿态的当前 Alpha 尚未降至零。这个重叠来自连续权重过渡,无需强制清零。应检查的是参考是否过早重建、下游是否继续施加旧 PoseYaw,以及双臂约束是否按自身条件退出。

周期角差、方向取样与背后接缝

求出角度后,原型采用最短角差限步。已有局部原版证据对应这个角差更新方法,但上游坐标与整帧调度仍需单独确认:

delta = shortestAngularDelta(currentAngle, targetAngle)
next = unwind(currentAngle + clamp(delta, -10×dt, 10×dt))
horizontal = fmod(1.5 + filteredYaw/(2*pi), 1)
vertical   = 0.5 + filteredPitch/(2*pi)

限步速度在这里是 10 rad/s。它不等于每秒转十度。以 dt=1/60 s 为教学步长,单步最大变化约 0.1667 rad,即 9.55°。限步发生在周期角度空间,因此从接近 +180° 到接近 −180° 应走短路,不能在归一化参数里直接从 0.99 线性走到 0.01。

水平映射的前方为 0.5,正负 90° 分别为 0.75 与 0.25,背后在 0/1 接缝。俯仰 ±30° 对应约 0.58333 与 0.41667。这些局部算例检查坐标输入下的映射关系,并没有证明方向片段所有帧、左右侧卧和原版全身输出已经一致。

归一化 horizontal 在当前图里用作约一秒方向序列的 ExplicitTime。这个巧合不应变成“所有片段都直接把 0..1 当秒”。若未来更换不同时长片段,应明确它要求归一化相位还是绝对时间,再决定是否乘时长。图中参数与资源时长之间有消费者契约,不能只看数值范围相似。

角度循环与方向姿态时间参数
图 7|前、侧、后的水平映射与最短角差。接缝代表周期等价,不代表有一段完整新动作;右侧算例只解释该输入约定。

方向姿态的目标权重与混合响应

静止方向目标权重由相对水平角构造:

x = clamp(abs(yawDegrees) / 45, 0, 1)
targetWeight = stationaryProne ? x*x*(3-2*x) : 0
poseAlpha = constantRateApproach(poseAlpha, targetWeight, 5/s, dt)

前方仍由腹部朝下的基础姿态负责,转角增大时方向姿态逐渐参与。45° 是原型编排参数,不能写成原版进入侧卧的阈值,也不表示片段采样在 45° 后停止变化。权重封顶后,horizontal 仍随观察方向沿序列变化。

目标权重由角度即时确定,当前 poseAlpha 则以每秒 5 的常速接近目标。出现移动请求后,目标转为零,已混入的方向姿态仍需时间退出。若从零持续向一推进,名义时间为 0.2 秒;实际过程取决于起点、中断与帧步。

上半身的 GripAlpha 又按另一条件与速率更新。它不应因为变量名都带 Alpha 就与方向姿态权重合并。一个负责全身方向姿态,另一个决定程序持枪约束参与程度,各自有独立输入与下游。

方向目标权重与当前混合权重
图 8|按原型公式绘制目标权重与常速 Alpha 响应。右侧假定从零开始且目标持续为一;不是录像中的实测响应。左图权重在 45° 封顶,不代表方向时间取样随之停止,也不代表原版切换阈值。

输入条件、目标权重与动画状态的独立记录

在同一次更新中,方向姿态可能已经被要求退出,而当前 Alpha 尚未降到零。日志和图表因此需要分别记录输入条件、目标权重、实际权重和最终选择的动画分支。例如持续匍匐时,方向目标权重是零;若刚从仰卧开始移动,输出还会短暂保留正在混出的方向姿态。这是连续混合的结果,并不表示角色又收到一条静止指令。

持枪权重有独立的启用条件:普通静止卧姿和瞄准匍匐可以要求程序握枪参与,非瞄准匍匐则向零退出。程序节点的头部操作位于 GripAlpha 提前返回之前,因此关闭手臂持枪约束后,头部修正仍可能执行。分层诊断应按实际执行语句划分,避免仅用一个“持枪关闭”标签概括整个节点。

目标角、取样时间与混合权重的更新顺序

当前逻辑至少会经过目标角、限步后的角、归一化时间、方向目标权重和当前混合 Alpha。它们描述不同阶段,不能都叫“瞄准角”。例如观察方向一次跳到很远,目标角可以立即改变,限步角仍以每秒十弧度追赶;时间参数跟随限步角,而当前 Alpha 又用另一常速接近目标权重。最终身体变化因此不是只由某个角度函数决定。

对 22.5°的教学输入,除以 45°得到 0.5,smoothstep 的目标权重也是 0.5;若 Alpha 从零起步,在 60 Hz 下每步名义增加约 0.08333,需要若干步才能到达目标。45°及更大角度使目标权重封顶,但源时间仍会继续扫描。只把 Alpha 曲线画出来,无法解释角色在同样满权重下为什么还会从侧卧转到仰卧。

GripAlpha 的目标条件则是普通卧姿已经成立,且处于瞄准或当前没有匍匐。也就是说,普通静止卧姿可以保留持枪约束,普通非瞄准匍匐则会使它向零退出;瞄准匍匐仍可要求它参与。更新速率为每秒八,与方向姿态每秒五不同。这里的每秒八描述权重变化速率,不是武器转速,也不能与后坐力或相机跟随系数混为一个 Handling 参数。

最短角差与归一化时间还应使用不同测试。可以让当前角接近 +179°,目标角接近 −179°,确认限步走约两度的短路;随后单独观察归一化时间经过 0/1 时,源片段两端和混合输出是否连续。前一项算术成立,不能替代后一项动画接缝验收,因为源资源自身也可能存在端点差异。

帧步变化时,常速接近按本轮 dt 决定允许步长,而不是固定加一个 Alpha。一次较长更新可能直接到目标,一连串短更新则逐步到达。名义响应时间相同不代表任意变帧情况下每个中间姿态逐位一致;如果要做严格回放,还要固定采样顺序、状态事件和参与混合的源时间。旧独立原型归档片段的采样频率较低;新增 Manny 录像提高了可辨认性,也仍是可变间隔采集,不能证明每个渲染帧的细小差异。

动画图的节点、空间与实际输出

本节先说明冻结独立原型的图记录,后面的 Manny 工程章节另列此前拍摄的真实图与显示链,不能将两者视为同一张 AnimGraph。最新落地修复作用于终端约束节点的 C++,没有更换下列动画蓝图或 AO 资源。

历史最终图从普通卧姿与匍匐 BlendSpace 进入方向姿态混合,方向侧使用 SequenceEvaluator,再经过姿态、飞扑和 Slot,转换到 Component Space 后执行程序瞄准约束,最后回到 Local Space 输出。

上篇新增源动画核对进一步区分了普通卧姿进入、匍匐进入和后续稳定循环。这里分析的原型 BlendSpace 构建方式仍以冻结源码为准;输入资源放进速度行,并不会自动获得原状态机的结束事件和循环交接语义。不能把本原型的采用方式当成已经等价接通原版稳定匍匐。

早期试验的匍匐瞄准覆盖、某些手臂层和通用持枪层在该记录中权重为零。它们的资产仍保留,并不等于最终链路还在使用。调试时只查资源名,容易把已禁用分支重新当成已生效的依据。

图中每一个变换空间都需要明确。普通局部动画依父层级组合;Component Space 节点读取评估后的骨骼位置和旋转,围绕关节 Pivot 旋转后代。再转换回 Local 时,必须保留更新后的父子关系,不能把组件空间值直接当成本地轨道写回。

基础姿态、方向评估与组件空间约束
图 9|历史最终动画链。灰色为保留但禁用的旧覆盖;图说明已记录的消费者,实际资产引用和图绑定仍需在目标工程中核对。

源码的 NativeUpdateAnimation 读取 Character、Controller、相机与场景射线,NativeThreadSafeUpdateAnimation 再消费缓存速度、姿态和角度进行计算。这种划分在本原型中避免将部分 UObject 查询直接放到线程安全更新段。它并不自动证明正式目标工程的 Proxy 数据同步已经完成;迁移时仍需按现有更新链传递输入快照。

下游 Slot 也需要检查。如果蒙太奇或另一姿态节点在之后覆盖刚恢复的方向姿态,前面的变量和资产再正确,最终画面也不会符合预期。验收应记录实际输出节点、时间与权重,而非只打印 AnimInstance 中某个值。

游戏线程采集与动画计算的职责分配

NativeUpdateAnimation 首先确认拥有者,必要时从 Pawn 重新取得角色,再采集实际速度、Actor 旋转、姿态、档位和瞄准。它还访问 Controller、FollowCamera 并执行场景射线,同时更新参考角、过滤角与目标权重。函数既采集也计算,文件中的“只采数据”注释没有完整概括实际职责。

NativeThreadSafeUpdateAnimation 消费缓存速度和旋转,计算 GroundSpeed、移动方向与各动画 Alpha,并生成参考补偿角。它不再在这个位置发场景射线,也不从调试相机取得新的目标。这种分工让 UObject 查询与纯数值消费相对集中,但是否安全迁入另一个 AnimInstance,仍取决于该工程的实际调用、Proxy 拷贝和动画节点输入绑定。

移动 Direction 采用实际速度相对 Actor 旋转计算。代码在非瞄准的普通站立移动中把方向固定为零,以使用前向列;瞄准或者卧姿移动时才保留方向输入。移动 Direction 与静止 Time Pose 的 horizontal 对应不同的轴:一个描述正在发生的平移相对身体朝向,另一个描述保留参考下的观察方向。

缓存速度、目标点与最终骨骼具有不同的取样时刻。速度来自动画更新所见的角色状态,目标来自该次 FollowCamera 射线;随后图评估骨骼,武器与相机 Pivot 在骨骼完成后更新。单个 CurrentFrame 编号不足以说明这些数据的时间关系,当前原型也没有提供覆盖全部引擎阶段的统一快照保证。

实际定位问题时,可以先把某一帧的输入快照、缓存值、节点输入和最终骨骼分别保存。若输入正确而节点仍读旧值,应查变量绑定和更新顺序;若节点输入正确而最终姿态不同,应查权重、Slot 和下游修正。反复调整角度公式,无法修复一个从未消费新变量的图节点。

当前 Manny 工程的实际显示链

独立原型把自己的姿态和程序约束直接输出到角色骨架。当前项目则先在 DFM 游戏骨架上运行已有动作与握点链,再由 Manny 的动画蓝图读取父组件姿态,通过重定向资产转换到 Manny,最后执行 Ground Only 显示适配。源姿态、重定向和显示贴地分别可能改变结果,需要沿链检查。

独立原型分析与当前Manny显示链的区别
图 10|上方归纳当前 DFM→Manny 的姿态显示依赖,下方强调它与独立原型源码分析的区别。该图根据实际图拓扑与节点详情整理,不替代下面的真实编辑器截图,也不表示完整上游图的所有分支同时活动。

从父组件姿态重定向到 Manny

真实 Manny AnimGraph 中,Retarget Pose From Mesh 读取父骨骼组件,并绑定 RTG_DFM_To_Manny_NativeHands;输出随后进入一个保留历史 Aim13 名称的自定义节点,再连接最终 Output Pose。源骨架的动作与握点关系在前,Manny 是重定向后的显示结果,不能从 Manny 侧的一个节点名反推全部瞄准都在这里求解。

当前Manny动画蓝图的重定向与输出连接
图 11|Retarget Pose From Mesh、重定向资产与最终输出的实际连接。编辑器中是正常材质 Manny;截图说明作者图与属性,不是逐帧运行分支跟踪。

下游自定义节点的 Ground Only 已勾选,Weight、Additive Weight、Grip Weight 均为零。按图中配置,Manny 侧承担显示贴地适配,历史名称 Aim13 不代表该处已启用最终瞄准层。源 DFM 图的持枪层位于更早的骨架处理阶段,不受这些下游零权重直接说明。

Manny显示节点的Ground Only及权重设置
图 12|Ground Only 开启,其余相关姿态和握点权重显示为零。名称与功能应分别核对;画面中的离地问题也表明,有这项显示处理不等于所有身体支撑已通过。

源骨架中的原生层、惯性化与握点

源骨架截图展示了原生 DFM 层、Inertialization 与握点层附近的局部连接。配套拓扑记录可沿源姿态输入、惯性化输出和后续握点层追踪,不必只凭界面里重复的节点标题判断职责。所选节点的 GripOnly 与 DiveFrameOnly 均未勾选,但这张局部窗口没有覆盖完整上游和最终输出,不能证明所有可见分支在同一帧都起作用。

源DFM动画图中的原生层与惯性化局部连接
图 13|截图保留节点连接与选中项的详情。它说明当前图的局部组织,不是胸、头和双臂逐层关闭的对照实验。

飞扑播放器也与独立原型的组织不同。这里的实际截图为 SequenceEvaluator:序列和显式时间由代理字段传入,Should Loop 关闭,Teleport to Explicit Time 开启。不能把独立原型 BS_Dive/BS_LandDive 的播放器逻辑直接套在这张图上,也不能仅凭属性已绑定就声称每帧实际取样权重已完整记录。

当前源图的飞扑SequenceEvaluator与代理输入
图 14|可见显式时间、非循环与 Teleport to Explicit Time 设置,以及代理输入连接。截图中的其他遗留节点或灰显分支,不据其名称或外观认定运行时是否活动。

这条显示链已有实际图和连续录像,足以讨论当前动作怎样可见;它仍没有提供胸、头、双臂各自独立开关的同机位 A/B。完整上半身融合、皮肤接地与握点误差应继续分别验收,不能用实际编辑器截图代替这些结果。

观察输入、目标查询与骨骼完成后的相机更新

DoLook 把水平和垂直输入交给 Controller。卧姿 Actor 根据控制器朝向策略旋转,SpringArm 关闭 PawnControlRotation,再设置相对 Yaw 为 ControlYaw−ActorYaw。忽略额外父变换和约束时,Actor 的世界 Yaw 加这段相对角,最终仍对应 ControlYaw。

这项关系避免把 Actor 已经执行的跟转再加一遍,也避免让静止计时器或某段动画夺走鼠标水平输入。观察相机、骨骼头向和移动方向都不应各自再成为 CameraYaw 的独立写入者。

但“姿态角度输入”与“空间瞄准目标”仍不同。前者以 Controller 方向求角,后者从 FollowCamera 的位置和前向发出长射线,默认到 100000 cm,Visibility 命中时缩短到接触点。这个目标为枪械几何提供世界点,而非仅提供一个 Yaw。

骨骼评估完成后,表现回调使用最终 head 位置再向上 15 cm 作为相机 Pivot 依据。普通臂长读取默认值,历史默认约 400 cm;瞄准采用 150 cm,肩侧偏按普通 35、瞄准 45 cm 加到默认偏移上。这些均为原型构图参数,未被证明是原版相机 Profile。

Controller、姿态角、相机射线与骨骼完成回调
图 15|输入、目标查询与骨骼后呈现分阶段。相机目标在动画更新时读取,Pivot 在骨骼完成后更新;图不声称两者天然都是同一份最终视图快照。

原版卧姿转换会同时改变身体、持枪与相机。连续片段展示腹卧经侧身转向仰卧的过程,可用于观察三者的相对变化。由于没有同步输入记录,录像无法确认当时是否持续按住瞄准;原型将瞄准状态与方向姿态分开保存的实现,仍以源码为依据。

原版卧姿转换|原速连续画面。观察身体翻转、持枪与镜头的相对变化;披风、植被和近景裁切仍会遮挡部分身体。它与前面的 Blender 取样不构成同输入对照。

原版构图参考与原型相机参数

原版 HD2 的低姿、转向和翻身录像提供最终构图参考,包括角色在画面中的位置、镜头与地面的关系,以及身体变化时的视角跟随。它与本文原型使用不同场景,也没有同步输入,适合比较可见构图,不适合直接反推参数。

原版构图参考|低姿转向与翻身附近的连续画面,原速播放。局部有披风遮挡;这里观察角色、镜头与地面的关系,不从录像推算精确臂长或内部约束状态。

适配时可以先列出可见现象,再为每个现象找实现输入。例如角色切换低姿后镜头靠近,是需要检查的构图变化;但具体由 FOV、臂长、Pivot 还是场景碰撞共同造成,不能仅从画面量出。原型目前采用的头部 Pivot、肩侧偏和瞄准臂长,是明确的实现选择,需要按自己的记录验证,而不是直接称为原版相机参数。

最终骨骼与相机的更新依赖

使用最终 head 作为 Pivot 可以改善卧姿构图,却也引入反馈链:姿态需要目标,相机需要姿态后的头位,目标又来自相机。若不标明取样阶段,就容易将上一份相机状态和本轮最终状态混称为“当前视图”。

这份原型在动画更新时读取 FollowCamera,骨骼完成回调再改 SpringArm 和武器。仅凭这两处代码,不能证明相机、姿态目标和最终枪口已经在整个引擎调度中无延迟地闭合。迁移时应记录取样时刻与更新者,使用稳定的控制输入,并明确哪一份视图用于目标查询,哪一份用于最终显示。

调试观察机位需要与姿态输入隔离。为检查上半身而将观察相机移到角色正面,不应同时改变角色的瞄准方向。该原型从 Controller 读取姿态角,以保留独立的输入来源;相机的后续更新时序仍需运行记录核对。

组件更新前置与骨骼完成回调

BeginPlay 将角色 Mesh 设为离开视锥后仍更新姿态并刷新骨骼。相机 Pivot 和武器依赖最终骨骼;如果低姿或抬头使身体出画后停止刷新,头部数据会过期,进而影响相机更新。持续评估用于满足这项依赖,同时仍需计入运行开销。

同一入口还把 CharacterMovement 与 Mesh 加入 SpringArm 的更新前置,并注册骨骼变换完成委托。委托执行时读取当前 AnimInstance 保存的目标和 GripAlpha,再调用表现更新。EndPlay 则注销这项委托,避免角色结束以后继续保留回调。若迁移时只复制表现函数,漏掉注册、前置和注销,代码看似相同,实际执行时机已经不同。

在普通卧姿之外,表现函数会隐藏原型枪,并恢复默认相机 TargetOffset、SocketOffset 和臂长;该操作只改变显示,不执行装备卸除。原型枪是角色持有的展示组件,没有接成本次独立系统中的正式库存、射击 Ability 或资源生命周期。看到它在趴下时出现,不能把这一点当作完整装备链已经迁移。

相机还在角色 Tick 中用 FInterpTo 逼近瞄准 FOV,而方向 Alpha 使用的是 FInterpConstantTo。两者虽然都有速度参数,却不是同一响应模型。常速接近在离目标较远或较近时采用相同的最大变化率;比例插值会根据误差改变步长。把两处参数统一替换为某个“平滑时间”,会改变进入瞄准时镜头与身体的相对节奏。

近距离构图也不只是缩短臂长。最终 head 的位置参与 TargetOffset,SocketOffset 另加入肩侧偏,SpringArm 还可能受场景和自身设置影响。把它概括成“相机永远位于头后 150 cm”会遗漏偏移与实际更新条件。当前代码明确的是输入参数和写入关系,实际最终机位仍需从玩家视图记录确认。

最终手骨、模型握点与武器放置

表现函数根据 GripAlpha 选择枪械旋转的来源。Alpha 大于 0.99 时,用最终右手四元数乘右握点参考旋转的逆,反推出枪旋转;其余情况采用握点到目标的几何解算。前一分支保持最终骨骼一致性,后一分支依据目标几何。完整进入过程需要检查两条分支之间的切换,alpha=1 的静态结果只覆盖终点。

随后枪的位置不是直接等于右手位置。模型的右握点具有自己的局部坐标,必须先用枪旋转把它转到世界方向,再从手的位置中减去这个偏移。这样才能让武器右握点落到手中,而非将武器模型原点误当作握点。模型原点、右握点、左握点和枪口是不同位置,这是近距离瞄准与持枪对齐都需要的基础。

gunPosition = rightHandWorld - gunRotation * rightGripLocal

换一把枪以后,局部握点与枪口偏移都可能改变。原型中的硬编码向量服务于当前模型,同一个 Socket 名称不能替代各武器的握点配置。即使枪的世界变换正确,角色的手掌参考旋转、掌心轴与肘平面仍需单独检查;武器放置与人体持枪姿态仍是两项检查。

源姿态与程序胸头约束的叠加问题

当前 Component Space 节点先补偿参考朝向与 ActorYaw 之差,再对平地脚部穿透做整套姿态抬升。随后它根据目标绝对仰角修正胸部,以世界 Up 重建头部朝向,并重解双臂到武器握点。

这些操作解决的是局部几何约束,尚未证明等价原版 TargetAimConstraint。源码里某些注释表达了期望行为,实际算式仍需单独看。当前胸部 PitchDelta 取目标方向相对 Up 的绝对仰角,夹在 ±30°,并没有先减去源动画当前已经抬高的枪或胸部仰角。

若源姿态已包含角度 θsource,而目标要求 θtarget,保留源动作的残差修正需要考虑两者差异。直接再次施加 θtarget 可能重复校正。三维求解还涉及骨轴、滚转和层级关系,标量相减只能说明当前公式缺少的输入,尚不足以构成完整修复。

当前节点将胸部修正按 spine1、spine2、chest 的 0.2、0.3、0.5 分配。这些是程序角度份额,和上篇原版 Mask 的逐骨骼覆盖权重不是同一件事。数值碰巧相近,也不能把它们对应成恢复参数。

头部绝对朝向与侧卧滚转的冲突

头部修正使用目标方向和世界 Up 构造绝对朝向,先给 neck 分担 35%,再让 head 完成剩余修正。这个构造能够让面部方向接近目标,也可能将源姿势中合理的侧卧滚转抹掉。

方向向量只约束旋转的一部分。即使 headForward 与 targetDirection 的点积接近一,头仍可以绕这根轴滚转。用世界 Up 固定另一根轴可以消除这项自由度,却不保证结果符合卧姿的颈部运动范围。侧卧时的身体 Roll、胸部朝向与头部允许转角仍需一起处理。

头向点积、相机相对头部的位置和枪口对准分别检验局部关系,上半身自然性还取决于关节姿态与连续变化。历史记录中的较大头部帧间四元数变化,以及随后人工指出的上半身问题,均未被这些局部指标覆盖。

绝对目标修正与源姿势残差的区别
图 16|解释上半身问题的几何关系。残差图只是后续求解应考虑的输入,不是本文已经实现的新 solver;当前胸头程序修正仍属于有问题的原型路径。

组件空间支链旋转与后代传播

程序节点先取得 Mesh 的组件到世界变换,把世界目标点变成组件空间点,把观察方向和世界 Up 变成组件空间方向。位置与向量不能互换:位置转换要考虑平移,方向转换没有平移;本实现对方向使用不带尺度的逆变换,再归一化观察向量。这些处理依赖当前 Mesh 的尺度与坐标约定,迁入有非均匀缩放的角色时需要重新核对。

节点随后把当前紧凑姿态中的全部骨骼组件变换复制到输出数组,后面的修正都基于这份已评估结果。使用的是 CompactPose 索引,不是源动画 JSON 索引,也不是一份可以跨 Skeleton 保存的固定整数。骨名引用先在有效骨集合中初始化并检查,必要骨不可用时,节点不应继续按旧索引写数据。

整套方向补偿对骨骼位置和旋转一起应用。若只转各骨自身的旋转、不转它们相对组件原点的位置,身体不会作为一个完整姿态转动,头手脚的空间关系也会改变。这里的 BodyTurn 围绕组件空间 Up,表达之前保留参考与 Actor 当前朝向的差异,不是重新根据头骨朝向对齐身体。

胸部等局部支链则使用另一种操作。RotateBranch 先读取关节位置作为 Pivot,再通过父链判断每根骨是否为该节点后代;属于支链的骨,位置绕 Pivot 旋转,旋转值左乘同一个修正并归一化。这样肩、肘、手和手指会一起跟随胸的改变,而不只是胸骨原地改变一个旋转属性。

这段实现为了清楚处理当前原型,会对骨骼反复检查祖先链。它能说明被修改的范围,不能据此称为已经优化好的大规模角色求解器。后续若缓存后代集合或改变输出结构,应保留相同骨架身份和更新顺序;错误复用另一副骨架的索引,往往比这里的遍历成本更危险。

胸部俯仰在取得右手到目标方向后计算,使用它与 Up 的点积求反正弦,再限制到正负三十度。当前算法没有先从源胸姿态减去已存在的俯仰。沿 spine1、spine2、chest 分配的 0.2、0.3、0.5,是程序目标角的份额,与上篇的逐骨骼 Mask 权重完全不同。即使这些份额合计为一,也不表示身体最终相对世界只改变这一个角,因为源姿态与后续头臂处理仍参与结果。

头部随后构建期望旋转:一个轴朝向目标,另一个轴参考世界 Up,并用导入参考姿态中的脸部局部基进行补偿。颈部先承担部分修正,头再对齐到期望旋转。这能约束比单纯前向点积更多的自由度,但也会把源侧卧中的滚转改向世界竖直,正是需要人工检查的地方。

还要注意计算时刻。目标头方向在颈部支链旋转前根据头位置取得,颈部旋转又可能改变头位置。最终头旋转达到此前的期望值,不等于最终头位置重新指向目标的射线必然零误差。因此验收应从最终姿态重新量方向,而不是直接把中间变量当输出。这里没有增加新的求解步骤,只把现有执行顺序的影响解释清楚。

在这些胸和头操作之后,代码才检查 GripAlpha 是否很小并提前返回。也就是说,在节点已经参与评估的条件下,手臂约束可以退出,而头部方向修正并未因为同一 GripAlpha 一起退出。胸部角度分配乘了该权重,头部构造却有自己的执行路径。把整个节点统一称为“一个持枪 Alpha”会遗漏这种行为差异。

握点、枪口与双臂约束的几何关系

当前武器使用原枪的右手与左手参考握点,枪模型以 +Y 为前向。右手目标先从右肩沿目标方向前推 24 cm,再向世界 Up 反方向移 8 cm;之后反复将两手目标投影到各自的可达范围,双臂采用 TwoBoneIK 求解。

可达范围由肩到肘、肘到手的当前长度相加,再乘 94%。迭代次数为 24。这些是当前原型参数,不是原版求解器常量,也不能因为参考握点来自原枪,就把整个双臂构造称为原版还原。

武器旋转还需要处理“握点不在枪口、枪口轴也不穿过握点”的几何。将目标减去握点直接作为枪前向,近距离会出现偏差。源码用一个带模型空间偏移的关系求解:

Target - Grip = R × (MuzzleLocal - GripLocal) + distance × R × localForward

其中当前模型 localForward 为 +Y。公式表达实际枪口发出的直线应经过目标,同时握点保持在手中。它仍包含滚转基准、可达性与近距离输入保护,不能等价为简单 LookAt(Grip,Target)。

右握点、枪口偏移与目标的几何关系
图 17|紫色向量为旋转后的模型局部偏移,绿色线段从实际枪口延伸到目标;红虚线显示直接从握点 LookAt 的另一条线。图为二维教学投影,不是原型的实测瞄准误差。

骨骼完成回调再由最终右手和参考握持旋转放置武器。这样可避免在武器 Actor 与角色骨骼上重复应用同一转角。但即使两个握点误差几乎为零,肘的弯曲平面、肩部负担、腕部翻转和枪托位置仍可能不合理。

当前求臂时尝试保留原有弯曲平面,退化时才采用备用方向;这比所有姿势强行共用一个相机空间 Pole 更有针对性,却仍需在腹卧、左右侧卧与仰卧分别观察。握点位置正确后,还需独立核对肩肘腕的姿态,以及实际开火链。

枪口相对握点的偏移与旋转求解

GunRotation 处理的 Offset 是枪口相对右握点的位置。它先取目标减握点的向量,再建立与 Up 正交的水平参考轴。当前武器局部前向是 Y,因此最终使用 MakeFromYZ 组织旋转,而不是假定模型沿 UE 的 X 轴开火。轴约定如果错了,枪身可能恰好转向目标,枪口射线却指到另一侧。

求解中先从目标距离平方扣去横向与竖直偏移的平方,结果不小于零后开方,得到沿轴几何所需的中间量。再利用竖直投影与偏移计算 Pitch,利用水平投影和横向偏移计算 Yaw。反三角输入会被限制到合法范围,半径分母有下限,避免某些近距离输入直接产生未定义数值。

但数值保护不等于几何永远有可行解。目标离握点太近时,扣除偏移后的量可能为负,被截到零后得到的是一个受保护的候选旋转;它不能证明枪口已经可以在该距离精确对准。实际测试应重新从算出的枪口位置和前向检查射线到目标的偏差,并分别记录近处遮挡,而不是只检查四元数是否有限。

单次 LookAt 后再将枪移到手边,会改变枪口位置,原先的对准方向随之失效;近目标处的反复调整还可能不稳定。当前解析式将模型空间偏移直接纳入旋转计算,但仍采用当前 Up 约定的滚转基准,属于原型的几何方案,原版完整武器目标求解器尚未恢复。

双手求解时,左手目标由右握点加上旋转后的左右握点差得到。只要枪旋转改变,左手目标就跟着改变;不能先在世界空间固定两个互不关联的手目标,再要求同一刚性枪械同时满足它们。目标约束与骨段可达范围必须围绕同一份武器变换计算。

肘部弯曲平面与后代骨重建

TwoBoneIK 的位置约束只解决肩、肘、手组成的两段链。它还需要决定肘朝哪个方向弯。当前代码把原肘相对肩的向量投影到肩手连线的垂直平面,得到尽量保留源姿态的 Bend;只有接近退化时才回退到左右侧参考方向。这避免所有卧姿都被一个固定相机 Pole 拉成同样的肘形,但未证明退化边界连续。

求臂前会保存整份修改前的组件姿态。解出肩、肘和手以后,手旋转还会根据枪旋转与握点参考补上,然后按 GripAlpha 与原结果混合。接着遍历肩部后代,用修改前的父子相对关系和新的父变换重建其余骨。这个顺序让手指、辅助骨与其他后代继续随手臂移动,避免它们停留在旧组件位置。

这份后代重建也有明确前提:使用的父子关系与遍历次序必须匹配当前紧凑骨架。不能把源动画骨号直接用作这里的数组下标,也不能在不同 LOD 必要骨集合中长期缓存未经重新初始化的索引。迁移时应先确认 BoneReference 与当前 BoneContainer 有效,再谈复用求解过程。

最后的混合还值得专门观察。Alpha 为一时,末端可能完全落到求解目标;Alpha 处于中间时,肩肘手同时与原姿态混合,刚性握点关系未必仍精确满足。于是“满权重误差很小”只能证明那个端点,不能证明进入持枪、退出匍匐或快速切向时的全过程。真正容易暴露问题的,往往正是这段中间权重。

枪与手之间的旋转切换边界

表现更新在 GripAlpha 大于 0.99 时根据最终右手反推枪旋转,其他情况下仍使用握点到目标的解析几何。0.99 两侧采用不同输入,不能仅凭函数返回有限四元数判定切换连续。需要把门限前后的枪旋转放到同一坐标空间,分别检查四元数角差、右握点位置、枪口前向及目标射线偏差。

若进入握枪时出现短促转动,应先确认是几何分支切换、腕部参考旋转,还是肩肘手的中间权重引起。给最终枪模型再加一个平滑器可能减轻视觉跳变,却不能说明上游姿态关系已经正确。本节提出的是针对现有分支的检查方法,没有重新运行这一门限实验。

双臂可达投影的条件与限制

以两段手臂长度之和乘 0.94 得到的半径,只约束手离肩不要太远。它没有表达肘部最小弯曲角、肩部扭转限制、枪托与胸口碰撞或左右臂同时自然的姿态成本。目标进入半径内,只能说明当前简化几何允许继续求解。

当前循环先把右手目标限制到右肩半径,再用枪旋转计算左握点,按左肩半径修正,再重复。枪旋转又依赖右握点和世界目标,因此两次投影之间不是两个完全固定球的简单最近点问题。执行固定次数会得到一个候选,不能仅因循环结束就证明收敛或找到了全身最优姿态。

求得肩、肘和手之后,节点还要更新相关后代骨。源码保存修改前的组件空间结果,再根据原父子相对关系与修改后的父变换重建后代,避免手指或辅助骨停在旧位置。若迁移时只复制三个 IK 关节,却遗漏后代传播,手掌位置可能正确,手指和武器辅助骨仍会脱离。

另外,权重混合可以让输出逐步接近求解结果,但在中间权重时也需要检查握点误差和腕部旋转。只测试 alpha=1,可能遗漏进入或退出持枪时的异常;只测试静止正前方,又可能遗漏侧卧时弯曲平面退化。验收矩阵需要把姿态、目标方向与权重过渡结合起来。

双臂可达范围与肘部弯曲平面的区别
图 18|可达半径只限制位置,肘平面与手腕朝向仍有独立条件。两手位置由同一枪械变换关联,固定次数的交替投影不等于全身姿态最优解。

平地支撑修正对上半身和相机的影响

当前程序节点读取两脚相对 Up 的最低高度,低于参考平面 5 cm 时,将整套评估姿态向上移动。这个修正改善了平地混合中脚部穿过地面的可见问题,却没有为每只脚建立独立地面接触,也没有求解胸、臀、肩与坡面的支撑。

整套骨骼一起上移还会改变最终 head 和手的位置,随后相机 Pivot 和武器放置都会读取这些变化。所以一个脚部修正即使不改变胶囊世界位置,也会影响上半身构图与目标几何。将其描述为“完全不影响瞄准的显示修复”同样不准确。

这条分支只能称平地支持修正。斜坡、台阶、墙边和低顶需要独立接地与净空策略;胶囊已经碰到地面,也不意味着水平展开的身体骨骼没有穿墙。当前站立与卧姿切换还直接改变胶囊半高并调整中心,开阔地原型没有完成顶头检测,迁入正式工程必须补上相应资格与失败结果。

原版目标约束的局部证据与未恢复环节

交接保存了目标约束组件、目标更新、角度计算、变量提交和约束位置写入的局部证据。原状态机还给出命名约束列表,其中部分名称与玩家配置中的目标约束相符。这些线索可以连接“动画变量”与“需要某个空间目标”的关系。

局部原指令记录还检查了一个位置写入后缀:显式准备 Unit、组件和约束矩阵,执行后只有矩阵中的位置字段改变,其余字节保留。它能够说明所执行片段是约束位置写入,不是胸骨旋转写入。

但句柄解析前缀、完整动画图进入、目标约束 solver 和全身最终骨骼没有在这个后缀测试中执行。把“位置已写入目标记录”写成“约束已解出上半身”,就跨过了这份证据没有覆盖的消费者。

同理,武器分类局部表达式得到候选权重,还缺对应状态激活和额外路由。下一步研究应先闭合当前武器在普通静止、匍匐、仰卧、瞄准下实际进入哪些上层状态,再追目标名称到约束定义、骨骼链、左右手附着和求解顺序。

原配置还有若干数值组,但完整字段意义和单位尚未全部确认。正文不把它们猜成胸或头的旋转速度。未知字段保留来源和缺口,比根据合理外观重新命名更便于后续验证。

源码、局部执行与运行画面的对应范围

本文的运行时说明主要来自独立原型的 Character、AnimInstance、程序瞄准节点与动画图构建代码。源码可以确认状态由谁写入、哪一步读取旧值,以及空间变换和权重采用什么算式;它不能单独证明当前工程加载了同一份资产和图。

旧独立原型的灰白占位素材仍保留在内部问题档案,不作为公开展示。新 Manny 素材已经具备正常材质、完整身体与可辨认光照,可用于观察实际适配状态;其中可见的侧卧支撑不足、躯干离地和肩臂问题仍是待修项,素材可见不表示姿态已经通过最终验收。

普通腹卧、侧卧与仰卧的源资源对照已在上篇完成,本篇保留对应 Blender 可视化,并新增当前工程的真实 AnimGraph 和 Manny 录像。有效的根锁隔离条件与胸、头、双臂独立开关画面仍缺少;对这些环节的解释继续限定在已有源码与诊断方法。

材料 可核对的内容 保留的限制
冻结原型源码 输入、阶段、权重、目标、骨骼与相机写入顺序 不证明当前运行资产与代码完全对应
原机制局部执行记录 已声明输入下的角差与目标记录写入等片段 不覆盖完整状态激活和目标约束求解器
Blender 源姿态 资源时间与身体形态、原骨架层级和局部轨道 无运行时持枪约束与地形适配
原版实录 连续动作、持枪与构图的外观参考 无同步输入和内部状态,不能逐帧映射原型变量
当前 Manny 录像与实际 AnimGraph 当前工程的方向动作、显示链、节点绑定与现存姿态问题 不验证独立原型全部函数、完整接地、上半身、射击或联网
历史 UE 日志与问题记录 当时的入口、状态和局部几何指标 不自动覆盖当前工程的构建与资源

历史测试的指标与证据范围

历史记录包含方向切换、持续转向、保持与反转、瞄准、匍匐及停止,也有方向飞扑入口测试。测试覆盖的是当时配置下的输入和指标,尚不能推及全部上半身姿态、网络或复杂地形。

历史指标分别检查相机 Yaw 与控制输入的差异、头部朝向、相机相对头部的位置,以及双手握点误差。它们用于发现方向反转、武器脱手或镜头位置错误;关节局部角度、滚转合理性和连续姿态质量仍需另行观察。这些数值应按各自测量对象解释,不统一称为“原版动画误差”。

还有一组截图曾实际捕获资产编辑器视口,后来被排除出玩家机位证据。这个错误值得保留:正确的图片文件和脚本成功返回,不保证实际采集了目标窗口。今后每段验收都应核对世界、Pawn、玩家相机与实际截图内容,而不是仅凭文件名判断。

源码与资产快照、历史测试和后续采集属于不同批次,尚未完成逐项版本对应。本文分别标明这些材料的用途,不将新增采集视为整套历史测试已重新执行。

源码、局部原码、历史运行与人工观察的证据范围
图 19|不同证据回答不同问题。源码、原机制局部结果与可用画面分别支持对应结论;待补的运行时分层对照仍保持为空缺。

持续转向的分阶段核对方法

对静止卧姿持续输入转向,至少应同时记录 ControlYaw、ActorYaw、ReferenceYaw、过滤后的 yaw、horizontal、方向姿态目标权重和当前 poseAlpha。否则看到躺卧开始得很晚,无法区分输入尚未积累、Actor 跟转抵消相对角、权重缓慢进入,还是源片段中该姿态本来位于另一采样区间。

再记录当前 SequenceEvaluator 的实际时间、最终 head、胸和骨盆的组件空间旋转。变量 horizontal 正确,不表示下游确实取到了这个时间;采样正确,不表示程序节点没有再次覆盖。角色模型与相机同屏时,还应分别记录最终相机方向和程序枪口方向,以免只看画面认定身体旋转来自相机。

实验首先关闭程序胸、头和双臂约束,观察源动画与方向补偿,再逐项启用。每次只改变一个消费者,更容易确认哪一步开始丢掉 Roll 或引入突然扭转。这个方法是后续验收建议,本文没有声称已执行新的对照组。

从独立原型到当前工程的差异与待验证接口

当前 Manny 已有实际适配输出,但仍需按依赖关系逐层核对。应先确认最小正常趴姿与匍匐,能够隔离时暂时关闭程序胸、头和双臂约束,检查 root、骨盆、手脚方向与地面高度。目标骨架如果是 Manny,需要先完成骨轴和参考姿态校准,不能把原 avatar 的名字映射、根锁与 180° 补偿一并照搬。

方向姿态随后接入,保留普通与瞄准均可侧卧/仰卧、移动混出、停止重建参考这些语义。45°、5/s、150 cm/s 等原型参数可以作为待验证起点,但不应先写入一个名为“原版默认”的公共配置。

上半身再单独解决。先恢复正确武器路由与非加法覆盖,核对源姿态自身的 Roll 与俯仰,再考虑受限残差约束。保留头部绝对世界 Up 和肩部固定前后偏移,只能延续当前拟合路径,不能替代原目标约束链。

飞扑位移接正式移动与输入入口,明确起跳、空中保持、触地、落地和退出,继续坚持唯一位移所有者。独立原型中直接停止速度、开阔地改胶囊尺寸且不做顶头检测等简化,迁移时都要显式处理。坡面、低顶、边缘落空和被打断不能由平地测试代替。

枪械系统最后还要连接实际开火、枪口、弹道或射线、命中与伤害、换弹和切枪。姿态正确之前,开火特效可能遮住上半身问题;姿态正确之后,也仍要检查近处遮挡和武器退出时机。持枪演示与射击完成应各有自己的验收项。

联网需要另一个完整环节:自主端预测、服务端资格与碰撞、动作身份、方向参数、纠正重放和观察端姿态。增加几个复制字段,不会自动解决飞扑中的打断、输入释放、死亡与起身净空。本阶段单机记录不能扩展成网络结论。

Manny 视频与图像的观察范围

视频 主要观察内容 需要保留的限制
前向飞扑 离地、触地、继续匍匐并停止 不是独立原型速度和时长门的验收
后向飞扑 仰卧方向接地后转入移动 不将方向落地统一写成先腹卧
左向飞扑 侧扑、接地与移动接续 肩臂、武器和支撑尚未最终通过
右向飞扑 另一侧的方向动作与接续 不扩大到全部过渡或复杂地形
卧姿与持续匍匐 姿态入口、持续移动与停止 完整卧倒进入与起身过渡未确认
方向匍匐 前后左右输入和释放后的停止 未逐帧导出内部 BlendSample 权重
非瞄准转向 连续转向、反转、移动退出和再次停止 观察机位不证明玩家相机表现
瞄准转向 RMB 分组、移动停止与俯仰变化 另一次 PIE;没有开火和命中伤害验证

普通与瞄准段来自不同 PIE,属于独立运行。录制前已加载当前表资产,并等待真实第三人称枪口提供者就绪,所以本次结果不覆盖首次使用时的异步加载流程。具体输入、状态和资产记录随片保留,正文只列与动作判断相关的信息。

观察灯光和曝光在现有场景的运行内调整,角色原有材质未替换,也没有通过后期提亮或重绘修补。有限文件基线比较覆盖了地图、动画表和相关 DLL 等指定对象,支持这些对象未在录制中保存改变;它不是全工程所有文件均未变化的证明。

玩家 TPS 补录:动作、射击与镜头仍需分别核对

以下一批记录的是落地接续专项修复之前的玩家 TPS 窗口,能看到真实准星、弹药和相机构图。保留它们用于说明当时的问题;最新获认可的落地接续片放在下一节,不能混作同一构建。它补足了独立观察机位不能回答的问题,但不会把现有姿态缺陷变成已经修好的结果。前面的 Blender 素材用于正式说明源动作;下面的 UE 录像用于展示接入现状和诊断问题。

录像保持实际时间关系,采用可变帧率,实采约 16—17 fps,保留偶发停顿和引擎运动模糊,没有插帧或慢放。采集只移除了窗口自绘标题栏,没有裁切游戏视口;部分姿态中人物和枪出画,来自实际相机构图。输入通过 PlayerController 路由发送,转向由 SetControlRotation 驱动,不是硬件鼠标手感测试。

飞扑期间的 TPS 构图与发射

四向飞扑分别记录非瞄准与持续请求瞄准两组条件。每次动作保留起扑前、空中、接地和后续移动,并包含实际开火与换弹。画面用于检查角色的位置变化、枪口与目标是否同时处于视野,以及落地后的构图接续。瞄准请求不代表所有阶段的权重相同,预定发令时间也不足以确定单颗子弹离膛的渲染帧。

TPS 四向飞扑·非瞄准|当前 UE Manny 玩家视图,保留实际开火、移动与换弹。动作模糊、身体支撑与构图问题按真实画面展示,不作为最终姿态验收。

TPS 四向飞扑·瞄准输入|当前 UE Manny 玩家视图,持续请求 RMB 瞄准,仍为第三人称。局部出画未通过额外裁切掩盖;不宣称原版飞扑镜头已逐值恢复。

同轮输入和发射日志可以核对这些指定操作是否真正进入发射路径。飞扑阶段来自距离发射时刻最近的状态采样,采样约 10 Hz,保留时间误差;这不支持精确定位阶段边界上的单个渲染帧。源码存在射击朝向和状态门,也不能代替实际拒绝案例。本批有记录的开火按下均观察到弹耗和原生发射,因此本批记录不支持“飞扑空中必定禁射”的判断。

卧姿转向、匍匐与瞄准构图

卧姿与匍匐录像先经过腹卧、侧卧和仰卧,再分别进行前后左右移动、开火和换弹。实际 TPS 画面仍可见非瞄准仰卧时的悬空,以及瞄准仰卧时头部和武器出画。输入状态已经生效,最终构图仍需从上半身姿态、身体支撑与相机关系继续检查。

TPS 卧姿与匍匐·非瞄准射击|当前 UE 接入的开发记录,包含转向、移动和实际发射;普通卧倒完整进入过渡、仰卧支撑与最终持枪融合仍未通过验收。

TPS 卧姿与匍匐·瞄准射击|当前 UE 接入的开发记录。瞄准时的出画和局部遮挡如实保留,不是正式展示用的完成效果。

发射日志、实际碰撞与伤害分别说明

补录共记录 58 次开火按下,每次都观察到弹药减少并关联到原生发射记录。这个统计对象是输入段落,不是 58 颗子弹,也不保证所有武器、帧和朝向都允许射击。发射函数中的预测命中结果只用于预测,不能当作子弹真正接触了场景。

实际命中由后面的独立静态碰撞片段补充。角色经正常姿态输入进入卧姿,改变方向,并在非瞄准与瞄准条件下向场景发射。记录中有 19 个真实阻挡碰撞与同一颗子弹的发射标识关联,碰撞对象是静态网格;它证明的是这些选中子弹与场景的实际接触。

TPS 卧姿静态碰撞检查|实际子弹命中以同轮有效 HitResult 记录核对。画面中的靶板是静态网格,没有目标扣血、受伤反应或死亡验收。

这一段有 8 次短按,合计减少 20 发弹药,但当前窗口只关联到 19 个发射标识;最后一次短按的两发弹耗仅关联到一个标识。因此不把它写成逐颗 20 发均完成跟踪。其他失败或未选用轮次的命中记录也没有计入本段结论。

当前场景未找到可验证扣血的目标。玩家 HUD 的 HP 不是靶子 HP,静态碰撞也不证明伤害接收、敌人反应或死亡。本批能够连起来的是输入、弹耗、原生发射和指定静态命中;完整伤害验收仍缺。

相机碰撞收缩与恢复

最后看当前相机链在障碍附近的变化。角色靠近靶板、改变卧姿与观察方向,再后退靠近障碍并前进离开。录像与相机 CSV 来自同一次运行,记录了 CameraManager 最终视图,以及 SpringArm 的目标长度、偏移、未受碰撞约束的位置、碰撞标记与最终位置。

TPS 相机碰撞与恢复|当前工程实际玩家窗口及同轮相机数据;用来分析现有跟随、收缩与恢复,不能称为 HD2 原版镜头参数等价验收。

这次记录中有 54 个相机碰撞标记为真的采样点。同一采样点内,臂根到未受碰撞约束位置的距离,与臂根到最终相机位置的距离,最大相差约 140.36 cm;这里没有把不同时间的两个极值相减。下图保留实际时间与距离,帮助区分目标臂长变化和碰撞后最终位置变化。

同轮玩家 TPS 相机碰撞前后距离记录
图 20|玩家 TPS 补录的实际相机采样。两条曲线分别是同一臂根到未受碰撞约束位置、到最终相机位置的距离;有色竖带标记碰撞修正状态,线段仅连接离散采样,不填补缺失帧。它不代表相机沿臂长方向之外的全部几何,也不说明最终构图已经合格。

本批运行的是当前工程现有 SpringArm、瞄准和姿态相机链,旧独立 HD2 原型核心未启用。不能拿旧原型的诊断字段、重新计算的 profile 编号或零计数,解释为当前最终镜头已经采用对应参数,或否认实际发生的碰撞收缩。已有的目标、武器与骨骼回调设计分析仍要回到它所属的实现版本。

补录解决了“没有玩家 TPS 画面和实际发射记录”的材料缺口;身体接地、最终上半身融合、瞄准构图及目标伤害还需继续验证。正常材质、完整解码和日志关联通过,均不能替代这些项目。

落地持枪过渡中的肩部、握点与手肘连续性

此前 TPS 录像中的侧扑落地出现枪械看似脱手、随后突然抬手的现象。终端双臂约束可以维持两手握枪,却无法单独保证运动连续:肩部基准一旦改变,即使右手安装关系、左手接触点与枪口方向仍正确,手肘也可能在相邻帧切换到另一条轨迹。

已认可的落地接续片依次记录后扑腰射、右扑腰射、后扑瞄准和右扑瞄准。每例保留飞扑到落地的连续动作,例间复位与换弹被剪去,因此是独立用例的选段合集。视频没有慢放或插帧;画面中的开火用于观察持枪过渡,逐发命中不在这段录像的验收范围内。

落地持枪与瞄准偏移接续|当前 UE Manny 本地实录,后扑/右扑分别覆盖腰射与瞄准。认可范围是这些用例的落地上半身接续效果;并非完整飞扑、全部方向、冷启动或联机验收。

固定过渡偏差的作用范围

旧过渡在进入地面状态时,保存上一帧姿态与当前输入姿态之间的固定偏差,再逐渐减弱这份补偿。这样能让切换当帧的手腕接上,却没有约束之后继续变化的输入。下一帧瞄准偏移动作继续抬手,肩部与上臂根部的位置发生变化,即使最终手腕仍被拉回同一个握点,双骨骼 IK 也可能求出明显不同的手肘位置。

枪口对准和双手接触误差接近零,只能说明末端几何关系成立。落地动作的连续性还取决于相邻帧的肩部坐标框架、手腕目标和肘部弯曲参考,这些量需要一起续接。

落地窗口内的肩部、握枪目标和肘部参考接续
图 21|当前 UE 适配方案的几何教学。左图保持相同上臂与前臂长度、共享同一个腕部目标,改变肩部基准仍可得到不同肘点;右图说明从上一帧已完成的握枪姿态跟进当前目标。图中的点、轨迹和比例不是实录测量,也不代表 HD2 原生算法。

最终约束节点中的上一帧完整姿态

修复在最终持枪约束节点中保存上一帧经过完整解算的世界空间姿态。下一帧将它转换到当前组件空间后再参与计算,避免角色移动或旋转后仍沿用旧局部坐标。相比只平滑较早的 AO 节点,这份历史保留了后续握枪约束已经完成的结果。

落地窗口内先续接左右锁骨/肩部旋转,转换回各自父骨的局部旋转;局部平移、缩放与骨段长度继续沿用原姿态。随后从上一帧已经完成的右手握枪姿态,跟进当前目标的位置与旋转。这样处理的是仍在变化的目标,而不是仅用切换时的一份固定偏差抵消第一帧。

当前目标位置的计算可概括为:

dtLimited = clamp(frameDt, 0, 1/30 s)
delta = desiredPosition − previousCompletedPosition
continuedPosition = previousCompletedPosition
                    + clampLength(delta, 180 cm/s × dtLimited)

目标旋转也从上一帧已完成姿态出发,按四元数角距离限制本步跟进量,当前可配置速率的默认值为 480°/s。180 cm/s、480°/s 和时间限制都是这份 UE 适配实现中的参数,不能写成由反编译确认的原版参数。

肘部不能另取一套不同时刻的参考。当前解算使用同一上一帧的完整肩—肘—腕三角形传递旋转,并连续跟进肘部 pole,也就是双骨骼 IK 的弯曲参考点。若只沿用上一帧旋转,却取当前输入的关节位置,旋转和几何参考就不再属于同一个三角形,仍可能产生跳变。

完成这些连续性处理后,再执行实际准星目标对准、双臂可达范围投影和双手握点约束,武器继续沿最终右手安装关系放置。这里的位置和旋转限幅作用于候选目标;后续精确对准与可达投影仍会修改结果,不能把它们直接当作最终手腕或枪口变化的普适硬上限。

这套处理只在相应落地窗口生效。空中与普通静止卧姿窗口外继续走原路径,没有关闭 AO,也没有替换仰卧 AO 资源、动画蓝图、相机或位移算法。它解决的是当前接入链中的过渡,不构成原游戏也采用同一算法的证据。

落地窗口的局部回归结果

密集采样记录同时比较了修复前后的 Manny 右手相邻采样位移。下表列出各用例在落地窗口内的最大值;它们来自独立重复运行,各次帧时间并不完全相同,个别采样间隔更长;不能当作严格同步的逐帧 A/B,也不是每一帧都减少了同样距离。

用例 修复前最大相邻采样位移 修复后最大相邻采样位移
后扑腰射 13.27 cm 5.86 cm
右扑腰射 8.93 cm 6.00 cm
后扑瞄准 21.40 cm 6.00 cm
右扑瞄准 15.22 cm 5.53 cm

这些数值支持当前落地窗口内的跳变幅度减小;动作是否自然仍需要结合已认可的录像判断。复测还保留了实际枪口和握点约束,并覆盖后扑、侧扑落地后立即进入匍匐的本地用例。只有肩部平滑的中间候选在瞄准落地时仍有跳变,已被最终方案取代,不用其中间结果宣称修复完成。

匍匐阶段的手臂覆盖权重

关联检查发现,持续匍匐时移动混合权重已为 1,静止卧姿层也已退出,但终端双手持枪解算仍以全权重覆盖手臂,破坏爬行动作中的手肘支撑。此时匍匐资源和上游状态都已生效,问题位于最后一层对手臂的覆盖。

最终约束现在服从已有的爬行持枪权重。持续爬行时让出对手臂的覆盖,停止后再按现有过渡恢复;武器仍跟随最终右手安装,不随手臂约束退出而解除挂接。这不是更换腿部动画,也不证明原游戏最终匍匐输出已完整恢复。

本次认可与回归限定在资产已就绪的本地 PIE。冷启动资源等待没有在这次修复中解决,联机、弱网、其他方向组合与完整角色流程仍需各自验收。旧录像里记录的支撑和构图问题,也不能因为这一组落地接续通过就一并宣布消失。

与上篇及 Lyra 战斗研究的衔接

资源来源与处理细节见上篇。从整体原型计划到武器、无人机、导演和炮台,各篇都分别处理配置、运行状态与实际引擎输出。本篇继续处理角色侧的输入、移动和姿态。

后续战斗接入还需要与射击流程、实体弹丸与命中反馈协调:使用哪一份最终枪口、哪一份目标与相机,以及姿态切换和装备结束时怎样停止相关活动。炮台 Lyra 接入篇说明了装备、能力、世界实体和 GAS 结果的职责;角色姿态接入同样需要明确各状态的持有者,不能把画面中出现枪械等同于完整战斗能力已经建立。

本文的独立原型分析与当前工程适配示例,为后续战斗接口提供了具体参照。各模块尚未在同一工程完成联机和正式玩法验收,相关结论仍按各自实现与记录区分。

AI 协作复盘

AI 参与了源码与运行记录的交叉整理,重点核对注释和实际算式之间的差异。胸部修正就是一处具体问题:注释希望避免源姿态仰角干扰,公式却仍取目标绝对仰角,没有计算源姿态残差。人工检查进一步指出,头向、枪口和握点的局部指标通过后,上半身仍可能不自然。正文据此分别保留了实现结果和视觉问题。

独立原型源码用于解释状态和约束的实现,当前 DFM→Manny 工程的 PIE 录像用于观察适配结果,两者分别保留对应的记录。文章整理只读取和核对这些材料,没有修改或重新运行工程。机制图解释状态、角度、权重与空间关系;姿态接地、上半身融合和完整射击仍需各自验证,不能由一次动作录制或一项数值通过代替。

发表评论