首页 / JS 脚本手册 / 交互与 use 回调

交互与 use 回调

给车厢和装饰物件划出可点击区域,玩家右键命中时收到局部坐标。按钮、驾驶台、开关门都能脚本化。

两步

先在 create 里声明区域,再在 use 里处理命中。

function create(ctx, state, train) {
  ctx.addUseBox("door_left", 0, -1.4, 1.0, -1.2, -1.2, 1.4, -0.8);
  ctx.addUseBox("horn", 0, 0.30, 1.05, -2.10, 0.45, 1.15, -2.00);
}

function use(ctx, state, train, event) {
  if (event.box === "door_left") {
    state.doorRequested = true;
  } else if (event.box === "horn") {
    state.hornUntil = Date.now() + 800;
  }
}
坐标和你画模型用的是同一套。 不需要做任何换算 —— 按钮在模型里的位置,直接就是 addUseBox 的参数。

声明区域

方法说明
ctx.addUseBox(id, car, x1, y1, z1, x2, y2, z2) 登记一个长方体区域。id 是你自己起的名字,car 是车厢序号(从 0 起)。同名会覆盖。
ctx.addUseBox(id, x1, y1, z1, x2, y2, z2) 不带 car 的重载,等价于 car = 0。装饰物件用这个。
ctx.removeUseBox(id)移除一个区域。
ctx.clearUseBoxes()清空全部区域。
ctx.useBoxCount()当前区域数量,调试用。

两个角点的顺序无所谓,内部会自动取最小/最大。

event 有什么

字段说明
box命中的区域 id
car车厢序号。装饰物件恒为 0。
x y z命中点的局部坐标 —— 射线打在区域表面的那一点,不是区域中心。
distance玩家眼睛到命中点的距离。
dirX dirY dirZ视线方向的单位向量,已变换到局部坐标系。
player玩家包装对象。
item主手物品 id,空手为 minecraft:air
offhandItem副手物品 id。
sneaking是否正在潜行。

有了 x/y/z,一个大区域也能当多个按钮用 —— 比如整块驾驶台声明成一个 box, 再按命中点的 X 判断按的是哪个键。

触发条件

条件
车厢玩家正在乘车,且右键命中已登记的区域。最大距离 5 格。
装饰物件右键该方块。未命中任何区域时 eventnull
没登记区域就完全不介入。 不写 addUseBox 的脚本,右键行为和以前一模一样 —— 不会莫名其妙吃掉玩家的操作。

为什么是「先声明、后回调」

脚本跑在独立线程,主线程不可能等它返回再决定要不要拦下这次右键。 所以反过来:区域由脚本预先登记,主线程自己做射线命中,命中了才拦截原版右键, 然后把事件异步派发给脚本。

装饰物件的写法

装饰物件的 use 多一个 player 参数,event 在最后:

function create(ctx, state, block) {
  ctx.addUseBox("switch", -0.2, 0.9, -0.5, 0.2, 1.1, -0.4);
}

function use(ctx, state, block, player, event) {
  if (event && event.box === "switch") {
    state.on = !state.on;
  }
}

老脚本的 use(ctx, state, block, player) 照常工作 —— event 是追加在末尾的, 不写它不影响任何已有代码。

带后缀的变体

和其他入口一样,useTrain / useBlock 同样会被调用,两组都定义则都执行。