This commit is contained in:
boybook
2025-12-01 20:59:16 +08:00
parent 12738a142c
commit 760c2dd9ad
5535 changed files with 21070 additions and 2021 deletions

View File

@@ -0,0 +1,23 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 入门
time: 5分钟
---
# 摘要
在本章中,我们将一起学习**特征****Feature**,又译**地物**)和**特征规则****Feature Rule**,又译**地物规则**),了解什么是特征,以及如何生成一个特征。
- 在第一节(*了解自定义特征*)中,我们将一起了解什么是特征。我们将一起认识各种种类的特征,以及如何自定义这些特征。
- 在第二节(*了解自定义特征规则*)中,我们将一起学习特征规则,了解到自定义特征生成的方法。
- 在第三节(*了解自定义矿石特征规则*)中,我们将一起自定义一个矿石特征及其特征规则。
- 在第四节(*了解自定义单方块特征规则*)中,我们将一起自定义一个单方块特征及其特征规则。
- 在第五节(*了解自定义散植特征规则*)中,我们将一起自定义一个散植特征及其特征规则。
- 在第六节(*了解自定义聚合特征规则*)中,我们将一起自定义一个聚合特征及其特征规则。
- 在第七节(*了解搜索特征规则*)中,我们将一起自定义一个搜索特征。
- 在第八节(*了解序列特征规则*)中,我们将一起自定义一个序列特征及其特征规则。
- 在第九节(*了解加权随机特征规则*)中,我们将一起自定义一个加权随机特征及其特征规则。
- 在第十节(*了解树特征规则*)中,我们将一起自定义一个树特征及其特征规则。
- 在第十一节(*挑战:制作沿河畔生长的植物特征*)中,我们将一起进行一个挑战,自定义一种可以沿着河畔生成的植物。
本章关键词:特征 地物 矿石 单方块 散植 聚合 搜索 序列 加权随机 树

View File

@@ -0,0 +1,408 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 25分钟
---
# 了解自定义特征
**特征****Feature**,又译**地物**)是一种由一个或多个方块组成的在世界中发挥点缀地形效果的结构,我们在原版世界中看到的树木、花草、矿石乃至遗迹、村庄、湖泊、晶洞等地形都是通过特征来生成的。特征充当了玩家所看到的世界的各种微观细节的角色,有了特征的生成,世界中由噪声和生物群系产生的局部一成不变的地形结构才有了更多地细节变化。在本节中,我们一起来了解如何自定义一个特征。
## 了解特征文件的结构
在第六章中,我们曾经一起学习了如何通过编辑器自定义一个特征,不过同时也了解到了当前编辑器暂时只支持配置一个**结构特征****Structure Feature**,又译**结构地物**。这个结构特征其实是一种中国版独占的特征类型。除了这种中国版独占的特征类型之外我们还有很多国际版提供的特征类型可以用于自定义。为了自定义更多类型的特征我们需要手动编辑JSON文件来更改特征的类型和参数。在此之前我们先一窥之前所创建的中国版结构特征的JSON内容
```json
{
"format_version": "1.14.0",
"netease:structure_feature": {
"description": {
"identifier": "tutorial_demo:demo_feature"
},
"places_structure": "tutorial_demo:pondwaterland"
}
}
```
我们可以看到,我们的结构模板特征存放在行为包的`netease_features`文件夹中的。在这个文件夹中的结构的生成不需要开启实验性玩法,并且同时支持所有国际版的特征类型,所以我们之后的特征可以全部都放置在这个文件夹中。
`netease:structure_feature`是该特征的**模式标识符****Schema Identifier**。顾名思义模式标识符就是用于确定该JSON文件的**模式****Schema**的标识符。之前我们了解过JSON文件的模式决定了JSON文件使用的文件结构比如`netease:structure_feature`的模式便决定了其下有`description``places_structure`两个字段,缺一不可。再比如,`minecraft:client_entity`的模式决定了其下只能有一个`description`字段,不过`description`中倒是可以有很多各种功能的字段;而`minecraft:entity`模式决定了下面可以有一个`description`和一个`components`,而且`components`中可以存在许多行为包组件。而**格式版本****Format Version**)则是在确定了模式标识符之后对各个字段的结构和属性根据游戏版本的变更进行的修改,这包括字段名称的修改、字段的必需和可选性的修改以及字段的增删等。比如,`minecraft:client_entity``1.8.0`格式版本的`description`中存在`animation_controllers`数组,用于定义动画控制器,而`1.10.0`的格式版本则删除了这一字段,将动画控制器统一定义到了与动画相同的位置`animations`对象中。
事实上如果你查看过原版的特征JSON文件你应该可以注意到特征的模式标识符并不像方块的`minecraft:block`、物品的`minecraft:item`、实体的客户端和服务端的`minecraft:client_entity``minecraft:entity`那样是统一的。特征的模式标识符对于不同种类的特征将有所不同。比如中国版的结构特征的模式标识符便使用了`netease:structure_feature`,而国际版提供的各种特征类型却使用了其他的模式标识符。如`minecraft:ore_feature``minecraft:single_block_feature`等。也就是说,不像物品、方块、实体在同一个模式标识符下定义描述和组件那样,特征是依据不同的特征类型使用不同的模式标识符,同时使用不同的模式结构的。所以对于每种类型的特征,我们都需要单独学习它的文件结构。
因为模式标识符并不一致,因此格式版本也应随之变化。中国版的结构特征`netease:structure_feature`的格式版本为`1.14.0`。除此之外,国际版的各种类型的特征的格式版本不尽相同,但是大部分都为`1.13.0`,另有少部分模式标识符使用`1.16.0``1.16.100`的格式版本。
接下来,我们通过分模式标识符来学习一些基本类型的特征。
## 自定义矿石特征
矿石特征的模式标识符为`minecraft:ore_feature`,惯用格式版本为`1.13.0`,也可以使用`1.16.0`。矿石特征旨在模拟矿石生成的矿床结构,事实上,任何方块都可以作为矿石特征的输入方块,并不一定必须是一个矿石。我们来看一个例子:
```json
{
"format_version": "1.13.0",
"minecraft:ore_feature": {
"description": {
"identifier": "example:malachite_ore_feature"
},
"count": 12,
"replace_rules": [
{
"places_block": "example:malachite_ore",
"may_replace": [
"minecraft:stone"
]
},
{
"places_block": "example:granite_malachite_ore",
"may_replace": [
"minecraft:granite"
]
},
{
"places_block": "example:andesite_malachite_ore",
"may_replace": [
"minecraft:andesite"
]
}
]
}
}
```
`count`为该矿石特征尝试替换的次数,而`replace_rules`则是替换规则。矿石特征的原理是将指定的一个或一些方块替换成特定的方块,往往是矿石方块。比如示例中,该特征尝试把`minecraft:stone`替换成`example:malachite_ore``minecraft:granite`替换成`example:granite_malachite_ore``minecraft:andesite`替换成`example:andesite_malachite_ore`。如果不填写`replace_rules`字段,则代表着方块可以替换任何的其他方块。
## 自定义单方块特征
单方块特征的模式标识符为`minecraft:single_block_feature`,惯用格式版本为`1.13.0`,也可以使用`1.16.0`。单方块特征旨在放置一个单独的方块,比如花草等小方块。在放置期间,我们可以可以像矿石特征那样设置可以替换的方块,也可以设置可以附着的方块,即我们可以使这个方块只能被附着地放置在某种或某些方块的某些面上。
```json
{
"format_version": "1.13.0",
"minecraft:single_block_feature": {
"description": {
"identifier": "minecraft:cocoa_age_0_feature"
},
"places_block": {
"name": "minecraft:cocoa",
"states": {
"age": 0
}
},
"enforce_survivability_rules": false,
"enforce_placement_rules": false,
"may_attach_to": {
"min_sides_must_attach": 1,
"sides": {
"name": "minecraft:log",
"states": {
"old_log_type": "jungle"
}
}
}
}
}
```
`places_block`是将要被放置的那个方块,这里使用了一个**方块引用****Block Reference**)的格式。不仅是这里,所有可以单独填写一个方块标识符的位置也都可以像这样填写一个方块引用对象`{"name": "<namespace:identifier>", "states": {"<state1>": <someValue>, "<state2>": <someValue> ... }}`。在不指定方块状态`states`字段时,单独写一个方块标识符和写一个方块引用对象是等价的。而方块引用的好处就是可以用于指定特定的方块状态,在这里就是用于在特征中直接生成一个特定状态的方块。`may_replace`字段用于指定可以被替换的方块,是一个和上面矿石特征中`may_replace`一样的数组。在我们这个示例中没有使用这个`may_replace`字段,这个字段和`places_block`是平级的。`may_attach_to`也和`places_block`平级,用于指定可以附着到哪些方块的哪些面上。`enforce_survivability_rules``enforce_placement_rules`分别用于告知引擎是否开启方块的可生存性和可放置性检验。方块可以通过在行为包中设置`minecraft:placement_filter`方块组件来定义可放置性和可生存性。不过值得注意的是,这个方块组件是`1.16.100`之后的方块组件,如需使用请注意格式版本。
## 自定义散植特征
散植特征的模式标识符为`minecraft:scatter_feature`,惯用格式版本为`1.13.0`。该特征旨在将一个特征进行一次或多次**散植****Scatter**),即相对于特征的起始点在其周围散布一次或多次某个指定的特征。
```json
{
"format_version": "1.13.0",
"minecraft:scatter_feature": {
"description": {
"identifier": "minecraft:grass_double_plant_patch_feature"
},
"iterations": 64,
"scatter_chance": {
"numerator": 1,
"denominator": 80
},
"coordinate_eval_order": "zyx",
"x": {
"extent": [ -8, 8 ],
"distribution": "gaussian"
},
"z": 10,
"y": "math.cos(math.random(0, 10))",
"project_input_to_floor": false,
"places_feature": "minecraft:grass_double_plant_feature"
}
}
```
这是一个改造过的双层高草的斑块特征的示例,`iterations`代表迭代的次数,即散植的次数。`scatter_chance`代表散植的可能性该字段可以为一个数字代表百分比比如50就代表“50%”的概率;也可以使用一个对象,该对象代表一个分数,其中`numerator`为分子,`denominator`为分母。我们称这种代表一个分数的对象为一个**可能性信息****Chance Information**)。`scatter_chance`如果判定通过,则会进行`iterations`次数的散植迭代,如果不通过,则一次迭代都不进行。`coordinate_eval_order`为坐标串演算顺序代表下述XYZ三个轴向上的值的演算顺序。一般来说三个轴向上的演算顺序并不影响最终的结果但是如果三个轴向上使用Molang表达式进行运算时使用了变量来保存信息 则演算顺序将造成结果差异。`x``y``z`三个字段可以使用一个值、一个Molang表达式或者一个对象来指定一个分布函数。上述示例中X轴向上便使用了高斯分布来计算散植的目的坐标。
## 自定义聚合特征
聚合特征的模式标识符为`minecraft:aggregate_feature`,惯用格式版本为`1.13.0`。聚合特征旨在将一个或多个特征集中生成在一个位置,即将它们**聚合****Aggregate**)在一起。
```json
{
"format_version": "1.13.0",
"minecraft:aggregate_feature": {
"description": {
"identifier": "minecraft:bamboo_then_podzol_feature"
},
"early_out": "first_failure",
"features": [
"minecraft:bamboo_feature",
"minecraft:optional_podzol_feature"
]
}
}
```
`features`数组即我们想要聚合的特征列表。`early_out`字段可以用于指定该次聚合是否会因为什么原因而提前退出。比如这里`first_failure`便代表在聚合过程中一旦有一个特征放置失败,便终止整次聚合。
## 自定义序列特征
序列特征的模式标识符为`minecraft:sequence_feature`,惯用格式版本为`1.13.0`。序列特征和聚合特征非常类似,都是用于放置多个其他的特征的特征,但是,序列特征的放置起始点并不是固定在某个位置的,而是在放置列表中的下一个特征的起始点会使用上一个特征的结束点。
```json
{
"format_version": "1.13.0",
"minecraft:sequence_feature": {
"description": {
"identifier": "minecraft:jungle_tree_with_cocoa_feature"
},
"features": [
"minecraft:jungle_tree_feature",
"minecraft:optional_jungle_tree_cocoa_feature"
]
}
}
```
`features`数组便是我们想要放置的特征列表。
## 自定义搜索特征
搜索特征的模式标识符是`minecraft:search_feature`,惯用的格式版本为`1.13.0`。搜索特征旨在在一个轴对其边界框中按特定的顺序进行搜索,一旦搜索到适合放置的位置即可放置一个特征。默认而言,搜索特征在成功放置一次后边会结束,但是我们可以更改这个结束之前的成功次数。
```json
{
"format_version": "1.13.0",
"minecraft:search_feature": {
"description": {
"identifier": "minecraft:beehive_search_feature"
},
"places_feature": "minecraft:select_beehive_feature",
"search_volume": {
"min": [ 0, 0, 0 ],
"max": [ 0, 6, 0 ]
},
"search_axis": "+y",
"required_successes": 1
}
}
```
`required_successes`是在停止搜索前所需要的成功次数。特征会在搜索满足成果次数或者搜索尽`search_volume`中指定的轴对其边界框体积后停止搜索和放置。`search_axis`代表搜索顺序,其中`+y`便代表Y轴正方向我们自然可以推知`-y``±x``±z`的意义。对于`+y`来说搜索的顺序便是从Y值低的层搜索至Y值高的层然后在每个Y层中从面相搜索方向的视角来看的左下角搜索至右上角。一旦搜索过程中有满足放置要求的位置即可放置特征并使搜索次数加1。
## 自定义加权随机特征
加权随机特征的模式标识符为`minecraft:weighted_random_feature`,惯用的格式版本为`1.13.0`。加权随机特征旨在在众多特征中通过权重随机选出一个特征进行放置。
```json
{
"format_version": "1.13.0",
"minecraft:weighted_random_feature": {
"description": {
"identifier": "minecraft:random_meadow_flower_feature"
},
"features": [
["minecraft:yellow_flower_feature", 5],
["minecraft:cornflower_feature", 5],
["minecraft:tall_grass_feature", 1]
]
}
}
```
一个加权随机特征只能放置一次,在上述示例中,在一次放置中,`minecraft:yellow_flower_feature`具有$\frac{5}{11}$的概率被选中,`minecraft:cornflower_feature`也具有$\frac{5}{11}$的概率被选中,`minecraft:tall_grass_feature`具有$\frac{1}{11}$的概率被选中。
## 自定义树特征
树特征是最复杂的特征之一,它的模式标识符为`minecraft:tree_feature`,惯用格式版本为`1.13.0`。通过树特征,我们可以制作出一些树状的方块结构。我们来看一个原版的桦树特征:
```json
{
"format_version": "1.13.0",
"minecraft:tree_feature": {
"description": {
"identifier": "minecraft:birch_tree_feature"
},
"trunk": {
"trunk_height": {
"range_min": 5,
"range_max": 8
},
"trunk_block": {
"name": "minecraft:log",
"states": {
"old_log_type": "birch"
}
}
},
"canopy": {
"canopy_offset": {
"min": -3,
"max": 0
},
"variation_chance": [
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 1
}
],
"leaf_block": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "birch"
}
}
},
"base_block": [
"minecraft:dirt",
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
}
],
"may_grow_on": [
"minecraft:dirt",
"minecraft:grass",
"minecraft:podzol",
"minecraft:dirt_with_roots",
"minecraft:moss_block",
// Block aliases sure would be sweet
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 0
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 1
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 2
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 3
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 4
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 5
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 6
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 7
}
}
],
"may_replace": [
"minecraft:air",
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "spruce"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "birch"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "jungle"
}
},
{
"name": "minecraft:leaves2",
"states": {
"new_leaf_type": "acacia"
}
},
{
"name": "minecraft:leaves2",
"states": {
"new_leaf_type": "dark_oak"
}
}
],
"may_grow_through": [
"minecraft:dirt",
"minecraft:grass",
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
}
]
}
}
```
对于树特征来说,首先需要指定一个**树干****Trunk**)的形状,这里使用了`trunk`字段。事实上,我们有多种树干型状可以选择,比如`trunk``fallen_trunk``acacia_trunk``mega_trunk``fancy_trunk`等。我们只能在众多树干形状字段中选择其一。不同的树干型状对象中的子字段也不尽相同,而一般都会有`trunk_height``trunk_block`,分别代表树干的高度和树干使用的方块。我们可以看到,`trunk_block`使用一个方块引用对象,通过前面的学习我们可以知道,任何可以用方块引用对象的地方都可以直接用一个方块标识符字符串。因此这里`trunk_block`后面其实也可以直接跟一个方块标识符。接下来是和`trunk`平级的`canopy`字段,代表**树冠****Canopy**)的属性。`canopy_offset`代表树冠偏移量,而`variation_chance`代表树冠变异的可能性。而树冠使用的树叶方块则由`leaf_block`指定。与树干一样,树冠也可以有其他的类型,比如往往和金合欢树干一起使用的随机传播型树冠`random_spread_canopy`。之后,`base_block`用于指定该树特征的**树基****Base**)方块,即树生成后其脚下应有的方块。如果生成之前树脚下不是该方块,则树将会使脚下替换为该方块。`may_grow_on`用于指定树可以生成在哪些方块上,我们可以看到,虽然桦树的树基方块中没有草方块,但是桦树是可以生成在草方块上的,这可以在我们能够在草方块上种植桦树树苗这一点上体现。`may_replace`则是树特征生长过程中可以用于替换的方块,这些方块会被随机替换为树的一部分,成为组成树的方块。最后`may_grow_through`是树可以穿破的方块,换句话说,树可以将这些方块替换为树自己的方块。
树特征非常复杂但是也非常高效。开发者们可以通过阅读原版的各种特征的JSON文件来充分地学习树特征的写法。当然其他类型的特征也无一不可通过阅读代码的形式学习。这种学习方式既高效又可以保证正确率无疑是一种非常有效的学习方式。

View File

@@ -0,0 +1,243 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 15分钟
---
# 了解树特征规则
最后,我们来到了树特征。我们仿照原版的橡树,制作一个红色的橡树特征并将其放置在世界中。
## 使用编辑器配置自定义树桩方块
![](./images/16.10_red_oak.png)
我们准备好红色橡木原木的纹理资源,在编辑器中创建,并将其设置为固体不透明。
## 使用编辑器配置自定义树叶方块
![](./images/16.10_red_oak_leaves.png)
同样,我们准备好红色橡木树叶的纹理资源,在编辑器中创建,并将其设置为非实体透明。
## 使用树木特征设计自定义树木
我们手动创建`red_oak_tree_feature.json`文件,并仿照原版的`minecraft:oak_tree_feature`书写如下:
```json
{
"format_version": "1.13.0",
"minecraft:tree_feature": {
"description": {
"identifier": "tutorial_demo:red_oak_tree_feature"
},
"trunk": {
"trunk_height": {
"range_min": 4,
"range_max": 7
},
"trunk_block": "tutorial_demo:red_oak_log"
},
"canopy": {
"canopy_offset": {
"min": -3,
"max": 0
},
"variation_chance": [
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 2
},
{
"numerator": 1,
"denominator": 1
}
],
"leaf_block": "tutorial_demo:red_oak_leaves"
},
"base_block": [
"minecraft:dirt",
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
}
],
"may_grow_on": [
"minecraft:dirt",
"minecraft:grass",
"minecraft:podzol",
"minecraft:dirt_with_roots",
"minecraft:moss_block",
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 0
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 1
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 2
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 3
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 4
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 5
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 6
}
},
{
"name": "minecraft:farmland",
"states": {
"moisturized_amount": 7
}
}
],
"may_replace": [
"minecraft:air",
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "spruce"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "birch"
}
},
{
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "jungle"
}
},
{
"name": "minecraft:leaves2",
"states": {
"new_leaf_type": "acacia"
}
},
{
"name": "minecraft:leaves2",
"states": {
"new_leaf_type": "dark_oak"
}
},
"tutorial_demo:red_oak_leaves"
],
"may_grow_through": [
"minecraft:dirt",
"minecraft:grass",
{
"name": "minecraft:dirt",
"states": {
"dirt_type": "coarse"
}
}
]
}
}
```
这意味着我们的树会以`tutorial_demo:red_oak_log`为树干,以`tutorial_demo:red_oak_leaves`为树叶,同时要求脚下必须为土或砂土,树叶生成时能够替换其他类型的树叶的位置,且树干可以在土、草或砂土上生成。
树特征会在成功生成树时判定成功,其余情况判定失败。
## 连接特征规则
我们新建一个`overworld_red_oak_tree_feature.json`文件,内容如下:
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_red_oak_tree_feature",
"places_feature": "tutorial_demo:red_oak_tree_feature"
},
"conditions": {
"placement_pass": "surface_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 1,
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": "query.heightmap(variable.worldx, variable.worldz)",
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
![](./images/16.10_red_oak_in-game.png)
进入游戏,可以看到正如我们期望的那样,每个区块平均生成了一个树特征!

View File

@@ -0,0 +1,156 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 20分钟
---
# 挑战:制作沿河畔生长的植物特征
在实践过程中,我们常常将特征分为多种类型。一般而言,我们可以将特征分为**内容特征****Content Feature**,又译**内容地物**)、**代理特征****Proxy Feature**,又译**代理地物**)、**场景特征****Scene Feature**,又译**场景地物**)和**雕刻器特征****Carver Feature**,又译**雕刻器地物**)。内容特征顾名思义是用于填充内容(方块)的特征,而代理特征也顾名思义是“代其他特征而理(放置)之”的特征,也就是说,代理特征往往是用于放置一个子特征的特征,只不过在放置之前往往会先执行一些逻辑。我们前面学习的结构特征、矿石特征、单方块特征和树特征都是内容特征,而散植特征、聚合特征、序列特征和随机加权特征都属于代理特征。通常而言,代理特征都是与内容特征相配合而放置的。这一节中,我们就将通过制作一个可以沿着河畔生成的竹子特征来加强内容特征与代理特征的学习。
## 制作设想
我们希望制作一种特征可以使竹子沿着河边生成。但是,依据我们目前所学习到的特征类型,如果单单只使用一种特征,这一设想可能会比较难以实现。我们希望能够使用一些代理特征配合一些内容特征来完成这一点。事实上,在这个过程中,我们只需要完成两个要点,第一个要点便是要寻找一个水方块,第二个要点是在该水方块相邻的固体方块,比如草方块上,分散放置一些我们的竹子。
第一个要点其实有两种实现方式,第一种便是使用搜索特征,当搜索特征在水平面上搜索时,搜索到第一个水方块便意味着这是一个邻岸的方块。第二种是在特征规则中进行多次迭代,总有一些迭代会选择到邻岸的水方块。但是,无论是第一种还是第二种方案,我们如何保证选出来的方块是水呢?这边需要单方块特征的附着和替换放置规则了。事实上,我们只需要制作一个“仅可以通过替换水而放置的水”单方块特征即可。这样的特征只要成功放置,则一定可以保证是放在了水里。但是,我们要注意一点。单方块特征自己替换自己并不能判定为成功,所以我们无法用水来替换水。不过,不知各位开发者们还记得我们在第五章最后一节中曾讲过的静止水和流动水吗?我们可以用流动水来替换静止水,来做到不改变原有的水同时成功替换到水。
第二个要点也比较简单,需要拆分成两点,其一是使用散植特征来放置我们的植物,第二是植物特征本身需要有其下必须是固体方块,比如草方块,的判定。这种判定往往也可以使用单方块特征的附着规则来实现。不过,幸运的是,我们目前还处于硬编码的竹子特征`minecraft:bamboo_feature`本身带有这种判定,我们便无需画蛇添足了。
我们选取第一个要点的第二种方案,同时配合二个要点的设想来制作一个特征层阶树:
```shell
特征规则
└─序列特征(代理)
├─水替换水特征(内容)
└─散植竹子特征(代理)
└─竹子特征(内容)
```
## 制作水替换水特征
我们新建一个`replace_water_feature.json`文件,内容如下:
```json
{
"format_version": "1.13.0",
"minecraft:single_block_feature": {
"description": {
"identifier": "tutorial_demo:replace_water_feature"
},
"places_block": "minecraft:flowing_water",
"enforce_survivability_rules": false,
"enforce_placement_rules": true,
"may_replace": [
"minecraft:water"
]
}
}
```
## 制作散植竹子特征
我们创建一个`bamboo_scatter_feature.json`文件,内容如下:
```json
{
"format_version": "1.13.0",
"minecraft:scatter_feature": {
"description": {
"identifier": "tutorial_demo:bamboo_scatter_feature"
},
"places_feature": "minecraft:bamboo_feature",
"iterations": 8,
"scatter_chance": 100,
"coordinate_eval_order": "zxy",
"project_input_to_floor": true,
"x": {
"distribution": "uniform",
"extent": [ -1, 1 ]
},
"y": "1",
"z": {
"distribution": "uniform",
"extent": [ -1, 1 ]
}
}
}
```
注意,我们这里开启了`project_input_to_floor`即将输入点投影到地板的功能。这代表着该散植特征的Y坐标能像特征规则中的`query.heightmap(variable.worldx, variable.worldz)`那样定位到地形的最上层。由于特征的作用域中无法访问`variable.worldx``variable.worldz`,所以`project_input_to_floor`也是一种非常常见的替代方案。
## 制作序列特征将它们连接在一起
我们创建一个`water_then_bamboo_scatter_feature.json`文件,内容填充如下:
```json
{
"format_version": "1.13.0",
"minecraft:sequence_feature": {
"description": {
"identifier": "tutorial_demo:water_then_bamboo_scatter_feature"
},
"features": [
"tutorial_demo:replace_water_feature",
"tutorial_demo:bamboo_scatter_feature"
]
}
}
```
## 连接特征规则
最后,我们挂接特征规则。我们创建`overworld_bamboo_near_water_feature.json`文件:
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_bamboo_near_water_feature",
"places_feature": "tutorial_demo:water_then_bamboo_scatter_feature"
},
"conditions": {
"placement_pass": "surface_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 20,
"x": {
"distribution": "uniform",
"extent": [
0,
16
]
},
"y": "query.heightmap(variable.worldx, variable.worldz)-1",
"z": {
"distribution": "uniform",
"extent": [
0,
16
]
},
"scatter_chance": 100
}
}
}
```
![](./images/16.11_bamboo_in-game.png)
可以看到,竹子如期在河边生成了!

View File

@@ -0,0 +1,103 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 15分钟
---
# 了解自定义特征规则
在第六章中,我们已经初步学期了如何通过编辑器制作一个**特征规则****Feature Rule**,又译**地物规则**。在本节中我们将从JSON的层面更详细地了解特征规则。
## 了解特征规则文件
我们先来查看我们之前使用编辑器制作的那个特征规则的代码:
```json
{
"format_version": "1.14.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:demo_feature_rules",
"places_feature": "tutorial_demo:demo_feature"
},
"conditions": {
"placement_pass": "surface_pass"
},
"distribution": {
"coordinate_eval_order": "xzy",
"iterations": 1,
"scatter_chance": 10,
"x": {
"distribution": "uniform",
"extent": [
0,
16
]
},
"y": "query.get_height_at(variable.originx,variable.originz)-3",
"z": {
"distribution": "uniform",
"extent": [
0,
16
]
}
}
}
}
```
我们可以看到,特征规则是可以放在`netease_feature_rules`文件夹中的,使用的模式标识符皆为`minecraft:feature_rules`,并且在这里我们使用的是`1.14.0`的格式版本。事实上,我们也可以使用`1.13.0``1.16.0`的格式版本。
在模式标识符下有三个字段,分别是`description``conditions``distribution`,分别是该特征规则的描述、条件和分布。其中,我们可以看到分布对象`distribution`中的内容非常地熟悉。没错,这里和散植特征的格式是完全一致的。其实,每一个已定义的特征规则都会在游戏生成生物群系的不同阶段上**逐区块**地执行一次类似于散植特征的逻辑。简单地说,在每个区块上,特征规则都会随机选取一个点,然后以该点为起始点做一次散植,因此,我们可以在`distribution`对象中定义一套与散植特征相同格式的字段。唯一的不同是,散植特征中的`places_feature`字段在这里被移动到了描述对象`description`中。
条件对象`conditions`则用于定义特征规则放置特征的条件,其中`placement_pass`便是我们之前在第六章中便接触过的特征**放置阶段****Placement Pass**)的定义。这里我们使用了`surface_pass`来代表在生成器生成地形中的地表阶段放置。
在我们这个示例中,三个轴向的坐标的串演算顺序为`xzy`。同时,我们可以看到`y`字段中使用了获得X轴和Z轴起始点处的世界高度的Molang查询函数。如果Y轴先于X、Z轴演算则我们无法通过变量得到X和Z轴的起始点坐标。那么我们目前的逻辑就会失效这也是为什么我们将串演算顺序调整成了`xzy`的原因。
我们再来看一个原版特征规则的示例:
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "minecraft:bamboo_jungle_after_surface_bamboo_feature",
"places_feature": "minecraft:bamboo_then_podzol_feature"
},
"conditions": {
"placement_pass": "after_surface_pass",
"minecraft:biome_filter": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "bamboo"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "jungle"
}
]
},
"distribution": {
"iterations": "math.clamp(math.trunc(math.ceil((query.noise(math.trunc(variable.originx / 80), math.trunc(variable.originz / 80)) + 0.3) * 160)), 15, 160)",
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": "query.heightmap(variable.worldx, variable.worldz)",
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
这里我们可以看到,`conditions`字段下多了一个`minecraft:biome_filter`条件。这个条件是用于过滤生物群系的。通过一个或多个过滤器的线性排列或逻辑组合,我们可以使特征规则仅在特定的生物群系下放置特征,这实现了规则驱动的特征放置,这样放置出的特征也正是我们先前所说的**自动型特征****Automatic Feature**,又译**自动型地物**),即**隐式特征****Implicit Feature**,又译**隐式地物**)。
所以,从宏观上看,我们有两种放置特征的方式,一种是通过特征规则通过条件自动控制放置的隐式特征,另一种是通过生物群系的定义强制放置的显式特征。
至此,我们便基本学习了特征和特征规则的写法。接下来的几个小节,我们一起通过各种演示亲自操作,将各种特征的定义与生成的编写落到实处。

View File

@@ -0,0 +1,115 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 10分钟
---
# 了解自定义矿石特征规则
矿石特征往往用来生成一种矿物的矿石或者类似于矿石那样成矿脉状生成的方块。在本节中,我们一起配置一个红宝石矿石的矿脉特征。
## 使用编辑器配置自定义红宝石方块
![](./images/16.3_ruby.png)
我们通过Photoshop等图片编辑器准备一个红宝石矿石方块纹理贴图。比如我们可以使用修改绿宝石矿石纹理的色相来获取一个红宝石矿石纹理。
![](./images/16.3_ruby_block.png)
我们通过编辑器新建一个方块,并为其赋予我们的纹理。我们使用这个方块`tutorial_demo:ruby_ore`作为我们生成红宝石矿脉的基本方块。
## 设置红宝石矿石特征
我们在行为包的`netease_features`文件夹中新建一个JSON文件并写入如下内容
```json
{
"format_version": "1.13.0",
"minecraft:ore_feature": {
"description": {
"identifier": "tutorial_demo:ruby_ore_feature"
},
"count": 8,
"replace_rules": [
{
"places_block": "tutorial_demo:ruby_ore",
"may_replace": [
"minecraft:stone"
]
}
]
}
}
```
我们将该文件命名为`ruby_ore_feature.json`。注意,特征和特征规则文件的文件名必须和其本身不带有命名空间的标识符匹配。比如我们这里的赋命名空间标识符为`tutorial_demo:ruby_ore_feature`,那么我们的文件名就必须为`ruby_ore_feature`,否则特征可能会加载失败。
我们将`places_block`设置成我们需要放置的方块`tutorial_demo:ruby_ore`,并设置其可以替换方块为原版的石头`minecraft:stone`。这意味着该特征在放置时会**输入位置**处按照矿脉逻辑生成一个矿脉状的替换区域,并将替换区域中的石头替换成我们的红宝石矿石方块。
每个特征都存在一个**输入位置****Input Position**)和一个**输出位置****Output Position**)。我的世界中术语**位置****Position**,简称**Pos**)一般指一个三维的或二维的坐标元组,其中世界位置往往是一个浮点数三元组,而方块位置往往是一个整数三元组。特征的输入位置和输出位置都是方块位置,也就是各分量为整数的一组坐标。每个特征都会以其输入位置为起始点开始放置方块,以其输出位置为结束。输出位置往往是一个特征放置最后一个方块的位置。
我们的红宝石矿石特征使用了矿石特征,会在至少有一个矿石方块替换成功时**成功****Succeed**),在全部矿石皆放置失败时**失败****Fail**)。特征的成功和失败一般不会影响到玩家的游玩层面,但是可能会被一些其他的特征检查以作为放置的判定标准。
## 挂接特征规则
我们在行为包的`netease_feature_rules`文件夹中新建一个`overworld_underground_ruby_ore_feature.json`文件,然后填入如下特征规则即可完成特征到特征规则的挂接。
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_underground_ruby_ore_feature",
"places_feature": "tutorial_demo:ruby_ore_feature"
},
"conditions": {
"placement_pass": "underground_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 50,
"coordinate_eval_order": "zyx",
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": {
"distribution": "gaussian",
"extent": [ 32, 481 ]
},
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
`overworld``overworld_generation`是主世界生物群系都有的生物群系标签。准确的说,在主世界生物群系中,每个生物群系都会具有`overworld``overworld_generation`的其中一个。`overworld`代表主世界地形和旧版世界地形,而`overworld_generation`代表仅主世界的地形生成。
![](./images/16.3_ruby_feature.png)
我们还可以在编辑器中制作特征规则。但是当前阶段我们建议不要在编辑器中进行国际版提供的特征类型的挂接这出于两点考虑其一是当前编辑器尚不支持国际版提供的特征类型的特征挂接其二是不仅编辑器不支持这些特征的挂接并且还会将已经挂接了国际版特征的特征规则中的挂接关系删除。如上图由于编辑器的打开我们手动在JSON中进行的挂接被删除了。
但不论怎样,我们的红宝石矿石特征就制作完成了。显然,这是一个隐式特征,我们来进入世界查看效果。
![](./images/16.3_ruby_feature_in-game.png)
可以看到,红宝石矿石如期生成!

View File

@@ -0,0 +1,117 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 15分钟
---
# 了解自定义单方块特征规则
在本节中,我们继续来看另外一种特征,也就是单方块特征的生成。我们制作一个蓝色蕨方块来进行演示。
## 使用编辑器配置蕨类方块
![](./images/16.4_blue_fern.png)
我们在编辑器中配置一个蓝色的蕨。注意,我们需要将其配置为非固体、透明且不吸收光线,同时具备一个裁剪体积却不具备碰撞体积的样子。我们将其标识符设置为`tutorial_demo:blue_fern`
然后,为了使其具备蕨类的形状,我们需要使用原版的方块形状来做到这一点。我们在资源包的`blocks.json`中手动配置如下:
```json
{
"format_version": [1, 1, 0],
"tutorial_demo:blue_fern": {
"blockshape": "cross_texture",
"textures": "tutorial_demo:blue_fern"
}
}
```
这样,我们就成功使用了交叉纹理的方块形状,使其具备了如原版的草和树苗等方块的交叉形状。
## 设置蕨类单方块特征
我们在特征文件夹中新建一个`blue_fern_feature.json`文件,并填充如下:
```json
{
"format_version": "1.13.0",
"minecraft:single_block_feature": {
"description": {
"identifier": "tutorial_demo:blue_fern_feature"
},
"places_block": "tutorial_demo:blue_fern",
"enforce_survivability_rules": true,
"enforce_placement_rules": true,
"may_attach_to": {
"auto_rotate": false,
"min_sides_must_attach": 1,
"bottom": [
"minecraft:grass",
"minecraft:dirt"
]
},
"may_replace": [
"minecraft:air"
]
}
}
```
我们希望我们的蓝色蕨只能在泥土和草方块上生成,并且只能替换空气,这是因为我们想避免其在空中、水中生成或者直接替换了其他的不该替换的方块比如树干方块,所以我们修改`may_attach_to``may_replace`来达到这一效果。
单方块特征的输入和输出位置为同一个坐标,同时如若方块成功放置即可判定成功,方块放置失败即为判定失败。
## 挂接特征规则
我们在特征规则文件夹中建立`overworld_first_blue_fern_feature.json`文件,然后写入:
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_first_blue_fern_feature",
"places_feature": "tutorial_demo:blue_fern_feature"
},
"conditions": {
"placement_pass": "first_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 1,
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": "query.heightmap(variable.worldx, variable.worldz)",
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
习惯上我们命名特征规则时将生成的位置,比如主世界还是哪些生物群系放在最前面,然后生成的放置阶段放在中间,生成的物体名称放在阶段之后,最后和特征一样使用`feature`结尾。不过,每个开发者也有自己的命名习惯,不必拘泥于原版我的世界的命名习惯。
我们在每个区块中生成一个我们的蓝色蕨方块来试验效果。所以我们将迭代次数`iterations`设为1。
![](./images/16.4_blue_fern_in-game.png)
可以看到,我们的蓝色蕨确实如我们希望的那样进行生成了!

View File

@@ -0,0 +1,93 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 10分钟
---
# 了解自定义散植特征规则
我们继续使用上一节的蓝色蕨方块来编写散植特征。我们希望在世界中生成更多的蓝色蕨,使其形成一个簇。
## 设置蕨类散植特征
我们在特征文件夹中建立`blue_fern_cluster_feature.json`文件。
```json
{
"format_version": "1.13.0",
"minecraft:scatter_feature": {
"description": {
"identifier": "tutorial_demo:blue_fern_cluster_feature"
},
"places_feature": "tutorial_demo:blue_fern_feature",
"iterations": 10,
"scatter_chance": 50.0,
"x": {
"distribution": "uniform",
"extent": [ 0, 5 ]
},
"y": 0,
"z": {
"distribution": "uniform",
"extent": [ 0, 5 ]
}
}
}
```
注意,在命名上我们习惯于将均匀分布的植物群体称为一个**簇****Cluster**),而高斯分布的植物群系称为一个**斑块****Patch**)。当然,对于游戏引擎本身来说,命名或许没那么重要,但是对于可读性而言,一个良好的命名还是必需的。
散植特征在放置时,只要至少一次迭代中的目标特征成功放置就会判定成功,全部迭代的目标特征全部失败就会判定失败。
## 挂接特征规则
我们在特征规则文件夹中新建`overworld_blue_fern_cluster_feature.json`文件。
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_blue_fern_cluster_feature",
"places_feature": "tutorial_demo:blue_fern_cluster_feature"
},
"conditions": {
"placement_pass": "surface_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 5,
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": "query.heightmap(variable.worldx, variable.worldz)",
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
我们迭代5次加之以特征本身有50%的概率放置相当于我们平均在一个区块中放置2.5个蓝色蕨簇。
![](./images/16.5_blue_fern_cluster_in-game.png)
可以看到,生成符合我们的预期!

View File

@@ -0,0 +1,86 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 15分钟
---
# 了解自定义聚合特征规则
我们承接前面的蓝色蕨散植特征,我们希望将这种特征和橡树相结合,生成一种树荫下带有蓝色蕨簇的橡树,我们此时需要使用聚合特征。
## 使用聚合特征连接蕨类与树木
我们建立`oak_tree_with_blue_fern_cluster_feature.json`文件,并填充如下:
```json
{
"format_version": "1.13.0",
"minecraft:aggregate_feature": {
"description": {
"identifier": "tutorial_demo:oak_tree_with_blue_fern_cluster_feature"
},
"early_out": "first_failure",
"features": [
"minecraft:oak_tree_feature",
"tutorial_demo:blue_fern_cluster_feature"
]
}
}
```
聚合特征会使列表中的特征全部以同一个起始点为输入点,也就是自己的输入点为输入点来放置多个特征。比如这里我们将橡树特征和蓝色蕨簇特征以同一个点为基础放置。橡树的输入点在树根部,而散植特征则是围绕输入点在周围多次散植,因此我们可以营造一种树荫下带有蓝色蕨簇的橡树。
`early_out`指定了何时退出本次特征放置,`first_failure`代表在列表中出现第一个判定失败的特征时便退出本次放置。但是这并不意味着本次聚合特征放置也会失败。聚合特征的判定为列表中只要有至少一个特征放置成功便是成功。注意,聚合特征的放置列表的顺序并不一定是实际放置的顺序,其顺序不能得到保证。
## 挂接特征规则
我们建立`overworld_oak_tree_with_blue_fern_cluster_feature.json`文件。
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_oak_tree_with_blue_fern_cluster_feature",
"places_feature": "tutorial_demo:oak_tree_with_blue_fern_cluster_feature"
},
"conditions": {
"placement_pass": "surface_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 5,
"x": {
"distribution": "uniform",
"extent": [ 0, 16 ]
},
"y": "query.heightmap(variable.worldx, variable.worldz)",
"z": {
"distribution": "uniform",
"extent": [ 0, 16 ]
}
}
}
}
```
并进入游戏查看效果。
![](./images/16.6_fern_tree_in-game.png)
我们可以看到,确实出现了很多与橡树伴随生成的蓝色蕨!

View File

@@ -0,0 +1,92 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 15分钟
---
# 了解搜索特征规则
本节中,我们继续一起了解搜索特征。我们尝试使用搜索特征为我们的橡树附着上我们之前在第十章中制作的自定义苹果方块,使其形成一个会结果实的橡树。
## 设置苹果单方块特征
我们还记得,我们的方块标识符为`tutorial_demo:apple`。我们新建一个单方块特征`apple_feature.json`
```json
{
"format_version": "1.13.0",
"minecraft:single_block_feature": {
"description": {
"identifier": "tutorial_demo:apple_feature"
},
"places_block": "tutorial_demo:custom_apple",
"enforce_survivability_rules": true,
"enforce_placement_rules": true,
"may_attach_to": {
"auto_rotate": false,
"min_sides_must_attach": 2,
"top": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
"west": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
"north": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
"east": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
},
"south": {
"name": "minecraft:leaves",
"states": {
"old_leaf_type": "oak"
}
}
},
"may_replace": [
"minecraft:air"
]
}
}
```
这里我们为其进行了严格的替换设置。我们期望一个苹果在上、前、后、左、右五个方向上至少有两个方向是连接像树叶的。这样,我们的苹果才会看起来是挂在橡树上一样,不至于特别突兀。
## 使用搜索特征连接苹果特征
我们新建`apple_search_feature.json`文件:
```json
{
"format_version": "1.13.0",
"minecraft:search_feature": {
"description": {
"identifier": "tutorial_demo:apple_search_feature"
},
"places_feature": "tutorial_demo:apple_feature",
"search_volume": {
"min": [ -4, -16, -4 ],
"max": [ 4, 0, 4 ]
},
"search_axis": "-y",
"required_successes": 1
}
}
```
我们这里从输入位置开始向Y轴负方向也就是向下开始搜索当成功获取一个能够使苹果单方块特征`tutorial_demo:apple_feature`的位置时便停止搜索并放置该单方块特征。当找到的“能够使要放置的特征成功”的位置少于`required_successes`的数值时就会使该搜索特征放置判定失败。
我们在下一节中继续通过序列特征实现该搜索特征的放置。

View File

@@ -0,0 +1,36 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 10分钟
---
# 了解序列特征规则
在本节中,我们使用序列特征将上一节中的苹果挂在橡树上。
## 使用序列规则连接苹果特征
我们新建`oak_tree_then_apple_feature.json`文件:
```json
{
"format_version": "1.13.0",
"minecraft:sequence_feature": {
"description": {
"identifier": "tutorial_demo:oak_tree_then_apple_feature"
},
"features": [
"minecraft:oak_tree_feature",
"tutorial_demo:apple_search_feature"
]
}
}
```
序列特征的放置列表中的特征的放置顺序是固定的,从列表中第一个特征放置到最后一个,同时上一个特征的输出位置会变成下一个特征的输入位置。树特征的输出位置在树顶,所以我们的苹果搜索特征才需要从上到下搜索,否则将搜索不到满足的位置。
和聚合特征不同,序列特征需要列表中全部特征都完成放置才会判定成功,而如果中途某个特征放置失败则整个特征都会被判定为失败。不过,列表中已放置的特征不会消失,但是失败特征之后的特征将全部被跳过,不再放置。
![](./images/16.8_apple_tree_in-game.png)
我们可以看大, 我们的橡树上果然挂上了一个个苹果。

View File

@@ -0,0 +1,154 @@
---
front: https://nie.res.netease.com/r/pic/20211104/69055361-2e7a-452f-8b1a-f23e1262a03a.jpg
hard: 高级
time: 10分钟
---
# 了解加权随机特征规则
加权随机特征可以用于按比重生成多种特征。我们使用加权随机特征来生成一些各种样式的水井。
## 使用编辑器导出多种水井结构
![](./images/16.9_save_structures.png)
我们在地图编辑器中制作多种水井,并将其导出。导出的结构会自动进入行为包的`structures/<namespace>`文件夹中。这里,由于我们在编辑器中设置的命名空间为`tutorial_demo`,我们的结构会放置于`structures/tutorial_demo`文件夹中。
## 使用结构特征配置水井
我们使用中国版的结构特征来制作四种水井的特征。
```json
{
"format_version": "1.14.0",
"netease:structure_feature": {
"description": {
"identifier": "tutorial_demo:woodwell_structure_feature"
},
"places_structure": "tutorial_demo:woodwell"
}
}
```
```json
{
"format_version": "1.14.0",
"netease:structure_feature": {
"description": {
"identifier": "tutorial_demo:stonewell_structure_feature"
},
"places_structure": "tutorial_demo:stonewell"
}
}
```
```json
{
"format_version": "1.14.0",
"netease:structure_feature": {
"description": {
"identifier": "tutorial_demo:cobblestonewell_structure_feature"
},
"places_structure": "tutorial_demo:cobblestonewell"
}
}
```
```json
{
"format_version": "1.14.0",
"netease:structure_feature": {
"description": {
"identifier": "tutorial_demo:stonebrickswell_structure_feature"
},
"places_structure": "tutorial_demo:stonebrickswell"
}
}
```
由于我们的结构位于`tutorial_demo`文件夹中,因此命名空间是`tutorial_demo`。我们将其挂接到`places_structure`中,即可实现结构特征的制作。
## 使用加权随机特征分配水井权重
我们新建`random_well_feature.json`文件:
```json
{
"format_version": "1.13.0",
"minecraft:weighted_random_feature": {
"description": {
"identifier": "tutorial_demo:random_well_feature"
},
"features": [
["tutorial_demo:woodwell_structure_feature", 1],
["tutorial_demo:stonewell_structure_feature", 1],
["tutorial_demo:cobblestonewell_structure_feature", 1],
["tutorial_demo:stonebrickswell_structure_feature", 1]
]
}
}
```
我们将所有水井的权重设置为1这样每个水井都有$\frac{1}{1+1+1+1}=\frac{1}{4}$的概率生成。当选中的水井成功生成时该特征会判定成功,否则就会判定失败。
## 挂接特征规则
我们创建`overworld_well_structure_feature.json`文件,并写入如下:
```json
{
"format_version": "1.13.0",
"minecraft:feature_rules": {
"description": {
"identifier": "tutorial_demo:overworld_well_structure_feature",
"places_feature": "tutorial_demo:random_well_feature"
},
"conditions": {
"placement_pass": "surface_pass",
"minecraft:biome_filter": [
{
"any_of": [
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld"
},
{
"test": "has_biome_tag",
"operator": "==",
"value": "overworld_generation"
}
]
}
]
},
"distribution": {
"iterations": 1,
"x": {
"distribution": "uniform",
"extent": [
0,
16
]
},
"y": "query.heightmap(variable.worldx, variable.worldz)-3",
"z": {
"distribution": "uniform",
"extent": [
0,
16
]
},
"scatter_chance": 100
}
}
}
```
![](./images/16.9_in-game_1.png)
我们可以看到,水井特征如期生成了。
![](./images/16.9_in-game_2.png)
当然,虽然我们的特征规则设置为了每个区块放置一次加权随机特征,但是也是有几率出现这种三联水井的情况的。这代表我们的特征放置非常成功!