How to Draw a Sphere - HeartyYF/fabric-carpet-Wiki-CN GitHub Wiki
Carpet 内置了了绘制基本图形的工具,球体包含在其中,所以要画一个球实际上并不需要用到 scarpet(译者注:好了,此帖终结),但是用 scarpet 试一试也是一个不错的练习。【而且/draw sphere
只会扫描绘制球体所必需的那些方块,所以它更加高效,比下文中的任何一个方法都要更快。但是因为同时填充这么多的方块总会让游戏卡顿那么一两秒,所以你也感觉不出来这个效率的提升就是了。】在下面的例子中,我们会画一个球心在 (100, 100, 100),半径为 50 个方块的球。要清空绘制区域,只需要运行这个命令:
/script run scan(100,100,100,50,50,50,set(_,'air'))
要画一个球,最简单的方法就是用 draw 命令:
/draw sphere 100 100 100 50 glass
或者如果要在已有的地形上绘制而不破坏除空气以外的任何方块,则可以这样:
/draw sphere 100 100 100 50 glass replace air
要想安全地删除这个球而不破坏已有的方块,只需要用空气方块把这个球填充回去:
/draw sphere 100 100 100 50 air replace glass
但是这样没有用到数学方法。要发挥数学的威力,我们就要用到 scarpet 脚本了:
注意:如果要在很大的区域内绘制,你需要先用 /carpet fillLimit
进行一番调整,因为 /script fill
和 /script scan
受到 /fill
命令的范围限制,一次最多只能填充 32000 方块。
这个命令用起来和原版 /fill
差不多,在 <expression>
条件为真的位置绘制方块。第一个坐标参数 <origin>
表示 <expression>
中各坐标的参考点,换言之,<expression>
中的 x
,y
和 z
会以 <origin>
为原点。将 <origin>
设为 (0,0,0) 就相当于用绝对坐标来绘制,但是把原点平移到球心去可以极大地简化我们的 <expression>
表达式。
既然这个球的半径是 50,以 (100, 100, 100) 为球心,那么绘制的范围就是 (50, 50, 50) 到 (150, 150, 150)。
首先来画一个实心球:
/script fill 100 100 100 50 50 50 150 150 150 "x*x + y*y + z*z <= 50*50" glass replace air
对于空心球而言,将 <=
号简单替换成 ==
号效果并不好,只有少数的点能匹配上,因此我们要稍微绕个圈子:
/script fill 100 100 100 50 50 50 150 150 150 "round(sqrt(x*x + y*y + z*z)) == 50" glass replace air
注意:因为 <expression>
后面还有其他参数,这里必须加上引号。
这个命令要更通用一些,它会扫描一个区域内的所有方块,然后执行命令。这里我们不再依赖 /draw
或者 /fill
来画方块,而是手动使用 set
命令:
/script scan 100 100 100 50 50 50 150 150 150 if( round(sqrt(x*x + y*y + z*z)) == 50, set(_, 'glass'))
如果要避免破坏已有的方块则这样:
/script scan 100 100 100 50 50 50 150 150 150 if( air(_) && round(sqrt(x*x + y*y + z*z)) == 50, set(_, 'glass'))
最后一个例子中我们连 scan 和 fill 提供的坐标系统也不用了,所有的运算都由我们自己来做:
/script run
c = l(100,100,100);
r = 50;
scan(c,r,r,r,
if ( air(_),
v=pos(_)-c;
if( round(sqrt(v:0*v:0+v:1*v:1+v:2*v:2))==r,
set(_, 'glass')
)
)
);
有点复杂、没看懂?这是因为我们要自己去设置绘画的位置。首先我们设置了球心 c 和半径 r,然后用 scan 函数(译者注:你这不还是用了吗……)在区域内逐个方块迭代。我们希望跳过所有非空气方块(包括 'cave_air'
和 'void_air'
)。然后定义向量 v 表示当前点到圆心 c 的位移,最后检查 v 的长度是不是与定义好的半径 r 相等。这有点多此一举(译者注:你还知道啊?),但是我们成功了。实际上比起把各个坐标列出来,用 reduce 函数可以稍微简化一点点:
/script run
c = l(100,100,100);
r = 50;
scan(c,r,r,r,
if ( air(_) && round(sqrt(reduce(pos(_)-c, _a+_*_, 0)))==r,
set(_, 'glass')
)
);
本篇由 @茨月 翻译。