油中移动阻尼

基本信息

属性 值
事件处理器 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 的步高当成"原值"保存。

相关条目