我觉得我还能再抢救一下
本帖最后由 Cat_Anchor 于 2026-5-17 19:13 编辑>
> <⌒/ヽ-、\__
> /<\_/___\_/
>  ̄ ̄ ̄ ̄ ̄ ̄ ̄
>
> !什么声音 Σ( ° △ °|||)︴
>
> ∧_∧
> ( ・ω・)
> \_|⊃/(__\_
> / \`-(___\_/
>  ̄ ̄ ̄ ̄ ̄ ̄ ̄
>
> 好像在那边。
>
> ∧_∧
> (・ω・ )
> \_|⊃/(__\_
> / \`-(___\_/
>  ̄ ̄ ̄ ̄ ̄ ̄ ̄
>
> 在这里!
>
> ∧_∧
> ( ・ω・ )
> \_|⊃/(__\_
> / \`-(___\_/
>  ̄ ̄ ̄ ̄ ̄ ̄ ̄
---
大家好,我又回来了,所幸还能回来。情况似乎没有我想象的那么严重。
---
现在我想先聊一聊最近 Minecraft 基岩版的技术性修改。
# 自定义方块实体
在 `26.30.28` 中,他们加入了自定义方块实体的功能,可以在方块内部存储数据。于是有人说[“自定义箱子来了!”](https://klpbbs.com/thread-170975-1-1.html)“自定义熔炉来了!”……
真的是这样吗?让我们看看更新日志原文。
> ## 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
我看不懂小鸟的语言,让我们翻译一下。
> ## 方块
>
> + 加入了 `minecraft:block_entity` 方块组件
>
> + 仅包含一个布尔字段 `dynamic_properties`
>
> + 需要格式版本 `1.26.20` 和 `即将推出的创作者功能` 实验性玩法
>
> + 不能在方块排列中使用
>
> + 将带有 `minecraft:block_entity` 的方块放置在世界中时,会随之放置一个关联的方块实体,目的是提供持久的本地元数据,类似于箱子、刷怪笼、告示牌等……
>
> + 虽然比原版对应物更轻量,但它们对内存的占用仍然高于非方块实体方块;请谨慎使用
好像挺算那么回事,他们都说了“类似于箱子”。但是……真的就这么简单吗?让我们看看自定义方块实体能存储什么数据。
可以在更新日志的 SAPI 部分找到这些描述。
> + Added the `minecraft:dynamic_properties` block component
>
> + It is only present on blocks defined with a `minecraft:block_entity` component with `dynamic_properties` set to true
>
> + It stores properties of the same `DynamicProperties` type that can be found on entities or item stacks
>
> + It comes with a `get`, `set` and `totalByteCount` API
>
> + It may only store up to 1KBytes of data per block entity
>
> + It (currently) does not carry over its data to the item resulting from mining or picking the block entity
依旧看不懂小鸟的语言,让我们翻译。
> + 加入了 `minecraft:dynamic_properties` 方块组件
>
> + 仅存在于定义了 `minecraft:block_entity` 组件且其中 `dynamic_properties` 为 true 的方块
>
> + 存储与实体或物品堆栈上相同的 `DynamicProperties` 类型的属性
>
> + 提供 `get` `set` 和 `totalByteSize` 三个 API
>
> + 每个方块实体最多只能存储 1 KB 的数据
>
> + 挖掘或拾取方块实体时,其数据目前不会转移到掉落物中
注意这条,“每个方块实体**最多只能存储 1 KB 的数据**”。1 KB,这是什么概念?在 UTF-8 中,上面这一段更新日志原文和译文,加起来大约就是 1 KB!就用这么一点数据容量,要制作自定义箱子,我们可以做到存储 27 个物品堆栈吗?
我的答案是,非常难,但也不是不可以。
让我们看看物品堆栈的数据结构,以一个方块物品为例。
```js
{
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。这就非常方便。而且现在“关闭箱子”也会触发对应的事件,用这些新接口做一个自定义箱子简直是轻而易举。
> 自定义箱子,轻而易举啊!
>
> 坏了
>
> 坏了坏了
不知道大家还记不记得,我其实曾经“预言”过自定义箱子,就在[这篇帖子](https://klpbbs.com/thread-168062-1-1.html)里。这篇帖子也是够古老的,已经是去年的了,结果今年官方才加入相关功能。当时我认为会有一个方块组件,只要指定这个组件,那么这个方块就会变成箱子。现在看来,官方应该是觉得自定义箱子太难做,转而做了一个最最基础的自定义方块实体。
所以,我仍然盼望加入自定义箱子的相关组件。在此之前,我不会继续尝试开发自定义箱子。
---
# 自定义维度
自定义维度的加入确实很重大,但是到目前为止,他们还没有开放更多自定义维度相关的 API。
而且最开始的自定义维度,高度限制是 -512 到 511,潜力巨大;最近他们把它改成了与主世界相同,自定义维度惨遭大削。他们甚至可能都已经忘记行为包里可以包括 dimensions 文件夹,用来自定义虚空世界的事情。
我猜测,在 `26.40` 的前两个版本,官方会放出自定义维度的地形生成器接口和实体生成接口。
---
# 自定义唱片
这个功能不算大,也没有上面两个功能那么激动人心,但是确实值得一提。以前,物品定义中的声音全部需要声音枚举,而这个枚举是原版硬编码的。也就是说,以前不可能做到真正的自定义唱片,用 `minecraft:record` 组件只能做到可以播放原版声音的物品。但是就在 `26.30.28`,他们支持了自定义声音。现在就可以非常方便地做出自定义唱片了。
---
下面是一份从 `26.30.20/21` 到 `26.30.28` 的技术性更新的整理,用于参考。
## 方块
* 将 `snow_log_no_collision` 重命名为 `snowlogging`
* 在 `minecraft:geometry` 中加入了 `n_way_visual_rotation` 字段,接受方块状态名称,使方块纯视觉旋转
* 为方块特征 `minecraft:placement_direction` 加入了 `minecraft:sixteen_way_rotation`,根据放置时玩家朝向自动应用 0-15 的状态值
* 为多方块加入了含雪
* 在 `minecraft:flammable` 中加入了 `lava_flammable` 字段
* 加入了 `minecraft:block_entity` 方块组件,含布尔字段 `dynamic_properties`
## 物品
* 在物品组件 `minecraft:use_modifiers` 中加入了新字段 `start_using`
* 启用“Beta API”时,物品组件 `minecraft:swing_sounds` `minecraft:durability_sensor` `minecraft:record` 和 `minecraft:use_modifiers` 现在除枚举值外还接受基于字符串的音效事件名称
## 实体
* 在 `minecraft:client_entity` 的 `script` 字段加入了 `hide_held_items` 字段,是 Molang 表达式,值为非零时隐藏手持物品
* 在 `minecraft:pushable_by_entity` 组件中加入了 `presets` 字段
* 为 `minecraft:area_attack` 组件加入了 `use_self_as_damage_source` 字段
* 为 `minecraft:leashable` 和 `minecraft:leashable_to` 组件加入了 `unleash_on_removal` 字段
* 加入了 `redstone_strength_at_position` 实体过滤器
* 加入了 `unleash` 实体事件响应
## 世界生成
* 加入了新地物类型 `height_difference_filter_feature`
* 在 `minecraft:structure_template_feature` 中:加入了 `ground_level` 字段,在 `constraints` 字段中加入了 `leveled` 字段,在 `block_intersection` 约束中加入了 `only_check_intersection_for_motion_blocking_blocks` 字段
* 加入了 `NoiseDescriptor` 模式定义,包含字段
* 加入了 `NoiseBlockSpecifier` 模式定义
* 更新了 `minecraft:noise_gradient` 表面和次表面构建器 API
## API
* 为 `Player` 加入了 `getPing()` 方法和 `playfabId` 属性
* 加入了 `playerWaypoints`,取代 `locatorBar` 属性;加入了枚举 `PlayerWaypointMode`
* 加入了 `EntityUpgradeAfterEvent` `SoundInstance` `LootItem.conditions` `PlayerStartBreakingBlockAfterEvent` `EntityStartSneakingAfterEvent`
* 加入了 `BiomeWater` `BiomeColorGrading` `BiomeLighting` `PlayerWater` `PlayerColorGrading` `PlayerLighting` `PlayerAtmospherics`
* 加入了 `DebugCone` `DebugCylinder` `DebugPyramid` `DebugEllipsoid`
* 移除了 `PartyInfo`
## 数据驱动 UI
* 重大更新:使用 `ObservableString`、`ObservableNumber`、`ObservableUIRawMessage`、`ObservableBoolean` 取代 `Observable`
* 现在应使用构造函数代替 `Observable.create`
* 将 `DropdownItem` 重命名为 `DropdownItemData`
* 更改了 `DataDrivenScreenClosedReason` 的值
## 通用
* 使用 `minecraft:spawn_entity` 组件生成的弹射物现在遵守 `minecraft:projectile` 的 `anchor` 和 `offset` 字段
* 为弹射物组件加入了 `owner_launch_immunity_ticks` 字段
* 在战利品表中加入了新条件 `biome_has_tag`,检查掉落战利品的实体所在位置的生物群系标签
* 加入了 `q.fuse_time` 查询,返回实体剩余引信时间,若无 `minecraft:explode` 组件则返回 `-1`
* 加入了 `playerWaypoints` 游戏规则,取代 `locatorbar`
* 本地化字符串中带无效参数编号的百分号将保持原样
* 内容日志消息现在仅显示相对路径
* 自定义维度高度范围现在与原版主世界默认值一致
---
我发了一个视频,其实这是我的 `dream5_exp` 写作计划中的。
bili:BV1DdL36tEBW
是的,这是一个所谓“剧情视频”,会出现在[这个](https://klpbbs.com/thread-169946-1-1.html)[写作](https://klpbbs.com/thread-170031-1-1.html)[项目](https://klpbbs.com/thread-171022-1-1.html)未来的某个章节中。
前面已经说过,只有写好前五章,我才会考虑发布这个系列的文章;现在我当然达到了这个目标,而且已经达到目标相当久了。但是我的写作热情是有限的,就像我不会一直高强度搞附加包开发一样,最近我几乎没碰写作计划。
我写这个系列,主要是为了测试一些能力,顺便把梦境表达出来。系列的前面五章是经过一番构想才写出来的。但是接下来我就不知道写什么了,只好走一步看一步。
好了,今天先到这里。我还有一些没有说的话,留到下次。
页: [1]