树 1.20 Fabric 长线教程计划
本篇教程的视频
(待发布)
本篇教程的源代码
(待发布)
本篇教程目标
- 理解原版的树的构造特征
- 学会自己编写基本的树的构造特征
- 学会编写树的生成器
查看源代码
那么本期教程我们来写树,后面一期的话就是树的世界生成
树的话,在游戏里面属于地物(Feature)
的一种,是一种多方块结构,这也就意味着,它有自己的构造特征
这里我们也来看看源代码相关的内容
我们找到TreeConfiguredFeatures
这个类,后面我们还会接触TreePlacedFeatures
这个类
前者是我们这期教程需要理解的构造特征类,后面是涉及其世界生成用的放置特征类
与TreeConfiguredFeatures
类似,原版其他地物也有各种各样的构造特征类,感兴趣的同学可以自行研究
构造特征类
我们直接来看源代码吧
1 | public static final RegistryKey<ConfiguredFeature<?, ?>> OAK = ConfiguredFeatures.of("oak"); |
这里我们瞅瞅橡木
的,可以看到,这里注册了一个构造特征的注册键
ConfiguredFeatures.of
方法是一个公共的方法,我们自己写的时候也可以直接用,但要加上模组的命名空间
我们继续往下看
1 | private static TreeFeatureConfig.Builder builder(Block log, Block leaves, int baseHeight, int firstRandomHeight, int secondRandomHeight, int radius) { |
这里我们看到了一个builder
方法,这个方法返回一个TreeFeatureConfig.Builder
对象
这个其实就是树它的构造特征的生成器,不过它是只用于最简单的直树干
的,而像樱花树
那样的,也有它自己的生成器
我们先来看看builder
方法中的返回值
BlockStateProvider.of
需要传入一个方块,第一个传入树干
方块,第二个传入树叶
方块
StraightTrunkPlacer
是树干生成器,它需要传入树干的高度,以及两个随机高度
,后面两个随机高度决定树干的下限
和上限
高度
BlobFoliagePlacer
是树叶生成器,它的第一个参数是树叶的半径
,第二个参数是树叶的偏移量
,第三个参数是树叶的生成高度
TwoLayersFeatureSize是配置树的层级结构,它的第一个参数是树底部的高度,一般指树干下方的叶子层数
,
第二个参数是树顶高度,一般指树干上方叶子层数
,第三个是树顶高度的随机增量
1 | private static TreeFeatureConfig.Builder oak() { |
再往下看,我们就能看到一个oak
方法,这个是橡树的构造生成方法,调用了上面的builder
方法
1 | public static void bootstrap(Registerable<ConfiguredFeature<?, ?>> featureRegisterable){ |
接下来我们能看到一个bootstrap
方法,这个东西呢,在我们之后的开发过程中,你就将其理解为用于数据生成
的方法,后面的数据生成我们也会用到
在这个方法中,我们也可以找到橡木
相关的内容
1 | ConfiguredFeatures.register(featureRegisterable, OAK, Feature.TREE, oak().build()); |
不过也就这一句话,因为它的构造方法已经在上面写了
而其他的树,其实也都类似
1 | ConfiguredFeatures.register( |
比如这个深色橡木
,它的生成器写法和橡木其实差不多
它这里还有ignoreVines
方法,这个方法的意思是忽略藤蔓
,也就是生成树的时候,不会生成藤蔓
树的构造特征就这样差不多了,当然这里我们只看来最基本的橡树的构造特征,光是橡树也有很多变种,比如珍异橡木
、大型珍异橡木
、覆藤橡木
等等,还有带蜂巢
的
不过呢,还是得从最基本的开始了解,后面的再慢慢研究
树苗生成器
我们还要来看树苗的生成器,让我们的树从树苗长成一个多方块结构的树
这里我们看看OakSaplingGenerator
这个类,这个橡树树苗的生成类
1 | public class OakSaplingGenerator extends SaplingGenerator { |
这个类是继承SaplingGenerator
,重写了getTreeFeature
方法
当然,上面也说过了,橡树有很多变种,所以它这里就取随机来选择橡树的构造特征
如果我们没那么复杂的话,就直接返回构造特征的注册键
就行
注册树
编写构造特征
这里我们创建ModConfiguredFeatures
类,用于定义地物的构造特征,当然,你也可以像原版那样按照不同种类分开来
1 | public class ModConfiguredFeatures { |
这里我们先来写注册键
1 | public static final RegistryKey<ConfiguredFeature<?, ?>> ICE_ETHER_TREE_KEY = of("ice_ether_tree"); |
注册键的注册方法用的是ConfiguredFeatures.of
,不过我们加上了模组的命名空间
接下来我们创建bootstrap
方法,用于注册构造特征,也用于后面的数据生成
1 | public static void bootstrap(Registerable<ConfiguredFeature<?, ?>> featureRegisterable) { |
bootstrap
方法需要传入一个Registerable<ConfiguredFeature<?, ?>>
对象,这个对象是用于注册构造特征的,这个与原版是一样的
接下来就是写树的构造特征
1 | ConfiguredFeatures.register(featureRegisterable, ICE_ETHER_TREE_KEY, Feature.TREE, |
注册键传入我们上面写的注册键,原木和树叶都是我们在之前的教程中注册好的
另外的树干
、树叶
、层级
构造特征我们就不一一解释了,上面都说过了
其实,树干、树叶以及层级的构造器都是可以自己自定义的,你可以参考参考相关的模组
树苗生成器
接下来我们创建IceEtherTreeGenerator
类,用于定义树苗的生成器
1 | public class IceEtherTreeGenerator extends SaplingGenerator { |
继承SaplingGenerator
类,并重写getTreeFeature
方法,这里我们没有那么多变种,所以就直接返回树的构造特征注册键
注册树苗方块
1 | public static final Block ICE_ETHER_TREE_SAPLING = register("ice_ether_tree_sapling", |
那么树苗它还是一个方块
,所以我们还得来注册一下
这里实例化的是SaplingBlock
,第一个参数是对应的树苗生成器,传入我们上面写的类即可
加入物品栏
1 | entries.add(ModBlocks.ICE_ETHER_TREE_SAPLING); |
最后别忘了加入物品栏
树苗渲染层设置
常规的,树苗的材质一般也有全透明
的区域,所以我们得设置一下它的渲染层
到TutorialModReClient中添加
1 | BlockRenderLayerMap.INSTANCE.putBlock(ModBlocks.ICE_ETHER_TREE_SAPLING, RenderLayer.getCutout()); |
数据生成
世界生成类
这里我们创建一个ModWorldGenerator
,并继承FabricDynamicRegistryProvider
1 | public class ModWorldGenerator extends FabricDynamicRegistryProvider { |
这个数据生成类与我们之前写的都不一样,它继承的是FabricDynamicRegistryProvider
,这是Fabric
提供的一个动态数据生成类,它允许我们在数据生成的时候动态的注册数据
这里我们重写了configure
方法,这里我们要将CONFIGURED_FEATURE
注册键的相关的数据注册到entries
中,这样我们就可以在数据生成的时候动态的注册数据了
getName
方法返回的是数据生成的名称,随便写一个就可以了
TutorialModReDataGenerator
接下来我们到模组的数据生成主类中,将我们写的世界生成类注册进去
在onInitializeDataGenerator
方法中添加
1 | pack.addProvider(ModWorldGenerator::new); |
然后,我们还要重写一个buildRegistry
方法
1 |
|
用它来调用我们ModConfiguredFeatures
中的bootstrap
方法
语言文件
1 | translationBuilder.add(ModBlocks.ICE_ETHER_TREE_SAPLING, "Ice Ether Sapling"); |
模型文件
树苗的模型文件生成方法与其他的方块有一点区别
1 | blockStateModelGenerator.registerTintableCross(ModBlocks.ICE_ETHER_TREE_SAPLING, BlockStateModelGenerator.TintType.NOT_TINTED); |
它用的是registerTintableCross
方法,后面的NOT_TINTED
表示不使用贴图颜色,意思就是会不会随着生物群系
的改变而改变其颜色
测试
那么,放好树苗的贴图文件之后,数据生成也要运行一下
数据生成会生成树的构造特征的json文件,在数据文件夹中的world_gen/configured_features文件夹中
最后我们就可以进入游戏进行测试了