油中移动阻尼
基本信息
| 属性 | 值 |
|---|---|
| 事件处理器 | buildcraft.oiltweak.OilTweakEventHandler |
| 扩展属性键 | "oiltweak"(IExtendedEntityProperties) |
| 扩展属性类 | OilTweakProperties(realStepHeight / inOil) |
| 涉及事件 | TickEvent.PlayerTickEvent、TickEvent.ClientTickEvent、LivingEvent.LivingUpdateEvent |
| 事件优先级 | 全部 EventPriority.LOWEST |
| 数值常量 | 阻尼系数 0.05、钳制阈值 0.05、下沉量 0.05、步高 0.0F |
三个处理器的函数体几乎完全相同(仅玩家多一行飞行处理), 都遵循同一套四步:夹紧 Y → 阻尼 → 下沉 → 夹紧 X/Z。
逐行数值
以 onPlayerUpdate(OilTweakEventHandler.java:91-100)为例:
player.motionY = Math.min(0.0D, player.motionY);
if (player.motionY < -0.05D) player.motionY *= 0.05D;
player.motionX = Math.max(-0.05D, Math.min(0.05D, player.motionX * 0.05D));
player.motionY -= 0.05D;
player.motionZ = Math.max(-0.05D, Math.min(0.05D, player.motionZ * 0.05D));
player.capabilities.isFlying = player.capabilities.isFlying && player.capabilities.isCreativeMode;
setStepHeight(player, 0.0F);
| 步骤 | 效果 |
|---|---|
motionY = min(0, motionY) |
上升被完全禁止(不是减缓,是清零) |
motionY < -0.05 时 *= 0.05 |
下落速度衰减到 5% |
motionX/Z = clamp(v * 0.05, ±0.05) |
水平速度衰减到 5% 且绝对值不超过 0.05 |
motionY -= 0.05 |
每 tick 额外压低 0.05,形成持续下沉 |
setStepHeight(entity, 0.0F) |
台阶高度归零,无法上台阶 |
注意 motionY 的顺序是"先夹紧、再阻尼、最后减去 0.05",
所以稳态下沉速度落在 -0.05 附近而非 -0.0025。
步高保存与恢复
setStepHeight / setNotInOil 通过扩展属性保存原值:
private void setStepHeight(EntityLivingBase entity, float height) {
OilTweakProperties props = getProperties(entity);
if (!props.inOil) { props.realStepHeight = entity.stepHeight; props.inOil = true; }
entity.stepHeight = height;
}
if (!props.inOil) 保证了只在首次进入油中时记录原步高,
重复调用不会把已被改成 0.0F 的值当成"原值"存回去。
离开油时 setNotInOil 还原 realStepHeight 并把 inOil 置 false。
getProperties 有一层兜底:取不到扩展属性时当场新建并注册(:200-208),
正常路径下 onEntityConstruction 已经注册过了。
飞行被降级
player.capabilities.isFlying = isFlying && isCreativeMode——
创造模式飞行保留,生存模式飞行被强制关闭(改 motionY 前先执行这一行,
下一 tick 再 setStepHeight(0.0F) 取消台阶高度)。
onLivingUpdate(非玩家生物)没有这一行,所以生物不受飞行影响。
源码缺陷:本地玩家被重复施加三次
同一个 eventHandler 实例被同时注册到 FML 总线与 Forge 总线
(BuildCraftOilTweak.java:51-54),而本地玩家会依次命中三个处理器:
| 处理器 | 事件 | 对本地玩家 |
|---|---|---|
onPlayerUpdate |
TickEvent.PlayerTickEvent |
✔ 两侧都触发 |
onPlayerClientUpdate |
TickEvent.ClientTickEvent |
✔ 仅客户端 |
onLivingUpdate |
LivingEvent.LivingUpdateEvent |
✔ EntityPlayer 是 EntityLivingBase |
三者都是无条件重写 motionX/motionY/motionZ 而非增量修正,
且 motionX/Z 的 * 0.05 是乘性的。三趟叠加后,
水平速度相当于被乘 0.05³ = 0.000125(再受 ±0.05 钳制),
远强于单趟设计意图。
onPlayerClientUpdate 的函数体与 onPlayerUpdate 逐行相同,
除 EntityClientPlayerMP 类型外无任何差异。
onLivingUpdate 是三者中唯一不带飞行处理的,但它同样会作用在玩家上。
NBT 键的分隔符不一致
OilTweakProperties 用了两种命名分隔符:
| 键 | 分隔符 | 位置 |
|---|---|---|
oiltweak.inOil |
点 | OilTweakProperties.java:18、:26 |
oiltweak:stepHeight |
冒号 | OilTweakProperties.java:20、:29 |
另外 stepHeight 只在 inOil 为真时才写入(:19-21),
读回时也要求 inOil 为真才读(:29)——
即实体若在浸没状态下死亡或卸载,realStepHeight 会丢失,
该实体下次进入油中时会把已被改成 0.0F 的步高当成"原值"保存。