花的世界生成 26.1 Fabric
本篇教程的视频
本篇教程的源代码
GitHub地址:
本篇教程目标
- 搞清花和树的区别:花是「一片一片」长的
- 认识 26.1 的一个大改动:随机斑块被搬到了放置特征里
- 学会用放置修饰器拼出「撒一片花」的效果
- 学会只往指定群系里加
介绍
上一期我们做了花和盆栽,花能放、能进花盆,但野外还是没有
这一期把它塞进世界生成
花和树都是地物,但形态不一样
树是一棵一棵的,位置比较独立;花是一簇一簇的,一小片地方能挤十几朵
原版把这种「一小片」的形式叫随机斑块(random patch)
26.1 的一个大改动
在 1.21 及更早,随机斑块是一个构造特征
你会看到一个 Feature.FLOWER,配一个RandomPatchConfiguration,里面写着「撒几次、水平多大、垂直多大」
26.1 把这两个东西都删了
现在的花,构造特征就是「放一个方块」(Feature.SIMPLE_BLOCK),「撒一片」的效果改由放置特征来拼
所以你要是在网上找老教程照着写 new RandomPatchConfiguration(20, 4, 3, ...),26.1 里根本找不到这个类
现在我们得换一种方式了
查看源代码
构造特征
原版平原上的花,在 VegetationFeatures 里
1 | FeatureUtils.register(context, FLOWER_DEFAULT, Feature.SIMPLE_BLOCK, new SimpleBlockConfiguration(provider)); |
就一句
Feature.SIMPLE_BLOCK 是「在这儿放一个方块」,SimpleBlockConfiguration 里给它一个方块状态提供器
provider 可以是单一方块,也可以是一组带权重的方块(比如蒲公英和虞美人按 2:1 混着长)
放置特征
一片花是怎么来的,看沼泽那组
1 | PlacementUtils.register(context, FLOWER_SWAMP, flowerSwamp, |
七条修饰器,可以分成两段读
前四条决定「中心点选在哪」
RarityFilter.onAverageOnceEvery(32):平均每 32 个区块才选一次,也就是「稀疏」InSquarePlacement.spread():在这个区块里随机取一个点PlacementUtils.HEIGHTMAP:把这个点贴到地表BiomeFilter.biome():确认这个位置属于当前群系
后三条决定「在中心点周围撒多大一片」
CountPlacement.of(64):以这个点为中心,撒 64 朵RandomOffsetPlacement.ofTriangle(6, 2):水平方向 ±6 格、垂直方向 ±2 格,三角形分布(越靠中心越密)BlockPredicateFilter:只往空气里塞,别把花塞进石头
后三条合起来,就是以前那个 RandomPatchConfiguration 干的事
为什么这么改(猜测)
虽然写起来更复杂了,但同样更灵活了,我们可以更多样地处理不同的植物生成
以前斑块只能整块用,现在你可以给「撒一片」这一步单独加条件
比如原版的花海,前面还会插一个NoiseThresholdCountPlacement,让花只长在噪声的低谷里,形成那种一片密一片稀的效果
这在以前的斑块配置里是做不到的(除非自己搓相关的方法)
动手写
构造特征
到 ModConfiguredFeatures 加一个注册键
1 | public static final ResourceKey<ConfiguredFeature<?, ?>> SIMPLE_FLOWER = createKey("simple_flower"); |
然后在 bootstrap 里注册
1 | FeatureUtils.register(context, SIMPLE_FLOWER, Feature.SIMPLE_BLOCK, |
就一朵花,没有别的参数
放置特征
到 ModPlacedFeatures,注册键
1 | public static final ResourceKey<PlacedFeature> SIMPLE_FLOWER_PLACED = createKey("simple_flower_placed"); |
注册
1 | PlacementUtils.register(context, SIMPLE_FLOWER_PLACED, |
onAverageOnceEvery(4):平均每 4 个区块选一个中心点
CountPlacement.of(32):一个中心点撒 32 朵,比原版 64 少一半
注入群系
1 | BiomeModifications.addFeature(BiomeSelectors.includeByKey(Biomes.PLAINS), |
这里第一次用 includeByKey
前面做树和矿的时候我们用的是 foundInOverworld(),那是「整个主世界」
includeByKey(Biomes.PLAINS) 是「只有平原」,想多加几个就逗号隔开
Biomes 类是原版的生物群系常量类,Biomes.PLAINS 就是平原
花属于植被,阶段用 VEGETAL_DECORATION
数据生成
这期还是不用改主类,CONFIGURED_FEATURE 和 PLACED_FEATURE 前面已经挂过了
测试
跑完数据生成我们就可以启动游戏进行测试了
常见问题(大肥鱼总结版)
- 编译报找不到
RandomPatchConfiguration:26.1 删了这个类,花要用Feature.SIMPLE_BLOCK+ 放置修饰器拼 - 野外一朵花都没有:
BiomeModifications没写,或者includeByKey(Biomes.PLAINS)的群系选错了(你去的不是平原) - 只有孤零零一朵一朵,不成片:少了
CountPlacement+RandomOffsetPlacement这两条,只剩「撒点」阶段 - 花多到铺满地面:
onAverageOnceEvery(4)的 4 太小,或者CountPlacement.of(32)太大 - 花嵌在石头里:
PlacementUtils.HEIGHTMAP用成了HEIGHTMAP_OCEAN_FLOOR之类,或者漏了空气过滤 - 花长到别的群系去了:用了
foundInOverworld(),应该用includeByKey(...) - 花长在树叶上:
HEIGHTMAP会取到树叶上方,想避开就把MOTION_BLOCKING换成MOTION_BLOCKING_NO_LEAVES - 数据生成没新文件:
Registries.CONFIGURED_FEATURE/PLACED_FEATURE没挂在buildRegistry里 - 改了参数游戏里没变化:旧存档区块已生成,得新建世界
- 花开在水上:水面被
HEIGHTMAP当成地表了,加一条BlockPredicate过滤水







