Posted on :: Updated on :: Tags: , ,

最近做了个粘土人, 想剪一个逐层切片教程, 要展示建筑的每一个横截面, 而且要有平滑石头这种有边框的方块作为背景参考. 突发奇想, 想试试能不能通过 MCScript 来调用 WorldEdit 的命令来实现.

首先验证一下选区和填充方块能不能实现:

let X1: int = -2;
let Z1: int = -2;
let X2: int = 2;
let Z2: int = 2;

fn main() {
    // 设置选区的起点和终点
    run_command!("/pos1 {},0,{}", X1, Z1);
    run_command!("/pos2 {},0,{}", X2, Z2);
    // 用石头填充选区
    run_command!("/set stone");
}

编译运行, 发现选区(前两条命令)能够正常选取, 但是到第三条命令就没有反应了.

原理上, MCScript 编译器会为每一个 run_command! 调用生成一个 mcfunction 函数文件. 例如上面的三条命令会被分别生成在 custom_cmd_0.mcfunction, custom_cmd_1.mcfunctioncustom_cmd_2.mcfunction 中. 检查其中后两个文件, 内容如下:

// custom_cmd_1.mcfunction

$/pos2 $(0),0,$(1)
// custom_cmd_2.mcfunction

/set stone

有的玩家可能没有见过 $ 符号. 这里解释一下, 这是 Java 版 1.20.2 中加入的新功能 - . 从此版本起, mcfunction 函数中的命令可以以 $ 开头, 其后必须包含一个或以上的宏参数 $(<parameter>). 运行该函数时, 将把命令中的 $(<parameter>) 部分直接替换为相应参数 <parameter> 的值的字符串形式. 例如, 可以通过下面的方式调用 custom_cmd_1.mcfunction:

function my_namespace:custom_cmd_1 {0: 114, 1: 514}

custom_cmd_1.mcfunction 中的命令将会被解析为

/pos2 114,0,514

Minecraft 在运行 mcfunction 函数前, 会先判断函数中的每一条命令是否是一条正确的 Minecraft 命令 (不包括模组命令). 也就是说, 如果在函数中直接加入一条类似于 /set stone 的模组命令, Minecraft 是会拒绝运行的. 这也就是上面的 custom_cmd_2.mcfunction 没有成功运行的原因. 可是为什么前两个带有宏的函数能够成功运行呢? 因为在运行带有宏的命令前, Minecraft 无法提前判断这条命令是否合法, 只能假设他是合法的, 并尝试解析和执行. 前两个函数正是因为带有宏, 所以绕过了游戏的检查, 得以成功执行.

这么一来, 想要通过函数运行 /set stone 这样的模组命令, 只要在命令的末尾加上一个宏参数, 并在运行的时候给这个参数传入一个空字符串即可:

// custom_cmd_2.mcfunction
$/set stone$(empty_str)
// 调用方式
function my_namespace:custom_cmd_2 {empty_str: ""}

于是我在 MCScript 中加入了 run_mod_command!. 有别于 run_command!, 在命令中不含任何参数时, run_mod_command! 会在命令的末尾自动加上一个 $(empty_str) 参数, 以实现对模组命令的调用.

下面就是切片展示工具的完整代码:

let X1: int = -15;
let Z1: int = -15;
let X2: int = 15;
let Z2: int = 15;

let Y1: int = 14;
let Y2: int = -12;

let y: int = Y1;

// 每调用一次此函数, 展示下一层横截面
fn slice_step() {
    if y < Y2 {
        run_mod_command!("/undo");
        return;
    }
    run_command!("say current y: {}", y - 1);

    if y != Y1 {
        run_mod_command!("/undo");
    }
    run_mod_command!("/pos1 {},{},{}", X1, y, Z1);
    run_mod_command!("/pos2 {},{},{}", X2, y, Z2);
    run_mod_command!("/set air");
    run_mod_command!("/pos1 {},{},{}", X1, y - 2, Z1);
    run_mod_command!("/pos2 {},{},{}", X2, y - 2, Z2);
    run_mod_command!("/set smooth_stone");
    run_command!("tp @s {} {} {} 0 90", (X1 + X2) / 2, y + 20, (Z1 + Z2) / 2);
    y -= 1;
}

可以看到效果还是挺不错的. 最主要的是, 它真的很快! 只要修改起点和终点坐标, 一分钟之内就能得到建筑的各层切片截图. 拖更理由减一