MCFPP——实体设计
在Minecraft命令中,实体操作是非常核心的部分。可以说,百分之九十的数据包,都和实体密切相关。在MCFPP中,当然也需要对实体操作部分进行精巧的设计。
Contents
实体的选择
在数据包中,大致有两种方法对实体进行选择操作。
第一种方法就是最常见的目标选择器。直接使用@e,@s之类的目标选择器,对实体进行选择,之后再对其进行各种操作。
第二种方法是物品Thrower法,适用于从大量实体中选择某一个实体,在总实体特别多的情况下,相较于@e的优势尤为明显。具体来说,这个方法就是将一个实体的UUID-NBT储存到一个物品实体的Thrower nbt中,之后通过execute on origin,就可以选中这个实体了。一般来说,用于选择用的物品实体都会拥有一个特定的UUID值,因此可以直接使用execute as uuid on origin,从而避免@e昂贵的性能开销。
在MCFPP中,提供了两种方法,用于选择实体,分别对应了上述的两种方案。
<Java>class Selector <MCFPP>type selector
Selector
类,在mcfpp中,对应selector
类型,表示一个目标选择器。一般来说,可以直接使用和Minecraft中一样的形式构建一个目标选择器:
//一个基本的目标选择器 selector a = @a; //当然也可以使用var关键字 var b = @a; //带有参数的目标选择器 var c = @e[type=creeper,distance=7..10];
在创建好一个目标选择器后,可以对目标选择器进行进一步的修饰,实质上是为目标选择器添加参数
//基本的目标选择器 var a = @e; //添加参数修饰 a = a.type("creeper"); a = a.distance(1,2); //相当于@e[type=creeper,distance=1..2] //允许使用参数 a = a.limit(count); //你可以使用链式调用 var p = @e.type("fox").limit(1).sorted(Sort.Nearest);
值得注意的是,目标选择器实际上有两种类型。一种是单数目标选择器,即只会选择一个实体的目标选择器(@p,@s,@e[limit=1]),一种是复数目标选择器,可能选择多个实体。在Minecraft中,某些命令仅可接受一个实体作为命令的参数, 因此我们也需要在MCFPP中对这两种目标选择器进行区分。同样都会使用selector
作为类型名,但是使用泛型参数进行区分。
对于selector<int limit>
,表示最多只会选择limit个实体,比如selector<1>
就可以表示一个单数目标选择器。而对于没有泛型参数的selector
,就表示会选择的实体个数不确定。
除了数量限制以外,还有实体种类限制。有selector<string type>
,用于限制目标选择器会选择哪种实体。比如selector<"pig">
只会选中猪。
<Java>class Entity <MCFPP>type entity
类Entity
对应mcfpp中的类型entity
。Entity
类继承于NBTBasedData
,意味着这是一个基于NBT的数据。事实上,entity
类型的数据中,就是存储的一个实体的uuid-nbt数组。它是基于第二种方案,也就是Thrower来选中实体的。
你可以使用目标选择器来获取entity
类型,也可以通过NBT数据获取。
//通过目标选择器 var s = @s; //单数目标选择器选择单个实体 entity e = s.select(); var a = @a; //复数目标选择器选择多个实体 list<entity> es = a.select(); //通过nbt nbt uuid = @s.nbt["UUID"]; entity e = (entity)uuid; //强制类型转换
entity
不适用于访问多个实体,一般适用于访问单个实体。
实体的操作
无论是selector
还是entity
,它们的类都继承了EntityBase
接口类型。EntityBase
接口要求其继承者实现所有对实体的操作。因此,selector
和entity
都能用同一套API实现对实体的操作。
对实体的操作主要分为两种。第一种是以实体为执行者执行命令。比如
//目标选择器 var a = @e; a.say("hi"); //相当于execute as @e run say hi //entity var b = (entity)[I;0,0,0,1]; b.say("hi");
第二种操作是获取实体上的数据,主要是记分板和nbt。以目标选择器举例
//目标选择器 var a = @p; int i = a.score["test"]; var b = @e; list<int> scores = b.score["test"];
可以看到,单数目标选择器和复数目标选择器获取数据的时候,获取到的数据也是不一样的。单数目标选择器获取到的说单个的数据,而复数目标选择器获取到的是这个数据类型的列表,里面存放的是选中的所有实体的数据,存放顺序和实体选择的顺序一致。