本篇教程的视频

(待发布)

本篇教程的源代码

(待发布)

本篇教程目标

  • 理解原版矿物的构造特征和放置特征
  • 学会为矿物配置构造特征和放置特征

查看源代码

矿物,或者说矿脉,它也是属于地物的,所以它也有构造特征和放置特征

构造特征

矿物的构造特征我们可以在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_REPLACEABLESDEEPSLATE_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坐标为0192之间梯形分布
每个区块尝试生成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));

这里我们用91210来代表不同维度下矿石矿脉的规模,也就是一个矿脉中最多会有多少个矿石

注册放置特征

接下来我们来到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();

测试

最后我们就可以跑数据生成,而后就可以进入游戏进行测试了