脚本核心功能¶
本页面介绍脚本API中最常用的核心功能:事件系统、任务调度、动态属性,以及runCommand方法。
事件系统¶
脚本API事件分为后置事件(afterEvents)和前置事件(beforeEvents)。
- 后置事件:在游戏行为已经发生后触发,脚本只能做出响应,无法取消。
- 前置事件:在游戏行为实际执行前触发,脚本可以通过
event.cancel = true阻止该行为。但此时处于受限执行模式,不能修改世界状态。
事件来源¶
| 来源 | 对象 | 说明 |
|---|---|---|
| 世界事件 | world.afterEvents / world.beforeEvents | 与游戏世界直接相关的事件 |
| 系统事件 | system.afterEvents / system.beforeEvents | 与引擎运行时相关的事件 |
| 脚本事件 | world.afterEvents.scriptEventReceive | 通过/scriptevent命令触发 |
订阅与取消订阅¶
import { world } from "@minecraft/server";
// 订阅并保存句柄
const subscription = world.afterEvents.playerSpawn.subscribe(
({ player, initialSpawn }) => {
if (initialSpawn) {
player.sendMessage(`欢迎来到服务器,${player.name}!`);
}
}
);
// 取消订阅
world.afterEvents.playerSpawn.unsubscribe(subscription);
脚本事件¶
通过/scriptevent <命名空间:事件名> [消息]命令可以从命令方块或聊天框向脚本发送消息:
world.afterEvents.scriptEventReceive.subscribe((event) => {
const { id, message, sourceType } = event;
if (id === "wiki:greet") {
world.sendMessage(`脚本收到问候:${message}`);
}
});
在游戏中用命令触发:/scriptevent wiki:greet 你好
事件过滤器
部分事件支持通过订阅时传入过滤器对象来减少不必要的触发,例如scriptEventReceive支持按命名空间过滤:
任务调度¶
脚本API通过system对象提供了多种调度方式。
单次延迟(system.run / system.runTimeout)¶
system.run(callback)会在下一刻执行一次回调,是最常用的"推迟执行"手段:
import { system } from "@minecraft/server";
system.run(() => {
world.sendMessage("这条消息在下一刻发送。");
});
system.runTimeout(callback, ticks)可指定延迟刻数(20刻约等于1秒):
周期性执行(system.runInterval)¶
system.runInterval(callback, ticks)会按固定间隔重复执行,返回一个任务ID:
const tickId = system.runInterval(() => {
world.sendMessage("每秒广播一次。");
}, 20);
// 停止周期任务
system.clearRun(tickId);
生成器任务(system.runJob)¶
当需要执行耗时操作(如大范围方块扫描)而不导致卡顿时,可以使用生成器任务。脚本会在每刻只处理生成器中yield之前的部分,将长时任务分散到多个刻中:
function* scanBlocks() {
for (let x = -100; x <= 100; x++) {
for (let z = -100; z <= 100; z++) {
const block = world.getDimension("overworld").getBlock({ x, y: 64, z });
if (block?.typeId === "minecraft:diamond_ore") {
console.log(`找到钻石矿:${x}, 64, ${z}`);
}
yield; // 每次循环后让出控制权,下一刻继续
}
}
}
system.runJob(scanBlocks());
动态属性¶
动态属性(Dynamic Properties)允许脚本将自定义数据存储在世界或实体上,数据会随存档持久化。
存储与读取¶
import { world } from "@minecraft/server";
// 存储到世界
world.setDynamicProperty("wiki:playerDeaths", 0);
// 读取
const deaths = world.getDynamicProperty("wiki:playerDeaths");
// 返回 number | string | boolean | Vector3 | undefined
// 存储到实体
const player = world.getAllPlayers()[0];
player.setDynamicProperty("wiki:firstLoginTime", Date.now().toString());
支持的数据类型¶
动态属性支持boolean、number、string和Vector3。如需存储复杂对象,可以序列化为JSON字符串:
const data = { kills: 5, deaths: 2 };
player.setDynamicProperty("wiki:stats", JSON.stringify(data));
const raw = player.getDynamicProperty("wiki:stats");
const stats = raw ? JSON.parse(raw) : { kills: 0, deaths: 0 };
字符串属性大小限制¶
单个字符串属性最大为32767字符,超过将抛出错误。如需存储更多数据,可以将数据分段存储为多个属性。
枚举所有动态属性¶
const keys = world.getDynamicPropertyIds();
for (const key of keys) {
const value = world.getDynamicProperty(key);
console.log(`${key} = ${value}`);
}
runCommand¶
脚本可以通过runCommand在实体或维度上执行命令,但有一定限制。
import { world, system } from "@minecraft/server";
world.afterEvents.playerSpawn.subscribe(({ player, initialSpawn }) => {
if (!initialSpawn) return;
const dimension = world.getDimension("overworld");
const result = dimension.runCommand(
`give ${player.name} minecraft:diamond 1`
);
console.log(`命令状态码:${result.successCount}`);
});
多条命令的执行¶
runCommand是同步执行的,但游戏内部处理可能有一帧延迟。若需要顺序执行多条命令且依赖上一条的结果,使用runTimeout间隔执行:
system.run(() => {
dimension.runCommand("summon minecraft:creeper 0 64 0");
system.runTimeout(() => {
dimension.runCommand("kill @e[type=minecraft:creeper,r=5]");
}, 2);
});
runCommand的限制¶
部分命令不能通过脚本的runCommand执行:
/fog- 脚本无法管理玩家的迷雾设置。
/stopsound- 脚本无法通过命令停止音效。音乐可以使用
World.stopMusic()或Player.stopMusic()停止。 /dialogue- 没有专用的脚本方法控制NPC对话,但可以用
player.runCommand("dialogue open @e[tag=npc,r=5] @s 场景标签")作为变通方案。