临时关闭物理:一个布尔值无法表达重叠占用
自动关联目录:临时关闭物理:一个布尔值无法表达重叠占用
核心判断:临时改变物理状态不是“记住一个旧布尔值,结束时写回”。只要两个区间重叠、不同 Body 原状态不同,或 Physical Animation 同时写入,恢复动作就必须知道自己拥有什么,而不能恢复整张 Mesh。
一、具体业务:展示附件时暂时抑制布料/刚体穿插
动作区间生成临时附件后,为避免附件与角色模拟 Body 穿插,Begin 检查来源 USkeletalMeshComponent 是否有 Body 正在模拟;若有,就调用 SetAllBodiesSimulatePhysics(false),并在生成的附件组件上记录“本通知关闭过物理”。Tick 发现其他系统重新开启物理时再次关闭;End 看到该标记后,对整张 Mesh 调用 SetAllBodiesSimulatePhysics(true)。
记录“是否由自己关闭”比无条件恢复更好,End 也与附件销毁放在同一条清理路径上。这说明实现已经意识到副作用需要所有权。但一个布尔值只能描述单写者、全 Body、无并发的世界,实际控制流并不满足这些前提。
二、缺陷闭环:恢复动作会制造新的错误状态
假设 A、B 两个通知重叠:A 先把物理关掉并记为 Owner;B 开始时看到已经关闭,因此不记 Owner。A 先结束后把全部 Body 打开,B 的区间仍未结束。即使 B 下一 Tick 再关闭,中间也出现了一次错误物理状态;若本帧已进入物理步进,穿插或姿势跳变已经发生。
逐 Body 状态也被丢失。IsAnySimulatingPhysics() 只证明“至少一个 Body 为 true”,随后关闭全部,End 却把全部设为 true。原本因层级、物理资产或业务策略保持 Kinematic 的 Body 会被错误开启。
Tick 中持续与 Physical Animation 争夺同一属性,则形成 Last Writer Wins:双方都在正确执行自己的局部意图,却没有一个地方计算最终状态。它增加了 Game Thread 到 Physics State 的重复写入,也让结果取决于 Tick 顺序,而不是业务优先级。
三、目标设计:物理状态由意图合成,调用方只持有 Lease
struct FPhysicsOverride {
FLeaseId Id;
FObjectKey Owner;
uint64 OwnerGeneration;
FBodyMask Bodies;
EPhysicsIntent Intent; // SuppressSimulation / ForceSimulation
int32 Priority;
};
class FMeshPhysicsCoordinator {
FBodyIntentMap BaseIntent; // 玩法/角色系统的长期意图
TMap<FLeaseId, FPhysicsOverride> Leases;
FBodyStateMap Applied;
public:
FLease Acquire(FPhysicsOverride intent);
void Release(FLeaseId id); // 幂等,只移除自己的意图
void SetBaseIntent(FBodyIntentMap state);
void Reconcile() {
auto desired = Compose(BaseIntent, Leases);
ApplyOnlyDiff(desired, Applied);
}
};通知 Begin 获取 Lease,End 或来源销毁时释放同一 Lease。两个抑制 Lease 重叠时,任意一个结束都不会重新开启 Body;只有最后一个抑制意图离开后,协调器才回到当前 BaseIntent。Physical Animation 也应更新 BaseIntent 或提交自己的 Lease,而不是在另一个 Tick 中把值写回。
Body 掩码必须精确到实际受影响的骨骼/刚体。若业务只要求附件附近不模拟,应使用物理资产中的 Body 集合或骨骼子树,而不是关闭全部 Body。应用阶段只对差异 Body 调用 UE 物理 API,必要时统一 Wake、Recreate Physics State 或更新 Physical Animation Profile。
四、迁移方案与代价
理想方案要求所有物理写入方经过协调器,迁移成本不低。可先做兼容桥:首次 Lease 获取时保存逐 Body 快照,引用计数归零时恢复快照;同时给 Mesh 和 Lease 加 Generation,防止角色重建后旧 Release 操作新 Body。这个桥仍无法正确吸收区间内合法发生的基础状态变化,因此只是过渡,不是最终架构。
| 方案 | 能解决 | 不能解决 |
|---|---|---|
| 单布尔恢复 | 单写者、全 Body 临时修改 | 重叠、逐 Body、外部改写 |
| 引用计数 + 快照 | 同类重叠和乱序 End | 区间内 BaseIntent 变化 |
| 意图协调器 + Lease | 重叠、优先级、逐 Body、幂等清理 | 需要统一接管写入口 |
五、验证标准
测试应覆盖 A/B 交错结束、同一 Lease 重复释放、部分 Body 原本禁用、区间内 Physical Animation 改状态、角色换 Mesh、组件销毁和 Montage 中断。每次 Reconcile 记录 Body、BaseIntent、ActiveLeaseIds、EffectiveIntent、AppliedDiff;关键不变量是:释放一个调用方的 Lease,不能撤销其他调用方仍然有效的意图。
这类问题表面上是“恢复错了”,本质上是物理属性没有所有权模型。每帧重写可以掩盖竞争,却无法把竞争变成确定规则。