BP_SceneItem_Base_C (= MainActor = 文中说的"壳")SM_MAL_01_A_Car* (真船体 Mesh)SceneItemComponent (挂在 MainActor 上的 TS 组件)BP_SceneItem_Base_C 上。代码里的 this.Owner 就是它。
因为它继承自 BaseActorComponent,所以能被塞给下面的 BasePlatform。BasePlatform (引擎的"移动平台携带"入口)
最初玩家站在移动中的落兵船甲板上,有两个问题:
期望的目标是:船像"会动的地面"——站着、走动、跳跃全都相对船,就跟站在普通地面上一样自然。
排查后发现,症结在于这艘船的结构很特殊,和引擎的移动平台机制"对不上":
| 对象 | 作用 | 坐标情况 | 引擎能用它做平台吗 |
|---|---|---|---|
BP_SceneItem_Base_C(MainActor / 壳) |
逻辑锚点、空壳 | 坐标正常 | 能,但它静止不动 → 跟了等于没跟 |
SM_MAL_01_A_Car*(真船体 Mesh) |
Sequence 驱动、真正在动 | 绝对坐标是 -120万偏移值 | 不能——类型不符,且坐标是垃圾值 |
BasePlatform 只肯读 MainActor 一个物体的坐标,可 MainActor 静止不动;
这不是 bug,是 UE 的一个正常机制被撞上了。船的移动序列 Seq_TroopShip 在代码里设了一句关键配置:
TransformOriginActor 的意思是:这个序列里的动画,不用世界绝对坐标,而是相对某个原点来算。
美术做序列时,是在编辑器原点(0,0,0)附近摆船、K 动画的,序列里存的是"船从 0 开到 5000"这种相对小数值。运行时引擎把这套动画变换到关卡里,而本项目的世界原点做过大世界坐标偏移(WorldPartition 大世界常见),于是:
| 同一个物理点 | 报告的坐标 | 用的坐标系 |
|---|---|---|
| 你(玩家)站的地方 | X = 79872 | 偏移后的正常世界坐标 |
| 你脚下的真船体网格 | X = -1200000 | 序列相对原点坐标 |
曾经直接把真船体 Mesh 的世界坐标拿来驱动平台,结果那 -120万 的垃圾坐标直接把玩家瞬移到十万八千里外,一碰船就被弹飞。这条路堵死。
加临时诊断日志实测,站在开动的船上时读到:
结论:真船体的绝对坐标是垃圾值(-120万),但它的每帧位移增量(这一帧比上一帧动了多少)完全正常。
→ 只用"位移增量",就能安全知道船每帧动了多少,且永远不会碰到 -120万。
思路一句话:让 MainActor(壳)跟着真船体动起来,再把它交给引擎当移动平台——这样引擎自带的平滑携带机制就能全自动生效。
AddActorWorldOffset 把 MainActor 挪同样的量。MainActor 坐标始终正常,绝不出现 -120万。BasePlatform 设为 MainActor。引擎"站立携带"自动生效 → 人跟着船平移,平滑如地面。
站立判定靠引擎的"角色脚下踩着谁"。但跳跃、颠簸、状态重置的瞬间角色会短暂离地,这个信息瞬时丢失 → BasePlatform 被反复"设/清" → 携带断断续续 → 某帧人被甩出船(日志实测坐标 Y 从 57353 突变到 12980)。
新增 LeaveGraceFrames = 20:判"离船"时不立即生效,要求连续 20 帧都没踩到本船才真正判离。跳跃/颠簸那种零点几秒的瞬时丢失被过滤掉,携带保持连续 → 正常移动 / 跳跃再也不会被甩出船。
| 保护措施 | 作用 |
|---|---|
| 只用位移增量,不用绝对坐标 | 从根上避免 -120万 偏移坐标进入计算 |
MaxFollowDeltaPerFrame = 1000 | 单帧位移超 10 米判定为坐标抽风/重置,跳过该帧不移动,绝不弹飞 |
sweep = false | MainActor 只作位置参考,不与船体碰撞体物理互推(曾因 sweep=true 弹飞过) |
| 离船宽限期 | 过滤瞬时状态抖动,避免携带断续导致的甩飞 |
| 版本 | 做法 | 结果 |
|---|---|---|
| v1 | MainActor 静止,跟随它 | 不弹飞,但船不动→没惯性 |
| v2 | 跟随真船体绝对坐标 | -120万坐标 → 一碰船就弹飞 |
| v3 | 起跳时给一次性初速度 | 有惯性但落回原地,跟不上变速的船 |
| v4 | 空中每帧手动挪玩家 | 有惯性了,但抖动/能被甩出 |
| v5 (当前) | MainActor 按位移增量随船动 + 引擎原生携带 + 离船宽限期 | 平滑如地面,惯性正确,跳不出船 ✓ |