本篇教程的视频:

本篇教程源代码

GitHub地址:TutorialMod-OreGen-1.21

介绍

前面我们写方块的时候,我们已经添加了一些矿物方块,那这篇教程我们将让矿物也生成在世界中,这样玩家才能在世界中找到矿物

这里和前面的两篇世界生成的教程一样,也是要为矿物编写构造特征放置特征

同样的,这里我们就不讲解源代码了,可以自行研究

创建ModOrePlacements类

我们先创建一个类,这个类里面存放一些方法,这里的方法来自OrePlacedFeatures这个类也是关于矿物的放置特征的

我们要用到里面的一些方法,但那些方法它的private的,所以我们得把他们搬过来,改成public

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ModOrePlacements {
public static List<PlacementModifier> modifiers(PlacementModifier countModifier, PlacementModifier heightModifier) {
return List.of(countModifier, SquarePlacementModifier.of(), heightModifier, BiomePlacementModifier.of());
}

public static List<PlacementModifier> modifiersWithCount(int count, PlacementModifier heightModifier) {
return modifiers(CountPlacementModifier.of(count), heightModifier);
}

public static List<PlacementModifier> modifiersWithRarity(int chance, PlacementModifier heightModifier) {
return modifiers(RarityFilterPlacementModifier.of(chance), heightModifier);
}
}

不过在这篇教程中,我们没有用到其中的modifiersWithRarity方法,这是按照稀有度生成的方法

值得一提的是,原版很少使用这个方法,上层安山岩、花岗岩、闪长岩大型钻石矿这四个用到了这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PlacedFeatures.register(
featureRegisterable, ORE_GRANITE_UPPER, registryEntry9, modifiersWithRarity(6, HeightRangePlacementModifier.uniform(YOffset.fixed(64), YOffset.fixed(128)))
);

PlacedFeatures.register(
featureRegisterable, ORE_DIORITE_UPPER, registryEntry10, modifiersWithRarity(6, HeightRangePlacementModifier.uniform(YOffset.fixed(64), YOffset.fixed(128)))
);

PlacedFeatures.register(
featureRegisterable,
ORE_ANDESITE_UPPER,
registryEntry11,
modifiersWithRarity(6, HeightRangePlacementModifier.uniform(YOffset.fixed(64), YOffset.fixed(128)))
);

PlacedFeatures.register(
featureRegisterable,
ORE_DIAMOND_LARGE,
registryEntry22,
modifiersWithRarity(9, HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-80), YOffset.aboveBottom(80)))
);

这是OrePlacedFeatures中,在bootstrap中注册的四个矿物的放置特征,这里用到了modifiersWithRarity方法

当然,我对于世界生成了解的不多,感兴趣的同学可以自行研究

矿物的构造特征

接下来我们来写矿物的构造特征

首先是注册键

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");

虽然说我们前面只写了一个矿石,但它可以生成在三个维度中,所以我们这里就写了三个注册键,分别代表ICE_ETHER_ORE主世界下界末地中的生成

然后我们来写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);

啥意思呢?规则判断是用于结构(地物)的生成,用于检测区块状态是否符合生成条件

说白了,生成矿石必然要替代世界中已有的一些方块,这个时候就要判断矿石的生成位置是否是那些可以替代的方块

那么这里除了末地直接用末地石判断之外,其他维度的都有对应的方块标签(因为原版的末地并没有矿石,也就没有对应的标签)

接下来写构造特征的目标

1
2
3
4
5
6
7
8
9
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()));

通过这里的createTarget方法,我们可以看到,这个方法需要两个参数,一个是规则判断,一个是方块状态,将我们的替换方块和我们自己的矿物方块联系起来

最后便是真正的注册

1
2
3
ConfiguredFeatures.register(featureRegisterable, ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(overWorldTargets, 8));
ConfiguredFeatures.register(featureRegisterable, NETHER_ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(netherTargets, 8));
ConfiguredFeatures.register(featureRegisterable, END_ICE_ETHER_ORE_KEY, Feature.ORE, new OreFeatureConfig(endTargets, 8));

这里的三条语句对应三个维度的矿物构造特征,OreFeatureConfig的参数分别是目标矿脉大小

矿物的放置特征

解决了构造特征之后,我们来写放置特征

那么常规的,先是注册键

1
2
3
public static final RegistryKey<PlacedFeature> ICE_ETHER_ORE_PLACED_KEY = of("ice_ether_ore_placed");
public static final RegistryKey<PlacedFeature> NETHER_ICE_ETHER_ORE_PLACED_KEY = of("nether_ice_ether_ore_placed");
public static final RegistryKey<PlacedFeature> END_ICE_ETHER_ORE_PLACED_KEY = of("end_ice_ether_ore_placed");

然后就是bootstrap中的注册

1
2
3
4
5
6
7
8
9
10
11
register(featureRegisterable, ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.ICE_ETHER_ORE_KEY),
ModOrePlacements.modifiersWithCount(12,
HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80))));

register(featureRegisterable, NETHER_ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.NETHER_ICE_ETHER_ORE_KEY),
ModOrePlacements.modifiersWithCount(12,
HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80))));

register(featureRegisterable, END_ICE_ETHER_ORE_PLACED_KEY, registryEntryLookup.getOrThrow(ModConfiguredFeatures.END_ICE_ETHER_ORE_KEY),
ModOrePlacements.modifiersWithCount(12,
HeightRangePlacementModifier.uniform(YOffset.fixed(-80), YOffset.fixed(80))));

这里我们调用了ModOrePlacements中的modifiersWithCount方法,这个方法是用于生成矿物的修饰符,参数分别是每个区块的生成数量高度范围

高度范围使用的是uniform方法,这个方法是一个均匀分布的高度范围,参数是最低高度最高高度

当然还有一个trapezoid方法,这个方法是一个梯形分布的高度范围,参数同样是最低高度最高高度,但它的分布法是两端低,中间高

以这里-8080为例,如果采用梯形分布,那么在高度0的位置,矿物的生成概率是最高的,而在-8080的位置,矿物的生成概率是最低的

我们可以看一下Wiki上的那个图,那个图里的三角形就是采用梯形分布的高度范围

矿物的生成器

之后我们还得写一个生成器,这个生成器是用来生成矿物的

我们创建一个ModOreGeneration类,然后在里面写一个generateOres方法

1
2
3
4
5
6
7
8
9
10
11
public class ModOreGeneration {

public static void generateOres() {
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);
}
}

这里我们区别于前面写的树和花,不是选定某个特定的生物群系,而是选定了主世界下界末地这三个维度,这样我们的矿石理论上可以生成在任意生物群系中

而后,不要忘记调用generateOres方法

1
ModOreGeneration.generateOres();

那么在此之后,我们就可以跑数据生成,然后看看我们的矿物是否生成在世界中了(建议新建存档哦)