<⌒/ヽ-、__
/<_/____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
!什么声音 Σ( ° △ °|||)︴
∧_∧
( ・ω・)
_|⊃/(___
/ `-(____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
好像在那边。
∧_∧
(・ω・ )
_|⊃/(___
/ `-(____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
在这里!
∧_∧
( ・ω・ )
_|⊃/(___
/ `-(____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
大家好,我又回来了,所幸还能回来。情况似乎没有我想象的那么严重。
现在我想先聊一聊最近 Minecraft 基岩版的技术性修改。
自定义方块实体
在 26.30.28 中,他们加入了自定义方块实体的功能,可以在方块内部存储数据。于是有人说“自定义箱子来了!”“自定义熔炉来了!”……
真的是这样吗?让我们看看更新日志原文。
Blocks
Added the minecraft:block_entity block component
It has a single boolean field dynamic_properties
It requires format version 1.26.20 and Experimental Upcoming Creator Features
It cannot be used in permutations
When a block with a minecraft:block_entity is placed in the world, an associated block-entity will be placed with the intent to provide persistent local metadata, akin to chests, spawners, signs, etc...
- While leaner than their Vanilla counterparts, they still are heavier on the RAM than non-block-entities; use them sparingly
我看不懂小鸟的语言,让我们翻译一下。
方块
好像挺算那么回事,他们都说了“类似于箱子”。但是……真的就这么简单吗?让我们看看自定义方块实体能存储什么数据。
可以在更新日志的 SAPI 部分找到这些描述。
依旧看不懂小鸟的语言,让我们翻译。
注意这条,“每个方块实体最多只能存储 1 KB 的数据”。1 KB,这是什么概念?在 UTF-8 中,上面这一段更新日志原文和译文,加起来大约就是 1 KB!就用这么一点数据容量,要制作自定义箱子,我们可以做到存储 27 个物品堆栈吗?
我的答案是,非常难,但也不是不可以。
让我们看看物品堆栈的数据结构,以一个方块物品为例。
{
Name: '',
Count: 1,
Damage: 0,
WasPickedUp: false,
Block: {
name: '',
states: {},
version: 0
},
tag: {},
CanDestroy: [],
CanPlaceOn: []
}
这实在是太复杂了,我们可以稍微简化一下,只关注最重要的数据,那就是 Name 和 Count,表示这个方块物品是什么,以及它的数量。我们在 SAPI 中获取一个 ItemStack 对象,它的构造函数正好接受这两个参数。
怎样存储这些数据?直接存储 NBT 是不可能的,没有开放这样的接口。我们可以设计一种数据格式,来存储物品数据。
好!假设我们要表示一个存满石头的箱子,那么可以这样写:
[{id:'minecraft:stone',count:64},{id:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{id:'minecraft:stone',count:64},{id:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{id:'minecraft:stone',count:64},{id:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64},{name:'minecraft:stone',count:64}]
我们用了多少字节?907 字节,正好在 1024 字节的限制下方。但是这只是石头,如果我们要存储一箱子圆石,那就是 1069 字节,大于 1024 字节;更别提什么涂蜡的氧化切制铜楼梯了。再说,如果我们给物品命名,那么就需要存储这个名称;没记错的话,原版铁砧可以输入 50 个字符,在 UTF-8 下大约是 150 字节,轻轻松松超过限制。而且我要是存储装满东西的潜影盒……
为了解决这堆问题,我们必须优化数据结构。我们可以创建一个 ID 映射表,把原版 1000 多个 ID 映射到更短的字符串;优化数据的表示方式,用 id,count|id,count|id,count 这种特殊格式的字符串,等等。
可是无论怎样优化,我们一方面避不开潜影盒和 NBT 箱子的问题,另一方面躲不掉无法获取完整物品数据的问题,我相信在目前的接口下,这种问题是无解的。
就用 1 KB,真的没法自定义箱子了吗?
当然不是。正所谓“天无绝人之路”,
“船到桥头自然直”,以及一堆其他相似的名言,我想到了一种好办法,那就是利用 Structure 类。通俗一点,就是用结构方块。
我们当然不是真的要把结构方块放置出来使用,真正要用的是它背后的“结构”,对应着 SAPI 中的 Structure 类。结构这东西好用得很,它可以存储几乎所有类型的任意 NBT 数据,它的保存和加载有完善的 API 支持,而且用结构批量放置方块,速度非常快。在万象添补中,我已经在各种大家可能意想不到的地方用到了结构,比如自定义地形的生成用结构,竹柜的存储用结构,便携式据点用结构,在未加载区块填充方块用结构……结构来结构去,现在又可以在自定义箱子这方面用结构了。
假如真的要做自定义箱子,那么我会在玩家点击它的时候,让它变成原版箱子,关掉箱子之后再把它用结构保存起来,在自定义方块中存储结构 ID。这就非常方便。而且现在“关闭箱子”也会触发对应的事件,用这些新接口做一个自定义箱子简直是轻而易举。
自定义箱子,轻而易举啊!
坏了
坏了坏了
不知道大家还记不记得,我其实曾经“预言”过自定义箱子,就在这篇帖子里。这篇帖子也是够古老的,已经是去年的了,结果今年官方才加入相关功能。当时我认为会有一个方块组件,只要指定这个组件,那么这个方块就会变成箱子。现在看来,官方应该是觉得自定义箱子太难做,转而做了一个最最基础的自定义方块实体。
所以,我仍然盼望加入自定义箱子的相关组件。在此之前,我不会继续尝试开发自定义箱子。
自定义维度
自定义维度的加入确实很重大,但是到目前为止,他们还没有开放更多自定义维度相关的 API。
而且最开始的自定义维度,高度限制是 -512 到 511,潜力巨大;最近他们把它改成了与主世界相同,自定义维度惨遭大削。他们甚至可能都已经忘记行为包里可以包括 dimensions 文件夹,用来自定义虚空世界的事情。
我猜测,在 26.40 的前两个版本,官方会放出自定义维度的地形生成器接口和实体生成接口。
自定义唱片
这个功能不算大,也没有上面两个功能那么激动人心,但是确实值得一提。以前,物品定义中的声音全部需要声音枚举,而这个枚举是原版硬编码的。也就是说,以前不可能做到真正的自定义唱片,用 minecraft:record 组件只能做到可以播放原版声音的物品。但是就在 26.30.28,他们支持了自定义声音。现在就可以非常方便地做出自定义唱片了。
下面是一份从 26.30.20/21 到 26.30.28 的技术性更新的整理,用于参考。