本篇教程的视频
(待发布)
本篇教程的源代码
(待发布)
本篇教程目标
- 理解原版矿物的构造特征和放置特征
- 学会为矿物配置构造特征和放置特征
查看源代码
矿物,或者说矿脉,它也是属于地物的,所以它也有构造特征和放置特征
构造特征
矿物的构造特征我们可以在OreConfiguredFeatures
类中找到
1 2
| public static final RegistryKey<ConfiguredFeature<?, ?>> ORE_COAL = ConfiguredFeatures.of("ore_coal"); public static final RegistryKey<ConfiguredFeature<?, ?>> ORE_COAL_BURIED = ConfiguredFeatures.of("ore_coal_buried");
|
这里我们以煤矿
为例,首先是注册键,煤矿分了两个,一个是普通的,一个是埋藏的,后者大概是埋在岩层中的,而不是直接裸露在外的
再接下来的bootstrap
方法中,我们可以看到一堆RuleTest
1 2 3 4 5
| RuleTest ruleTest = new TagMatchRuleTest(BlockTags.BASE_STONE_OVERWORLD); RuleTest ruleTest2 = new TagMatchRuleTest(BlockTags.STONE_ORE_REPLACEABLES); RuleTest ruleTest3 = new TagMatchRuleTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES); RuleTest ruleTest4 = new BlockMatchRuleTest(Blocks.NETHERRACK); RuleTest ruleTest5 = new TagMatchRuleTest(BlockTags.BASE_STONE_NETHER);
|
这些其实就是矿物生成规则中可以用来替换
的方块,我的理解是,在世界生成的过程中,
先是基本岩石
先生成,比如Stone
,然后矿物
根据规则判断一些方块是否可以被替换
,将那些方块替换为矿物
1 2 3 4
| List<OreFeatureConfig.Target> list6 = List.of( OreFeatureConfig.createTarget(ruleTest2, Blocks.COAL_ORE.getDefaultState()), OreFeatureConfig.createTarget(ruleTest3, Blocks.DEEPSLATE_COAL_ORE.getDefaultState()) );
|
接下来还有一个List
,里面有两个Target
,分别是普通的煤矿和深层煤矿,
Target
中有一个RuleTest
和一个BlockState,RuleTest
就是上面提到的可以替换的方块
对于煤矿是替换STONE_ORE_REPLACEABLES
和DEEPSLATE_ORE_REPLACEABLES
这两个标签下的方块
1 2
| ConfiguredFeatures.register(featureRegisterable, ORE_COAL, Feature.ORE, new OreFeatureConfig(list6, 17)); ConfiguredFeatures.register(featureRegisterable, ORE_COAL_BURIED, Feature.ORE, new OreFeatureConfig(list6, 17, 0.5F));
|
那么最后就是构造特征的注册
OreFeatureConfig
中,第一个参数就是上面提到的List
,第二个参数是矿物矿脉
的规模,也就是一个矿脉中最多会有多少个矿石,
第三个参数为可选参数,指是矿物暴露在空气中时被丢弃的概率
,值越高越容易被丢弃,毕竟矿石不能悬浮在空中吧
构造特征基本上是这样,接下来我们来看放置特征
放置特征
放置特征在OrePlacedFeatures
类中
1 2
| public static final RegistryKey<PlacedFeature> ORE_COAL_UPPER = PlacedFeatures.of("ore_coal_upper"); public static final RegistryKey<PlacedFeature> ORE_COAL_LOWER = PlacedFeatures.of("ore_coal_lower");
|
还是以煤矿为例,它这里分了两个,根据Wiki
上的描述,煤矿有两种生成规则,我们从下面的代码来看看它们具体的生成规则
同样,也是在下面的bootstrap
方法中
1 2 3 4 5 6 7 8 9 10 11
| RegistryEntryLookup<ConfiguredFeature<?, ?>> registryEntryLookup = featureRegisterable.getRegistryLookup(RegistryKeys.CONFIGURED_FEATURE);
RegistryEntry<ConfiguredFeature<?, ?>> registryEntry13 = registryEntryLookup.getOrThrow(OreConfiguredFeatures.ORE_COAL); RegistryEntry<ConfiguredFeature<?, ?>> registryEntry14 = registryEntryLookup.getOrThrow(OreConfiguredFeatures.ORE_COAL_BURIED);
PlacedFeatures.register( featureRegisterable, ORE_COAL_UPPER, registryEntry13, modifiersWithCount(30, HeightRangePlacementModifier.uniform(YOffset.fixed(136), YOffset.getTop())) ); PlacedFeatures.register( featureRegisterable, ORE_COAL_LOWER, registryEntry14, modifiersWithCount(20, HeightRangePlacementModifier.trapezoid(YOffset.fixed(0), YOffset.fixed(192))) );
|
这里我们就能方向它的两个生成规则了,也就是这里的两个放置特征的注册
第一个放置特征,也就是ORE_COAL_UPPER
,它的生成规则是,在Y
坐标为136
到世界最高点之间均匀分布
,
HeightRangePlacementModifier.uniform
方法中的两个参数分别是Y坐标的最小值和最大值,其效果是均匀分布,
modifiersWithCount
方法的第一个参数在一个区块中,尝试生成的次数
第二个放置特征,也就是ORE_COAL_LOWER
,它的生成规则是,在Y
坐标为0
到192
之间梯形分布
,
每个区块尝试生成20
次,trapezoid方法表现的效果就是梯形分布,也就是两端分布少,中间分布多,
按照这里的参数,煤矿会在Y
坐标96
时,生成最多
当然,这也只是理论上(代码上)的生成规则,其实际分布还得取决于地形等因素,
看Wiki
上的每十万个方块中煤矿的数量图
显示,Y=45
时,煤矿是分布最多的
注册矿物世界生成
注册构造特征
那么接下来我们就来写自己的矿物
首先我们回到ModConfiguredFeatures
类中,写一些注册键
1 2 3
| public static final RegistryKey<ConfiguredFeature<?, ?>> ICE_ETHER_ORE_KEY = of("ice_ether_ore"); public static final RegistryKey<ConfiguredFeature<?, ?>> NETHER_ICE_ETHER_ORE_KEY = of("nether_ice_ether_ore"); public static final RegistryKey<ConfiguredFeature<?, ?>> END_ICE_ETHER_ORE_KEY = of("end_ice_ether_ore");
|
今天我们就直接在三个维度中生成我们的矿石,所以直接写了3个
然后是在bootstrap
方法中,先写一些替换规则
1 2 3 4
| RuleTest stoneReplace = new TagMatchRuleTest(BlockTags.STONE_ORE_REPLACEABLES); RuleTest deepSlateReplace = new TagMatchRuleTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES); RuleTest netherReplace = new TagMatchRuleTest(BlockTags.BASE_STONE_NETHER); RuleTest endReplace = new BlockMatchRuleTest(Blocks.END_STONE);
|
主要分主世界
的浅层
和深层
、下界
和末地
这4个部分,末地的话没有对应的方块标签,所以用BlockMatchRuleTest
传入末地石
就行
接下来是写List
1 2 3 4 5 6 7
| List<OreFeatureConfig.Target> overWorldTargets = List.of( OreFeatureConfig.createTarget(stoneReplace, ModBlocks.ICE_ETHER_ORE.getDefaultState()), OreFeatureConfig.createTarget(deepSlateReplace, ModBlocks.ICE_ETHER_ORE.getDefaultState())); List<OreFeatureConfig.Target> netherTargets = List.of( OreFeatureConfig.createTarget(netherReplace, ModBlocks.ICE_ETHER_ORE.getDefaultState())); List<OreFeatureConfig.Target> endTargets = List.of( OreFeatureConfig.createTarget(endReplace, ModBlocks.ICE_ETHER_ORE.getDefaultState()));
|
当然,因为我们之前只写了一个矿石方块,所以三个维度下都用同一个了,大家可以按照不同维度中基本岩石的材质来注册矿石方块
然后是注册构造特征
1 2 3 4 5 6
| ConfiguredFeatures.register(featureRegisterable, ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(overWorldTargets, 9)); ConfiguredFeatures.register(featureRegisterable, NETHER_ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(netherTargets, 12)); ConfiguredFeatures.register(featureRegisterable, END_ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(endTargets, 10));
|
这里我们用9
、12
、10
来代表不同维度下矿石矿脉的规模,也就是一个矿脉中最多会有多少个矿石
注册放置特征
接下来我们来到ModPlacedFeatures
类中,先搬两个方法过来
1 2 3 4 5 6
| private static List<PlacementModifier> modifiers(PlacementModifier countModifier, PlacementModifier heightModifier) { return List.of(countModifier, SquarePlacementModifier.of(), heightModifier, BiomePlacementModifier.of()); }
private static List<PlacementModifier> modifiersWithCount(int count, PlacementModifier heightModifier) { return modifiers(CountPlacementModifier.of(count), heightModifier);
|
这两个方法是OrePlacedFeatures
类中的,因为它是私有方法
,而我们也得用,所以就直接搬过来即可
随后来注册一些注册键
1 2 3
| public static final RegistryKey<PlacedFeature> ICE_ETHER_ORE_PLACED_KEY = ModPlacedFeatures.of("ice_ether_ore_placed"); public static final RegistryKey<PlacedFeature> NETHER_ICE_ETHER_ORE_PLACED_KEY = ModPlacedFeatures.of("nether_ice_ether_ore_placed"); public static final RegistryKey<PlacedFeature> END_ICE_ETHER_ORE_PLACED_KEY = ModPlacedFeatures.of("end_ice_ether_ore_placed");
|
然后在bootstrap
方法中注册放置特征
1 2 3 4 5 6 7 8 9
| PlacedFeatures.register(featureRegisterable, ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.ICE_ETHER_ORE_KEY), modifiersWithCount(10, HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80)))); PlacedFeatures.register(featureRegisterable, NETHER_ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.NETHER_ICE_ETHER_ORE_KEY), modifiersWithCount(12, HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80)))); PlacedFeatures.register(featureRegisterable, END_ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.END_ICE_ETHER_ORE_KEY), modifiersWithCount(8, HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80))));
|
这里我都选择了平均分布,每个维度下,单位区块中生成的次数大家可以按照自己的需求去编写
注册世界生成
最后我们创建一个ModOreGeneration
类,用于注册矿物的世界生成
1 2 3
| public class ModOreGeneration {
}
|
然后我们创建一个registerOres
方法,并在其中利用Fabric API
的方法添加矿物
1 2 3 4 5 6 7 8
| public static void registerOres(){ BiomeModifications.addFeature(BiomeSelectors.foundInOverworld(), GenerationStep.Feature.UNDERGROUND_ORES, ModPlacedFeatures.ICE_ETHER_ORE_PLACED_KEY); BiomeModifications.addFeature(BiomeSelectors.foundInTheNether(), GenerationStep.Feature.UNDERGROUND_ORES, ModPlacedFeatures.NETHER_ICE_ETHER_ORE_PLACED_KEY); BiomeModifications.addFeature(BiomeSelectors.foundInTheEnd(), GenerationStep.Feature.UNDERGROUND_ORES, ModPlacedFeatures.END_ICE_ETHER_ORE_PLACED_KEY); }
|
这里也是和之前的世界生成差不多,选择一个生物群系生成,当然,这里选择的是所有的生物群系
这个方法还要在ModWorldGeneration
类中调用
1
| ModOreGeneration.registerOres();
|
测试
最后我们就可以跑数据生成
,而后就可以进入游戏进行测试了