fix: 修掉 yaw 无界增长 —— 它让机器人原地走之字形、净位移恒为 0(1.11.0)
mineflayer 收到服务端 position 包时会直接 bot.entity.yaw = conv.fromNotchianYaw(…) 不做取模,而 GrimCompat 又在它基础上逐 tick 累加,两者叠加让 yaw 一路涨到 19254°(≈336 弧度)。 抓包实测的后果: yaw°: -1373 → 19254 → -1650 → -1407 → -1373 → 19254 → … 坐标: (6757.50, 11953.70) ⇄ (6757.89, 11954.12) 0.4 格内反复振荡 相邻两包的朝向折合相差约 114°,而机器人一直按着前进键 —— 于是它原地走之字形、净位移恒为 0,看起来像「卡在墙前反复起跳」 (物理录制里 jump 74/240、水平碰撞 195/240、onGround 只有 79/240)。 这是扩地跑不完那 763 格长途的直接原因,也解释了出站包里 look 包(38490) 比 position_look(23804)还多。 修复:新增 wrapAngle,_onPhysicsTick 每 tick 归一化 entity.yaw 到 (-π, π], setLook 也归一化传入的目标角度。归一化只改表示不改朝向 (shortestAngleDelta(huge, wrapped) === 0),对旋转逻辑完全透明。 测试 478 → 480。
这个提交包含在:
@@ -8,6 +8,7 @@ const { FakeBot } = require('./helpers/fakeBot');
|
||||
const {
|
||||
GrimCompat,
|
||||
shortestAngleDelta,
|
||||
wrapAngle,
|
||||
eyeDistanceToEntity,
|
||||
VANILLA_PHYSICS_BASELINE
|
||||
} = require('../src/bot/grimCompat');
|
||||
@@ -111,6 +112,31 @@ test('shortestAngleDelta 走最短路径并处理环绕', () => { assert.ok(Mat
|
||||
assert.ok(Math.abs(shortestAngleDelta(0, Math.PI * 2)) < 1e-9);
|
||||
});
|
||||
|
||||
test('wrapAngle 把任意角度归一化到 (-π, π]', () => {
|
||||
assert.ok(Math.abs(wrapAngle(0.5) - 0.5) < 1e-9, '范围内不动');
|
||||
assert.ok(Math.abs(wrapAngle(Math.PI * 2) - 0) < 1e-9, '整圈归零');
|
||||
// 实测抓到过 19254°(≈336 弧度)的 yaw —— 必须被归一化
|
||||
const huge = (19254 * Math.PI) / 180;
|
||||
const wrapped = wrapAngle(huge);
|
||||
assert.ok(wrapped > -Math.PI && wrapped <= Math.PI, `归一化后应在 (-π, π],实际 ${wrapped}`);
|
||||
assert.ok(Math.abs(shortestAngleDelta(huge, wrapped)) < 1e-9, '归一化不应改变朝向');
|
||||
assert.strictEqual(wrapAngle(NaN), 0);
|
||||
assert.strictEqual(wrapAngle(Infinity), 0);
|
||||
});
|
||||
|
||||
test('旋转推进后 yaw 始终保持归一化(回归:无界增长导致原地走之字形)', () => {
|
||||
// mineflayer 收到 position 包时会直接给 entity.yaw 赋一个未取模的值,
|
||||
// 本层又在其上累加;不归一化的话 yaw 会一路涨到上万度,
|
||||
// 发包朝向在两个方向间来回跳,机器人原地走之字形、净位移恒为 0。
|
||||
const { grim, bot } = setup();
|
||||
bot.entity.yaw = (19254 * Math.PI) / 180; // 未归一化的巨大值
|
||||
grim.setLook(0.5, 0);
|
||||
for (let i = 0; i < 40; i++) grim._onPhysicsTick();
|
||||
assert.ok(bot.entity.yaw > -Math.PI && bot.entity.yaw <= Math.PI,
|
||||
`yaw 应被归一化,实际 ${bot.entity.yaw}`);
|
||||
assert.ok(Math.abs(shortestAngleDelta(bot.entity.yaw, 0.5)) < 1e-4, '最终仍应朝向目标');
|
||||
});
|
||||
|
||||
test('eyeDistanceToEntity 计算眼睛到碰撞箱的最近距离', () => {
|
||||
const bot = new FakeBot({ x: 0, y: 64, z: 0 });
|
||||
// 目标在正前方 2 格,碰撞箱中心高度与眼睛同高
|
||||
|
||||
在新工单中引用
屏蔽一个用户