跳转至

JavaScript 基础速成(KubeJS 版)

原文:原创教程(ぴよまる)

KubeJS 用 JavaScript(JS) 写脚本——不用编译、不用写 Java,把 .js 文件丢进 kubejs/ 文件夹就能改游戏。这篇教程用最小必要的 JS 知识带你上手 KubeJS,会 Java 的玩家对照着看会很快。

1. 脚本放哪里

先记住三个文件夹(对应三种时机):

kubejs/                // 这是 KubeJS 脚本的根文件夹,里面按用途分成三个子文件夹
├── startup_scripts/   # 启动时执行一次(注册物品/方块/事件监听)  // 这个文件夹的脚本在游戏启动时执行一次
├── server_scripts/    # 服务端运行(配方/标签/方块/实体/玩家)    // 这个文件夹的脚本在服务端(服务器)运行
└── client_scripts/    # 客户端运行(渲染/提示文本/GUI)          // 这个文件夹的脚本在客户端(游戏本体)运行

改完脚本进游戏执行 /reload(服务端)或 F3+T(客户端)即可生效。

2. 变量:let / const

JS 用 let 声明可变变量,用 const 声明不可重新赋值的常量:

// let:声明一个"可变"变量。等号右边的 0 是初始值,意思是"把 0 存进名为 count 的变量里"
let count = 0          // 可以改
// 重新把 count 的值改成 5。let 声明的变量之后可以随时改值,所以这行没问题
count = 5              // OK
// (空行:只是排版分隔,把两段代码分开,没有实际作用)
// const:声明一个"常量",值一旦定下来就不能再重新赋值
const serverName = '落英'   // 不能重新赋值
// 下面这行是注释掉的代码(前面有 // 表示不会执行):如果取消注释,给常量重新赋值会直接报错
// serverName = 'xxx'       // 报错!

⚠️ 老教程里常见的 var 也能用,但推荐用 let/const(作用域更安全)。KubeJS 的 Rhino 引擎支持 ES6 语法。

3. 数据类型

// 数字类型:整数和小数都用数字表示,不需要区分
let number = 42            // 数字(整数/小数都一样)
// 字符串类型:用单引号 ' ' 把一串文字包起来,表示这是一段文本
let text = 'hello'         // 字符串,单引号
// 字符串也可以用双引号 " " 包,效果和单引号完全一样
let text2 = "world"        // 字符串,双引号也行
// 布尔类型:只有两个值,true(真)和 false(假),用来表示"是/否"
let bool = true            // 布尔:true / false
// 数组类型:用方括号 [ ] 装多个值,值之间用逗号隔开
let arr = ['a', 'b', 'c']  // 数组
// 对象类型:用花括号 { } 存"键值对",冒号左边是键(名字),右边是值,多个键值对用逗号隔开
let obj = { name: 'pyz', level: 99 }  // 对象(键值对)

模板字符串(拼接文本最方便,用反引号):

// 定义一个字符串变量 playerName,内容是 'Steve'
let playerName = 'Steve'
// console.log() 的作用是把内容打印到日志文件里,方便调试
// 这里用的是反引号 ` ` 包起来的"模板字符串",
// 里面的 ${playerName} 会被自动替换成变量 playerName 的值
console.log(`欢迎 ${playerName} 来到服务器!`)   // 欢迎 Steve 来到服务器!

4. 对象:KubeJS 里到处都是对象

KubeJS 的事件参数、物品、方块都是对象,用 . 访问属性/方法:

// 监听"配方"事件:游戏加载配方时,会执行后面这个箭头函数(大括号里的代码)
ServerEvents.recipes(event => {
  // event.shaped() 是"添加有序合成配方"的方法,需要按 3x3 图案摆放材料
  // 第 1 个参数:产物,'minecraft:diamond' 表示合成出钻石
  // 第 2 个参数:3x3 图案,'A'、'B' 代表不同材料,空格代表那个位置不放东西
  // 第 3 个参数:花括号里的对象,说明图案里每个字母对应哪种材料
  event.shaped('minecraft:diamond', ['AAA', 'ABA', 'AAA'], {
    A: 'minecraft:stone',   // 图案里所有 A 的位置都放石头
    B: 'minecraft:iron_ingot'   // 图案里所有 B 的位置都放铁锭
  })
})   // 事件回调(箭头函数)结束

这里 event 就是一个对象,event.shaped(...) 是调用它的方法;A: 'minecraft:stone' 是给对象传键值对。

5. 数组操作(非常常用)

// 定义一个数组 list,里面先放两个物品 ID(都是字符串)
let list = ['minecraft:stone', 'minecraft:iron_ingot']
// (空行:只是排版分隔,没有实际作用)
// push() 方法:往数组的末尾追加一个新元素
list.push('minecraft:gold_ingot')   // 末尾追加
// forEach() 方法:遍历(挨个处理)数组里的每一个元素
// item 是变量名,会依次代表数组里的每个元素;箭头函数里的代码会对每个元素执行一次
list.forEach(item => {              // 遍历每个元素
  console.log(item)   // 把当前这个元素(物品 ID)打印到日志
})

配合配方/标签移除很好用:

// 监听"物品标签"事件:处理 item 类型的标签时,执行大括号里的代码
ServerEvents.tags('item', event => {
  // 一次性给多个物品加标签
  // 直接写一个数组,然后调用 forEach 遍历;id 会依次变成数组里的每个物品 ID
  ['minecraft:stone', 'minecraft:cobblestone'].forEach(id => {
    event.add('forge:stone', id)   // event.add() 把物品加进标签:把 id 对应的物品加进 forge:stone 标签
  })
})

6. 箭头函数:KubeJS 的心脏

KubeJS 的 API 几乎都是「事件监听 + 回调函数」模式。箭头函数 => 就是简写函数:

// 完整写法
// function 是定义函数的关键字:这里定义一个"接收一个参数 event"的函数
// 意思是给 ServerEvents.recipes 注册一个回调函数,
// 当配方事件发生时就会调用这个函数,并把事件信息放进 event 参数里
ServerEvents.recipes(function (event) {
  // ...
})

// 箭头函数写法(推荐,KubeJS 文档都用这个)
// => 是"箭头函数"的符号,作用和上面的 function 写法完全一样,只是更简洁
// event 是参数,{ } 里是函数体(事件发生时执行的代码)
ServerEvents.recipes(event => {
  // ...
})
  • event => { ... } = 接收一个参数 event,执行花括号里的代码
  • 只有一个参数时可以省略括号:event => ...
  • 多个参数要括号:(a, b) => ...

事件监听三步走什么事件 + => + { 做什么 }

// 监听"实体死亡"事件:游戏里任何实体死亡时,执行大括号里的代码
EntityEvents.death(event => {
  let entity = event.entity   // event.entity 是"死亡的那个实体"对象,先存进变量 entity 里
  if (entity.type === 'minecraft:creeper') {   // 判断:这个实体的类型是不是苦力怕(=== 表示严格相等)
    console.log('一只苦力怕炸了!')   // 是苦力怕,就往日志打印这句话
  }   // if 判断结束
})   // 事件回调结束

7. 条件判断:if / else

// 监听"玩家登录"事件:有玩家进入服务器时,执行大括号里的代码
PlayerEvents.loggedIn(event => {
  let player = event.player   // event.player 是"登录的这个玩家"对象,先存进变量 player 里
  // (空行:只是排版分隔,没有实际作用)
  // if:条件判断。如果玩家的用户名严格等于 'pyz',就执行下面花括号里的代码
  if (player.username === 'pyz') {
    player.tell('欢迎回来,主人!')   // player.tell() 是给这个玩家发一条消息
  } else if (player.username === 'Steve') {   // 否则如果用户名是 'Steve',就执行这里的代码
    player.tell('你好,Steve!')
  } else {   // 上面两个条件都不满足时(也就是其他所有玩家),执行这里的代码
    player.tell('欢迎来到服务器!')
  }
})

比较运算符===(严格相等)、!==(不相等)、> < >= <=&&(且)、||(或):

// if:条件判断。只有"level 大于等于 30"并且"hasItem 为真"两个条件都满足时,
// 才会执行下面花括号里的代码(&& 表示"并且",两边的条件都要成立)
if (level >= 30 && hasItem) {
  // 等级≥30 并且 有物品
}

⚠️ JS 推荐用 === 而不是 ==== 会做类型转换,容易出诡异 bug)。

8. 循环

for(知道次数):

// for 循环:让花括号里的代码重复执行若干次
// 第 1 部分 let i = 0:从 0 开始数(i 是计数器变量,初始值是 0)
// 第 2 部分 i < 5:只要 i 小于 5 就继续循环
// 第 3 部分 i++:每执行完一次循环体,i 就自动加 1
// 所以 i 会依次变成 0、1、2、3、4,循环体一共执行 5 次
for (let i = 0; i < 5; i++) {
  console.log('第 ' + i + ' 次')   // 用 + 把文字和变量 i 拼成一个字符串,再打印到日志
}

forEach(遍历数组,最常用):

// 定义一个数组,里面装着三种矿石的物品 ID(字符串)
let ores = ['minecraft:iron_ore', 'minecraft:gold_ore', 'minecraft:diamond_ore']
// forEach:遍历数组,ore 会依次变成数组里的每一个元素
ores.forEach(ore => {
  console.log(ore)   // 把当前这个矿石 ID 打印到日志
})

9. 字符串与物品 ID

Minecraft 物品 ID 就是字符串:'minecraft:stone' = 命名空间 + 路径。

KubeJS 里经常要拼 ID:

// 定义一个变量 modId(模组 ID),值是 'kubejs'
let modId = 'kubejs'
// 定义一个变量 itemId(物品路径名),值是 'my_item'
let itemId = 'my_item'
// (空行:只是排版分隔,没有实际作用)
// 拼成 'kubejs:my_item'
// 这里用的是模板字符串(反引号):把 modId 的值、冒号、itemId 的值连起来
// 其中 ${modId} 会被替换成 'kubejs',${itemId} 会被替换成 'my_item'
let fullId = `${modId}:${itemId}`

判断物品是不是某个 ID:

// 监听"合成物品"事件:玩家在合成台合成出物品时,执行大括号里的代码
ItemEvents.crafted(event => {
  // event.item 是"合成出来的物品"对象,.id 是它的物品 ID
  // 判断这个 ID 是不是钻石(=== 表示严格相等)
  if (event.item.id === 'minecraft:diamond') {
    event.player.tell('你合成了钻石!')   // 是钻石,就给这个玩家发一条消息
  }
})

10. 函数的定义与复用

写复杂脚本时,把逻辑拆成函数:

// 定义函数:判断是不是矿石
// function 是定义函数的关键字,isOre 是函数名,id 是参数(调用时传入的物品 ID)
function isOre(id) {
  // return 把计算结果交还给调用它的地方
  // id.includes('_ore'):判断字符串里是否包含 '_ore' 这几个字,包含就返回 true
  // || 表示"或者":ID 里含有 '_ore' 或含有 '_raw',就返回 true(判定为矿石)
  return id.includes('_ore') || id.includes('_raw')
}
// (空行:只是排版分隔,没有实际作用)
// 监听"合成物品"事件
ItemEvents.crafted(event => {
  // 调用自己刚定义的函数 isOre(),把合成出来的物品 ID 传进去,得到 true 或 false
  if (isOre(event.item.id)) {
    event.player.tell('挖到矿啦!')   // 是矿石,就给玩家发消息
  }
})
  • function 名字(参数) { return 返回值 }
  • return 把结果交出去,没有 return 的函数返回 undefined

11. 常用 KubeJS 写法模式

① 给物品加合成配方:

// 监听"配方"事件:游戏加载配方时,执行大括号里的代码
ServerEvents.recipes(event => {
  // 有序合成:3x3 图案
  // event.shaped() 添加"有序合成"配方:必须按指定图案摆放材料才能合成
  // 第 1 个参数:产物 '4x minecraft:iron_ingot',4x 表示一次合成出 4 个铁锭
  // 第 2 个参数:3x3 图案,A 代表放材料的位置,空格代表留空
  // 第 3 个参数:对象,说明图案里字母 A 对应哪种材料
  event.shaped('4x minecraft:iron_ingot', ['AAA', 'A A', 'AAA'], {
    A: 'minecraft:stone'   // 图案里所有 A 的位置都放石头
  })
  // (空行:只是排版分隔,没有实际作用)
  // 无序合成:任意摆法
  // event.shapeless() 添加"无序合成"配方:材料怎么摆都行
  // 第 1 个参数:产物 'minecraft:stick'(木棍)
  // 第 2 个参数:材料列表,需要两个橡木木板
  event.shapeless('minecraft:stick', ['minecraft:oak_planks', 'minecraft:oak_planks'])
  // (空行:只是排版分隔,没有实际作用)
  // 移除配方
  // event.remove() 删除已有的配方;花括号里的对象是筛选条件
  // 这里表示:删掉所有"产物是钻石剑"的配方
  event.remove({ output: 'minecraft:diamond_sword' })
})

② 给物品加提示文本(tooltip):

// 监听"提示文本"事件:玩家把鼠标悬停在物品上时,执行大括号里的代码
ItemEvents.tooltip(event => {
  // event.add() 给指定物品添加提示文本(鼠标悬停时显示的小字)
  // 第 1 个参数:物品 ID,'kubejs:my_item' 是要加提示的那个物品
  // 第 2 个参数:文本数组,数组里的每个字符串会显示成一行
  event.add('kubejs:my_item', ['这是一件神器!', '右键可以召唤闪电'])
})

③ 自定义物品(启动脚本):

// 监听"注册物品"事件(要放在 startup_scripts 文件夹里):游戏启动时执行
StartupEvents.registry('item', event => {
  // event.create() 创建一个新物品,'thunder_sword' 是这个物品的 ID
  // 后面用的是"链式调用":.方法() 会返回对象本身,所以能一直 .方法() 接下去
  event.create('thunder_sword')
    .displayName('雷电之刃')   // 设置物品在游戏里显示的名字
    .maxStackSize(1)   // 设置一组最多堆叠 1 个(这个物品不能堆叠)
    .tooltip('右键召唤闪电')   // 设置鼠标悬停时显示的提示文本
})

这就是 KubeJS 的链式调用.create() 返回对象,后面可以一直 .方法() 接下去。

12. 常见坑与调试

① 代码块(JSON)里别加注释 —— event.remove({output: 'xxx'}) 这种花括号里是对象,不是代码块。

② 用 console.log 调试:

// 监听"配方"事件:游戏加载配方时,执行大括号里的代码
ServerEvents.recipes(event => {
  // console.log() 把文字打印到日志文件(logs/kubejs/server.txt)
  // 用来确认这段代码有没有被执行到,是最常用的调试方法
  console.log('配方事件触发!')
})

日志在游戏目录 logs/kubejs/server.txt(或 latest.log)。

③ 注意事件类型对应文件夹: - StartupEvents.* → startup_scripts - ServerEvents.* / BlockEvents.* / EntityEvents.* / PlayerEvents.* → server_scripts - ClientEvents.* / ItemEvents.tooltip → client_scripts

放错文件夹 = 事件不触发。

④ 大小写敏感ServerEvents 不能写成 servereventsevent.player.tell() 不能写成 Event.Player.Tell()

⑤ 字符串引号:外层单引号 ',里面要写引号时用双引号 ",或者反过来。

13. 推荐学习路径

  1. 先抄官网示例(本文档站的「配方」「标签」「自定义物品」篇都是现成例子)
  2. 把示例改成自己的物品/配方,理解事件+回调模式
  3. 学会 console.log 调试
  4. 需要什么功能,去「事件列表」篇找对应事件
  5. 进阶:查 KubeJS 在线 API 文档(按 K 键或 /kubejs hand 看物品信息)

💡 记住核心一句话:KubeJS 脚本 = 监听事件 + 用 JS 操作游戏对象。看懂事件列表,你就掌握了 80% 的 KubeJS。