本帖最后由 Dahesor 于 2021-12-9 01:29 编辑

命令教程“真”从零开始 (六) /tag指令,与进阶~选择器参数 [1.18]

声明:
1. 本教程默认所有读者看过本系列之前的内容。作者不对可能冒出来的术语或方言负责。以前帖的链接在最后。
2. 本系列教程默认读者拥有关于Minecraft游戏的基础了解。
3. 本系列全部教程适用于当前Java最新版(1.18)。
4. 本系列教程致力于基础原理而非使用方法,因为某些原因,这是本声明里最重要的一条。


前言


我们的指令教程已经有了5帖了,这已经是计划的8分之1。
但是,在前5帖中,你可能一直在冒出关于选择器的的疑问。
喂,别告诉我你忘了什么是选择器,那个在第一帖就讲了。
我们当前接触到的选择器只有五种 * @a(选择所有玩家)@p(选择最近玩家)@r(选择随机玩家)@e(选择所有 实体 )@s(选择执行者)
好像够用了?
好吧,一点都不够用。
仅仅使用上面5个选择器,我们无法选择单一一只苦力怕,不知道如何选择处于特定位置的玩家——一点都不够用。
所以今天,我们就来探讨一下——如何更精确的选择?
这就是本帖的内容—— 选择器参数(Selector parameter)

* 教育版的选择器不考虑在内


标签 /tag


让我们从一个实例开始:
随机分队

第4帖 我们讲了队伍指令,(不懂请回去看)相信你已经......有一些队伍竞技地图的构思了吧!
那,今天的问题就是,如何,随机的,把玩家添加至数个不同的队伍中?

假设,我们有10个玩家进行随机分队,分成红蓝两队,每队5人——你该怎么办?
找一下上方的选择器,涉及到随机的是 @r(选择1个随机玩家)
看起来我们是要用这个选择器喽!
不错嘛,10位玩家,把指令: /team join @r red 运行5次就可以把五名玩家添加进红队(red)了......是吗?
选择器@r全称应该是 “在所有玩家中随机选择一名” ,那......如果重复运行上述指令,有可能重复选择。@r可能抽中已经加入了红队的玩家,这样就无法确定肯定有5名玩家在红队中。
如果你运气不好,甚至可能从始至终都抽中一人。
我们该如何以一种更精确的方式区分不同玩家?
容我介绍下面这一条指令——
标签/tag
这条指令可以为实体添加标签,并可以使用选择器参数来选择拥有特定标签的实体——
注意,本指令在Java1.12.2及以前,与基岩版中仍为/scoreboard命令的一部分。
你可以前往 Wiki本页面 ,或 阅读本帖 以获得相关信息。

首先,让我们添加一个 标签
格式:
  1. /tag <目标> add <名称>
复制代码
<名称>处填写一个任意英文字符串,只要你能分得清而且不重复就好。
举个例子:
  1. /tag @r add choose
复制代码
本指令会为一名随机玩家添加一个叫做choose的标签。
任何实体都可以插入 数个不同 的标签。
嗯嗯,你可能到这里看得有点迷糊,标签是干嘛用的?
你可以使用 选择器参数 选择拥有特定标签的玩家

@a
这是我们的选择器,选择所有玩家。
想要添加一个参数,紧接着它打上中括号“[]”:
@a[]

中括号内部打上你要的参数:
参数名=值
啊啊,翻译一下:
选择标准=值
我们的选择标准是标签: tag
我们要选择的标签是前面添加的 choose
两者联起来就是“tag=choose”。
添加到选择器后的中括号内:
@a[tag=choose]
本选择器会选择所有带有choose标签的玩家。
你当然可以使用其他选择器:
  1. @r[tag=choose]
复制代码
本选择器会从所有 带有choose标签的玩家 选择一位

  1. @p[tag=win]
复制代码
本选择器会选择带有 win标签 最近 玩家

把它添加到以前讲过的指令中:
  1. /kill @a
复制代码
杀死所有玩家

  1. /kill @a[tag=dead]
复制代码
杀死所有 带有dead标签 的玩家。

  1. /scoreboard players add @r[tag=add] score 10
复制代码
为一名 带有add标签 随机玩家 在score计分板上添加10分。

  1. /tp @e[tag=teleport] 0 0 0
复制代码
将所有带有teleport标签的实体传送至世界中心(0,0,0)。

所有用选择器的地方都可以使用选择器变量。
你懂了么?我应该讲明白了。

标签会持续保持在实体身上,直至被删除——
删除标签格式:
  1. /tag <目标> remove <名称>
复制代码

本指令可以删除一个标签:
  1. /tag @p remove dead
复制代码
删除最近的带有dead标签的玩家。

  1. /tag @a[tag=delet] remove choose
复制代码
将所有带有delet标签的玩家身上的choose标签移除。

接下来,如果你忘了自己添加过什么标签,可以使用list子指令:
  1. tag <目标> list
复制代码
本指令会列出<目标>身上带有的所有标签。


那么,有了这种进阶的选择器,再回头看向原来的问题就很好解决了:
如何随机分队?
简单。
首先,为所有玩家加入choose标签:
  1. /tag @a add choose
复制代码
接下来,准备一个小空间,设置在一个离分队大厅比较远的地方,用于隔离正在被操作的玩家。
这个大厅......就放在坐标 (0, 100, 0) 上吧。
那么接下来, 随机 将一名 带有choose标签 的玩家从所有玩家身边传送至这个空间:
  1. /tp @r[tag=choose] 0 100 0
复制代码
接下来,在本房间内运行命令方块,内容为,将最近的玩家添加至红队:
  1. /team join @p red
复制代码
因为这名玩家已经被传送至这个空间,所以“@p”会选择是他——命令方块就在旁边嘛。
最后,把他的choose标签移除:
  1. /tag @p remove choose
复制代码
再传送回大厅:
  1. /tp @p <x> <y> <z>
复制代码
"<x> <y> <z>" 为大厅坐标
由于这名玩家的choose标签 已经被移除 ,就不再会被选择器“@r[tag=choose]”选中,也就不会出现重复的问题了。
重复运行上述指令5次,就可以获得五名随机的红队玩家了。


嗯......好麻烦啊......又得弄个独立空间,又要传送来传送去的......
当然,我们有简单的办法。
用一个标签来判定玩家有没有被选过实在是太麻烦了。有没有一种方法,或者说,一种选择器参数可以直接检测玩家有没有加入队伍,但不使用标签辅助?

看一下我们的选择器:
@r[tag=choose]
@r 选择器 ,代表 随机玩家
tag 选择器 条件 ,是 标签 的意思,
choose 要检测的 ,是我们自定的 标签
现在,我们要改变的是选择器条件,也就是“tag”:
既然有tag=(标签=),当然也可以有其他等于,比如,team=(队伍=)

通过队伍选择目标


除了通过标签选择玩家,还可以通过队伍选择玩家:
  1. [team=<队伍>]
复制代码

这个选择器会选取所有属于指定队伍的玩家:
  1. @r[team=red]
复制代码
随机选择一名红队玩家。

如果不填“<队伍>”,会选择所有没有队伍的玩家:
  1. @r[team=]
复制代码
随机选择一名不属于任何队伍的玩家。

也就是说,我们根本就用不上使用标签费尽力气地判断一名玩家是否拥有队伍,可以省去上面所有的关于独立小房间的步骤,只运行这一条指令5次就好了:
  1. /team join @r[team=] red
复制代码
将一个不属于任何队伍的随机玩家加入红队。

怎么样?

接下来,你可以将“!”放在值的前面来表示它不成立:
@a[tag=abc]为所有拥有abc标签的玩家。
@a[tag=!abc]为所有 没有 abc标签的玩家。
@r[team=red]的含义是随机的红队玩家。
@r[team=!red]即为随机的 红队玩家。
所以在将随机5名玩家加入红队后,很简单的就可以把剩下的玩家通通加入蓝队了:
  1. /team join @a[team=!red] blue
复制代码
将所有 不属于 红队的玩家加入蓝队。

这样简单多了哈。

但是......还是有一个麻烦的地方,就是我需要把 /team join @r[team=] red 运行五遍才能将五名玩家加入红队......那,我们不能直接用一条指令解决么?
这个问题实际上是, @r这个选择器只能随机选择1名玩家,我们有办法让它直接随机选择5名玩家么?

调整数量

当然可以。我们需要使用一个新的选择器参数(或者说选择其条件):
最大数量=: "limit="
这个选择器参数会限制(对于@a或@e),或者增加(@r或@p)。
  1. @a[limit=3]
复制代码
这个平常会选择所有玩家的@a如今只会选择3个玩家:
  1. @r[limit=5]
复制代码
这个平常只随机抽选一人的@r,如今会随机选择5人!
所以,这就简单多了,我们只需要直接随机选择5人加入红队就好了:
  1. /team join @r[limit=5] red
复制代码
将5个随机的玩家加入红队。


啊啊limit=对@s是无效的哦!因为虽然@s在/execute指令中有可能代表很多个目标,但“执行者本身”永远只有一人。
前面这句话没看懂没关系,以后看完execute再回来看就懂了啦。

多个条件(

你不只可以使用一种参数哦!使用逗号“,”可以连接多个参数:
  1. @<选择器>[<参数>=<值>,<参数>=<值>,…]
复制代码
比如:
  1. @a[team=blue,tag=!dead]
复制代码
选择所有蓝队的,没有dead标签的玩家。

  1. /kill @p[team=!,limit=3]
复制代码
杀死最近的3名加入了任意队伍的玩家。
(“team=”是指无队伍玩家,反转后的“team=!”也就是加入了任意队伍的玩家了。)

好,以上我们已经讲了三种参数了——队伍“team=”标签“tag=”数量“limit=”。但还有更多,我们要加快速度了。


通过分数选择目标

我们还可以使用以前讲过的记分板来选择目标:
  1. @a[scores={<记分板ID>=<值>,...}]
复制代码

本参数会选择所有在<记分板ID>上的分数符合<值>的玩家。
  1. @a[scores={test=5}]
复制代码
选择所有在记分板test上的分数为5的玩家。
这里有一个很方便的事情就是,你可以用".."来表示分数“几~几”:
  1. @e[scores={test=0..10}]
复制代码
选择所有在记分板test上的分数为10到12之间(包含)的实体。
你也可以在一侧留空来表示大于或小于:
  1. @e[scores={test=10..}]
复制代码
选择所有在记分板test上的分数大于10的实体。

这里还有一个比较方便的地方,就是:
  1. @e[scores={test=5..10},scores={abc=7}]
复制代码

这个分成了两个条件并以","相连的选择器可以被简化为这样:
  1. @e[scores={test=5..10,abc=7}]
复制代码

你可以在大括号中添加“,”来表示两个不同的记分板。

通过位置选择玩家


本参数可以选择位于特定坐标的玩家:
有两种方法可以这么做,但首先要指定一个 基准点
基准点是哪里?
额,就是,从哪里开始算。
接下来要填入“算到哪里”。
我们有两种方法,一种是指定距离,一种是对角体积。
指定距离:
  1. @a[x=<值>,y=<值>,z=<值>,distance=<值>]
复制代码

其中,“x=”,“y=”,“z=”就是基准点的坐标,而“distance=”即是以基准点为中心,离基准点多远的目标:
  1. @a[x=100,y=100,z=100,distance=10]
复制代码
这个选择器会选择以基准点(100, 100, 100)为中心,distance(10格)上目标。

除此之外还可以进行体积选取
确定一个根据基准点向三维延伸的第二个坐标“dx,dy,dz”,并选择两个坐标所形成的长方体区域:
  1. @a[x=100, y=100,z=100,dx=10,dy=-5,dz=5]
复制代码
这会选择两个点,基准点(100,100,100)与经过延伸后的目标点(100+10,100-5,100+5)之间的长方体内的目标。
是不是看糊涂了?
一般来说这种时候后面肯定有图:
w.jpg
这样,图中画的就是@a[x=100,y=100,z=100,dx=10,dy=-5,dz=5]的选择区域。


通过类型选择实体
@e 这个选择器是可以选择除了其他玩家的生物的,但就麻烦在它是“选择所有实体”这个巨大的范围。
而本参数可以选择不同类型的实体:
  1. [type=<实体类型>]
复制代码

“<实体类型>”需要填写任意一个实体的ID,比如creeper是苦力怕,那么:
  1. @e[type=creeper]
复制代码
本选择器会选择所有苦力怕。
如果你不知道实体ID是什么的话,可以查找 Wiki的这个页面

此外,你还可以使用标签(Tag)来选取所有处于该标签下的实体:
@e[type=#impact_projectiles]
选择所有弹射物
这包含了箭矢,雪球,火球,末影珍珠等所有弹射物。
在这里寻找标签列表

注意!这和上面的指令/tag不是一个东西,别搞混!



排序


到这里我们可能有几个问题:
1. @a是所有玩家,但是如果加入了前面说的“limit=”,就会限制选择的玩家数量,比如“@a[limit=3]”最多只能选择3个玩家了——那么,这三个玩家是如何选择出来的?随机的?有某种规律?我们能否调整?
2. 如何选择一名随机的实体?@r是选择随机玩家,并不包含实体。

以上这两个疑问都可以使用“sort=”来解决。
sort,即排序,可以在使用“limit=”的前提下决定“如何排序”。
我们有4种选择:
sort=nearest : 靠近近者优先。
  1. @e[limit=1,sort=nearest]
复制代码
选择最近的一名实体

  1. @a[limit=2,sort=nearest]
复制代码
选择最近的两名玩家。
额,本选择器与“@p[limit=2]”是等价的。

sort=furthest 由远到近。
  1. @e[limit=2,sort=furthest]
复制代码
选择最远的2名实体

sort=random 随机选择。
  1. @e[limit=3,sort=random,type=creeper]
复制代码
选择随机的3只苦力怕。


sort=arbitrary 不排序也就是@a与@e的默认情况。
这个参数没有啥用:
@r[limit=3,sort=arbitrary] 与 @a[limit=3] 是等价的。

注:现在已经无法直接在@r上使用type=来指定类型,必须使用sort=

我宣布,本帖从此进入垃圾时间
其他选择器

以下是一些不需要讲解,但为了完整性又必须放上来的选择器:
以下选择器均支持使用"!"。

指定目标的经验等级过滤目标
  1. [level=<值>]
复制代码
注意,这是“经验等级”, 等级

  1. @a[level=12]
复制代码
选择所有经验12级的玩家。

本选择器参数支持使用“..”作为范围:
  1. @a[level=5..]
复制代码
选择所有大于等于5级的玩家。

通过游戏模式选择目标


我们还可以通过游戏模式选择目标——这种方式经常用来在地图里对付开创造作弊的熊孩子们。


  1. [gamemode=<模式>]
复制代码
选择所有处于指定游戏模式的玩家。‌‌
<游戏模式>的值可以参考第三帖的内容。‌‌
这会自动过滤非玩家目标。

  1. @a[gamemode=creative]‌‌
复制代码
选择所有创造模式的玩家。


名称选择:
  1. [name=<ID>]
复制代码
选择所有符合ID的目标。
这是一个字符串,应当用双引号括起,但注意,这不是JSON。
  1. @a[name=Steve]
复制代码

选择所有叫史蒂夫的玩家。
嗯......虽然用史蒂夫皮肤的玩家挺多,但能叫steve的只有一个。


通过旋转选择目标

  1. [x_rotation=<值>]
复制代码
  1. [y_rotation=<值>]
复制代码

这可以选择朝向指定方向的目标,关于如何填入数值,请去第2帖查阅/tp指令或这去找Wiki。
这里注意,有一个和你印象不符的事情是,y_rotation指的是“沿y旋转”,也就是水平旋转。x_rotation反而是仰角。
此选择器支持使用范围:
  1. @e[y_rotation=0..90]
复制代码
选择所有看向正南到正西的目标。



以上就是今天的全部内容了。
除了我们提到的选择器限制条件以外,还有类似于进度,NBT与谓词等选择器——这些我们会在讲到是再详细说。

这一帖好乱啊,状态不佳。
原因是写到一半的草稿因为论坛特性被发出来了......我有在矿工茶馆说过。
还有就是,我的输入法坏了,只能用mac自带的,(真的很垃圾)
然后数据又丢了,只好重写。
结果重写完后发现原来一直在我的剪切板里有备份......
本一帖写的超级混乱。我不知道你们是不是看得云里雾里,思路被打断太多次了。
啊啊啊啊啊啊啊啊
请你们离发疯的我远一点。

附录

#更新日志

Java 1.16.5/a 页面发布
Java 1.16.5/b 修改了有关distance=的错误,重新整理格式。
Java 1.16.5/c  根据 Wiki命令:语法 ,更改了命令语法

Java 1.17/a 1.17版本升级完成
Java 1.18/a 1.18版本升级完成

上一篇: [命令] 记分板与/scoreboard 命令教程“真”从零开始 (五)

系列《命令教程“真”从零开始》
前往来自 麻瓜草稿簿 系列教程索引

下一篇: [命令] 新手指令杂烩2号 命令教程“真”从零开始 (七)