生物群系 26.1 Fabric
本篇教程的视频
本篇教程的源代码
GitHub地址:
本篇教程目标
- 搞清一个群系由哪四部分组成
- 认识 26.1 的一个改动:天空色、雾色搬进了环境属性
- 用数据生成写一个完整的群系
- 用
TerraBlender把群系塞进主世界,并改掉它的地表
介绍
群系决定了一片地方长什么样
温度、降水、地面刷什么怪、长什么树、天是什么颜色,都是群系说了算
从数据上看,群系就是一份 json,和 configured_feature 一样是数据包注册表
四部分
一个群系可以拆成四块
气候:温度、降水量、有没有降水。这几个数字会影响下雪、结冰、草的颜色
特效:水色、草色、叶色,加上天空色、雾色
生成设置:这个群系里塞哪些地物,洞穴、矿石、树、花都在这里挂着
刷怪设置:刷哪些怪、权重多少、一次刷几只
这期做到哪
群系本身我们能完整做出来,但「让它自然分布在主世界」是另一件事
因为主世界的群系分布是由一张巨大的参数表定的(温度、湿度、侵蚀度这些维度上的参数点),Fabric 没有官方 API 能往那张表里加东西
所以这期分两段:先把群系定义做扎实,再用 TerraBlender 把它接进主世界,顺便把地表换成我们自己的方块
查看源代码
原版平原
原版的群系都在 OverworldBiomes 这个类里,看平原
1 | return baseBiome(0.8F, 0.4F) |
baseBiome(temperature, downfall) 是原版抽出来的小工具,把公共部分(生成设置、刷怪)都塞好了
我们自己的群系没这个便利,得一个个填
26.1 的一个改动
在 1.21 及更早,天空色、雾色是写在 BiomeSpecialEffects 里的
1 | // 1.21 的写法,26.1 已经不能这么写 |
26.1 把这三个搬进了「环境属性」(EnvironmentAttributeMap)
1 | EnvironmentAttributeMap darkForestAttributes = EnvironmentAttributeMap.builder() |
现在 BiomeSpecialEffects.Builder 只剩四个方法
waterColor、grassColorOverride、foliageColorOverride、dryFoliageColorOverride
颜色类的东西被拆成了两半:水和植物还在 specialEffects,天空和雾去了 attributes
为什么这么改?因为 26.1 搞了一套通用的「环境属性」系统,光照、音乐、迷雾、水雾全都走这套键值,群系只是其中一个使用方
对我们来说就是记住:天空色、雾色、水雾色写在 attributes 里,水色、草色、叶色写在 effects 里
动手写
注册键
新建 ModBiomes
1 | public static final ResourceKey<Biome> ICE_ETHER_FOREST = ResourceKey.create(Registries.BIOME, |
刷怪设置
1 | MobSpawnSettings.Builder spawns = new MobSpawnSettings.Builder(); |
farmAnimals 加的是牛羊猪鸡,commonSpawns 加的是僵尸骷髅这些常见怪
这两行直接把原版平原的配置搬过来了
再补一个自己的:兔子,权重 8,一次 2 到 4 只
SpawnerData 的后两个参数是 minCount 和 maxCount,顺序别写反
生成设置
1 | BiomeGenerationSettings.Builder generation = new BiomeGenerationSettings.Builder( |
第一个参数是「放置特征」的表,第二个是「雕刻器」的表
这两个都得从 context 里拿,因为群系里存的是引用,不是实体
然后往里面塞东西
1 | BiomeDefaultFeatures.addDefaultCarversAndLakes(generation); |
addDefaultCarversAndLakes:洞穴和湖泊
addDefaultOres:所有原版矿石,当然这里实际上指的是石头、深板岩、花岗岩等这种岩石,为后面的真正我们指的煤矿、铁矿等矿物生成做铺垫
addDefaultSoftDisks:沙子、砂砾、黏土那些圆盘(参见 Wiki 圆盘相关内容)
addDefaultExtraVegetation:甘蔗、南瓜这类额外植被
最后加我们自己的
1 | generation.addFeature(GenerationStep.Decoration.VEGETAL_DECORATION, ModPlacedFeatures.ICE_ETHER_TREE_PLACED); |
注意这里要传的是放置特征,不是构造特征
传错了群系能生成,但那两样东西一棵都不会长
特效
1 | EnvironmentAttributeMap attributes = EnvironmentAttributeMap.builder() |
天空、雾、水下雾,三个冷色调
1 | .specialEffects(new BiomeSpecialEffects.Builder() |
水色、草色、叶色
最终注册
1 | return new Biome.BiomeBuilder() |
温度给 0.1F, 降水量 0.7F
hasPrecipitation(true) 是有降水
然后 context.register(ICE_ETHER_FOREST, ...) 注册
数据生成
和前面几期一样,两步
buildRegistry
1 | registryBuilder.add(Registries.BIOME, ModBiomes::bootstrap); |
ModDynamicRegistryProvider
1 | entries.addAll(registries.lookupOrThrow(Registries.BIOME)); |
用 TerraBlender 进主世界
Fabric 官方没有提供「往主世界塞群系」的 API
主世界的群系分布是由 multi_noise_biome_source_parameter_list/overworld 那份数据定的,里面是一大堆「温度 / 湿度 / 侵蚀 / 怪异度」的参数点,每个点指向一个群系
所以社区的标准做法是用第三方库,也就是TerraBlender,NeoForge那期用的也是它
依赖
build.gradle 里加仓库和依赖
1 | repositories { |
版本号这块得留神
TerraBlender 的版本号是 <对应的 MC 版本>-<它自己的版本> 两段拼起来(具体版本可以到模组站上去找已发布的版本)
入口点
TerraBlender 得等它自己初始化完之后,再来调我们的代码
Fabric 这边的做法是加一个自定义入口点,在 fabric.mod.json 里
1 | { |
少了这一行,代码编译得好好的,运行时就是不执行,一点提示都没有
区域
新建 ModOverworldRegion,继承 TerraBlender 的 Region
1 | public class ModOverworldRegion extends Region { |
RegionType.OVERWORLD 限定它只管主世界
addModifiedVanillaOverworldBiomes 的意思是「拿原版那张参数表来改」
replaceBiome(原来的群系, 我们的群系) 就是把雪原针叶林换成冰以太森林
也就是说,以后你走到原本该是雪原针叶林的地方,看到的是我们的群系
如果你想「新增」而不是替换,就用父类的 addBiome(...),自己给六个气候参数和一个偏移值,相当于在原版那张表上多插一个点
表面规则
群系换掉了,但地面还是原版那套(草方块 / 石头)
想让地面长得不一样,得写表面规则
1 | public class ModSurfaceRules { |
SurfaceRules.state(方块状态) 是「这里放这个方块」
ifTrue(条件, 规则) 是「满足条件才用这条」
sequence 是「从上往下试,谁先匹配谁生效」
这里用了三个深度条件,得分开理解
ON_FLOOR:只匹配地表最上面那一格,放草
stoneDepthCheck(0, true, 3, CaveSurface.FLOOR):地表往下 3 格以内,放泥土
DEEP_UNDER_FLOOR:地表往下 6 格以外,换成钻石块
ABOVE_WATER 是「水位以上」,这样水下那一格不会长草,而是泥土(原版也是这么干的)
iceEtherDeep 外面那个 isBiome 一定要加,不加的话整个主世界的地下都会变成钻石
这里踩过一个坑
之前的教程草方块的生成有问题,水面以上的草方块直接一层一层堆下去了,往下挖十几格(如果地表够厚的话)还是草方块,底下不是泥土或者石头
abovePreliminarySurface() 这个名字有点误导,它只是个「高度判断」
它回答的是「这个位置在地表之上吗」,不管深度
真正管深度的是 ON_FLOOR、UNDER_FLOOR、DEEP_UNDER_FLOOR 这一类条件
少了深度检查,那个柱体里所有「在地表以上 + 在水位以上」的方块都会被换成草,于是堆上去了
所以写表面规则之前,先想清楚这次要改的是「哪一层」,再挑对应的深度条件
注册
1 | public class ModTerraBlenderApi implements TerraBlenderApi { |
Regions.register 注册区域,第二个参数是权重
命名空间不用怕撞:TerraBlender 自己有个 minecraft:overworld,我们的是 tutorial:overworld
addSurfaceRules 把表面规则挂到主世界
测试
在跑完数据生成之后,我们就可以启动游戏进行测试了
常见问题(大肥鱼总结版)
- 日志里没有
Registered region:fabric.mod.json少了terrablender入口点,或者类名写错 - 群系进了世界但地表没变:表面规则没注册,或者
isBiome里的 key 写错 - 草方块一层层堆到地下,草下面没有泥土和石头:表面规则少了深度条件(
ON_FLOOR),abovePreliminarySurface()只管高度不管深度 - 整个主世界地下都变成冰以太:表面规则少了
isBiome限定 /locate biome说找不到:可能这片区域离得太远,往一个方向多走几个区块再试;也可能是替换的群系(雪原针叶林)本来就在很冷的地方/fillbiome说找不到群系:数据没进 jar(buildRegistry漏挂Registries.BIOME),或者 json 序列化失败了- 数据生成目录里没有 biome 文件:去翻日志,大概率是
minCount needs to be smaller or equal to maxCount,SpawnerData的参数写反了 - 天空颜色没变:写进了
specialEffects,26.1 得写进EnvironmentAttributeMap - 群系里一棵树都没有:
addFeature传成了ConfiguredFeature,要传PlacedFeature - 群系里没有矿:
addDefaultOres没加,或者矿的 placed feature 没挂在群系上 - 改了群系颜色但游戏里没变:旧区块缓存了,新建世界
- 温度设了
0.0F却不结冰:结冰还跟降水、光照有关,光看温度不够 - 想加点新的而不是替换:
Region里用addBiome(...)自己给参数,别用replaceBiome







