Skip to content

宏函数

太长不看

这是内部实现细节 — 你不需要理解它就能使用 RedScript。一切都是"自动工作"的。

但如果你好奇:"等等,MC 命令不支持在坐标位置用变量啊... 这怎么做到的?" — 本章解释幕后的魔法。

RedScript 自动使用 MC 1.20.2+ 的函数宏功能,允许在通常需要编译时字面量的位置(坐标、实体类型等)使用运行时变量。

问题

在原版 Minecraft 命令中,某些值必须是字面量:

mcfunction
# ❌ 这不行 - 坐标不能是计分板值
teleport @s $x $y $z

解决方案

RedScript 自动检测你在这些位置使用变量的情况,并编译为宏语法:

rs
fn spawn_at(x: int, y: int, z: int) {
    summon("minecraft:zombie", x, y, z);
}

// 用运行时值调用
let px: int = get_player_x();
spawn_at(px, 64, 100);

编译结果:

mcfunction
# spawn_at.mcfunction
$summon minecraft:zombie $(x) $(y) $(z)

# 调用处:
execute store result storage rs:macro_args x int 1 run scoreboard players get $px rs
data modify storage rs:macro_args y set value 64
data modify storage rs:macro_args z set value 100
function ns:spawn_at with storage rs:macro_args

自动优化

RedScript 智能决定何时使用宏:

rs
spawn_at(100, 64, 200);   // 全常量 → 直接内联,不用宏
spawn_at(px, 64, pz);     // 有变量 → 使用宏

支持的内置函数

所有内置函数都支持宏参数。 任何参数都可以是运行时变量:

rs
fn dynamic_say(msg: string) {
    say(msg);  // 可以!
}

fn dynamic_effect(eff: string, dur: int) {
    effect(@s, eff, dur, 1);  // 可以!
}

fn dynamic_setblock(x: int, y: int, z: int, block: string) {
    setblock(x, y, z, block);  // 可以!
}

包括 saytellgiveeffectsummonteleportparticlesetblockfillcloneplaysoundweathertime_setgameruletag_addtag_remove 以及所有其他内置函数。

相对坐标偏移(~ident

你可以用 ~变量名 语法将一个变量作为相对坐标偏移

rs
fn launch_up(target: selector, height: int) {
    tp(target, ~0, ~height, ~0);   // 在 Y 轴相对当前位置偏移 height 格
}

为什么这里必须用宏?

Minecraft 的 ~N 语法要求 N 是字面量数字,不存在任何命令能把计分板值作为相对偏移读取。

~height 的含义是*「从当前位置相对偏移 height 格」*,与把 height 作为绝对坐标传入是本质不同的。在 MC 命令层面,唯一的实现方式是在运行时把数值替换进命令文本中,这正是 1.20.2+ 函数宏做的事。

如果没有宏,这在 MC 中是不可能实现的。 替代方案是预先算出绝对 Y 值传入——但那样就失去了相对移动的语义。

编译结果:

mcfunction
$tp $(target) ~0 ~$(height) ~0

调用时通过 function ns:launch_up with storage rs:macro_args,宏在运行时将 height 的值替换进波浪号偏移中。

示例:动态传送

rs
struct Waypoint {
    x: int,
    y: int,
    z: int
}

fn tp_to_waypoint(wp: Waypoint) {
    teleport(@s, wp.x, wp.y, wp.z);
}

@keep fn go_home() {
    let home: Waypoint = { x: 100, y: 64, z: -200 };
    tp_to_waypoint(home);
}

示例:粒子网格

rs
fn spawn_particle_at(x: int, y: int, z: int) {
    particle("minecraft:flame", x, y, z, 0, 0, 0, 0, 1);
}

@keep fn draw_grid() {
    for (let i: int = 0; i < 10; i = i + 1) {
        for (let j: int = 0; j < 10; j = j + 1) {
            spawn_particle_at(i * 2, 64, j * 2);
        }
    }
}

要求

  • Minecraft 1.20.2 或更高版本(函数宏在此版本添加)
  • 早期版本无法使用宏编译的函数

工作原理

  1. 检测:编译时,RedScript 扫描函数体
  2. 标记:在需要字面量位置使用的参数被标记
  3. 生成:被标记的函数生成 $ 前缀的命令
  4. 调用处:变量参数通过 NBT storage 传递

这一切都是自动的 - 你不需要任何特殊语法或装饰器。

下一步

Released under the MIT License.