本篇教程的视频

本篇教程的源代码

GitHub地址:

查看源代码

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

需要特别注意的是:原版的世界生成逻辑都是按照区块进行的生成

矿石的生成也都是按照一个个区块来生成,而如果大家玩过GTNH(或者就纯粹的GT),它矿石的生成逻辑是被修改过的,即它是按照3*3的区块去生成,而不是按照单个区块来生成

这里需要注意这一点

构造特征

矿物的构造特征我们可以在OreFeatures类中找到

1
2
public static final ResourceKey<ConfiguredFeature<?, ?>> ORE_COAL = FeatureUtils.createKey("ore_coal");
public static final ResourceKey<ConfiguredFeature<?, ?>> ORE_COAL_BURIED = FeatureUtils.createKey("ore_coal_buried");

这里我们以煤矿为例,首先是注册键,煤矿分了两个,一个是普通的,一个是埋藏的,后者大概是埋在岩层中的,而不是直接裸露在外的

再接下来的bootstrap方法中,我们可以看到一堆RuleTest

1
2
3
4
5
RuleTest ruletest = new TagMatchTest(BlockTags.BASE_STONE_OVERWORLD);
RuleTest ruletest1 = new TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES);
RuleTest ruletest2 = new TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES);
RuleTest ruletest3 = new BlockMatchTest(Blocks.NETHERRACK);
RuleTest ruletest4 = new TagMatchTest(BlockTags.BASE_STONE_NETHER);

这些其实就是矿物生成规则中可以用来替换的方块,我的理解是,在世界生成的过程中,
先是基本岩石先生成,比如Stone,然后矿物根据规则判断一些方块是否可以被替换,将那些方块替换为矿物

1
2
3
List<OreConfiguration.TargetBlockState> list5 = List.of(
OreConfiguration.target(ruletest1, Blocks.COAL_ORE.defaultBlockState()),
OreConfiguration.target(ruletest2, Blocks.DEEPSLATE_COAL_ORE.defaultBlockState()));

接下来还有一个List,里面有两个Target,分别是普通的煤矿和深层煤矿,
Target中有一个RuleTest和一个BlockState,RuleTest就是上面提到的可以替换的方块

对于煤矿是替换STONE_ORE_REPLACEABLESDEEPSLATE_ORE_REPLACEABLES这两个标签下的方块

1
2
FeatureUtils.register(pContext, ORE_COAL, Feature.ORE, new OreConfiguration(list5, 17));
FeatureUtils.register(pContext, ORE_COAL_BURIED, Feature.ORE, new OreConfiguration(list5, 17, 0.5F));

那么最后就是构造特征的注册

OreConfiguration中,第一个参数就是上面提到的List,第二个参数是矿物矿脉的规模,也就是一个矿脉中最多会有多少个矿石,
第三个参数为可选参数,指是矿物暴露在空气中时被丢弃的概率,值越高越容易被丢弃,毕竟矿石不能悬浮在空中吧

构造特征基本上是这样,接下来我们来看放置特征

放置特征

放置特征在OrePlacements类中

1
2
public static final ResourceKey<PlacedFeature> ORE_COAL_UPPER = PlacementUtils.createKey("ore_coal_upper");
public static final ResourceKey<PlacedFeature> ORE_COAL_LOWER = PlacementUtils.createKey("ore_coal_lower");

还是以煤矿为例,它这里分了两个,根据Wiki上的描述,煤矿有两种生成规则,我们从下面的代码来看看它们具体的生成规则

同样,也是在下面的bootstrap方法中

1
2
3
4
5
6
7
8
9
HolderGetter<ConfiguredFeature<?, ?>> holdergetter = pContext.lookup(Registries.CONFIGURED_FEATURE);

Holder<ConfiguredFeature<?, ?>> holder12 = holdergetter.getOrThrow(OreFeatures.ORE_COAL);
Holder<ConfiguredFeature<?, ?>> holder13 = holdergetter.getOrThrow(OreFeatures.ORE_COAL_BURIED);

PlacementUtils.register(pContext, ORE_COAL_UPPER, holder12,
commonOrePlacement(30, HeightRangePlacement.uniform(VerticalAnchor.absolute(136), VerticalAnchor.top())));
PlacementUtils.register(pContext, ORE_COAL_LOWER, holder13,
commonOrePlacement(20, HeightRangePlacement.triangle(VerticalAnchor.absolute(0), VerticalAnchor.absolute(192))));

这里我们就能方向它的两个生成规则了,也就是这里的两个放置特征的注册

第一个放置特征,也就是ORE_COAL_UPPER,它的生成规则是,在Y坐标为136到世界最高点之间均匀分布
HeightRangePlacement.uniform方法中的两个参数分别是Y坐标的最小值和最大值,其效果是均匀分布,
commonOrePlacement方法的第一个参数在一个区块中,尝试生成的次数

第二个放置特征,也就是ORE_COAL_LOWER,它的生成规则是,在Y坐标为0192之间梯形分布
每个区块尝试生成20次,triangle方法表现的效果就是梯形分布,也就是两端分布少,中间分布多,
按照这里的参数,煤矿会在Y坐标96时,生成最多

当然,这也只是理论上(代码上)的生成规则,其实际分布还得取决于地形等因素,
Wiki上的每十万个方块中煤矿的数量图显示,Y=45时,煤矿是分布最多的

注册矿物世界生成

注册构造特征

那么接下来我们就来写自己的矿物

首先我们回到ModConfiguredFeatures类中,写一些注册键

1
2
3
public static final ResourceKey<ConfiguredFeature<?, ?>> ICE_ETHER_ORE_KEY = createKey("ice_ether_ore");
public static final ResourceKey<ConfiguredFeature<?, ?>> NETHER_ICE_ETHER_ORE_KEY = createKey("nether_ice_ether_ore");
public static final ResourceKey<ConfiguredFeature<?, ?>> END_ICE_ETHER_ORE_KEY = createKey("end_ice_ether_ore");

今天我们就直接在三个维度中生成我们的矿石,所以直接写了3个

然后是在bootstrap方法中,先写一些替换规则

1
2
3
4
RuleTest stoneReplace = new TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES);
RuleTest deepSlateReplace = new TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES);
RuleTest netherReplace = new TagMatchTest(BlockTags.BASE_STONE_NETHER);
RuleTest endReplace = new BlockMatchTest(Blocks.END_STONE);

主要分主世界浅层深层下界末地这4个部分,末地的话没有对应的方块标签,所以用BlockMatchTest传入末地石就行

接下来是写List

1
2
3
4
5
6
7
List<OreConfiguration.TargetBlockState> overWorldTargets = List.of(
OreConfiguration.target(stoneReplace, ModBlocks.ICE_ETHER_ORE.get().defaultBlockState()),
OreConfiguration.target(deepSlateReplace, ModBlocks.ICE_ETHER_ORE.get().defaultBlockState()));
List<OreConfiguration.TargetBlockState> netherTargets = List.of(
OreConfiguration.target(netherReplace, ModBlocks.ICE_ETHER_ORE.get().defaultBlockState()));
List<OreConfiguration.TargetBlockState> endTargets = List.of(
OreConfiguration.target(endReplace, ModBlocks.ICE_ETHER_ORE.get().defaultBlockState()));

当然,因为我们之前只写了一个矿石方块,所以三个维度下都用同一个了,大家可以按照不同维度中基本岩石的材质来注册矿石方块

然后是注册构造特征

1
2
3
4
5
6
FeatureUtils.register(context, ICE_ETHER_ORE_KEY, Feature.ORE,
new OreConfiguration(overWorldTargets, 9));
FeatureUtils.register(context, NETHER_ICE_ETHER_ORE_KEY, Feature.ORE,
new OreConfiguration(netherTargets, 12));
FeatureUtils.register(context, END_ICE_ETHER_ORE_KEY, Feature.ORE,
new OreConfiguration(endTargets, 10));

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

注册放置特征

接下来我们来到ModPlacedFeatures类中,先搬两个方法过来

1
2
3
4
5
6
7
private static List<PlacementModifier> orePlacement(PlacementModifier pCountPlacement, PlacementModifier pHeightRange) {
return List.of(pCountPlacement, InSquarePlacement.spread(), pHeightRange, BiomeFilter.biome());
}

private static List<PlacementModifier> commonOrePlacement(int pCount, PlacementModifier pHeightRange) {
return orePlacement(CountPlacement.of(pCount), pHeightRange);
}

这两个方法是OrePlacements类中的,因为它是私有方法,而我们也得用,所以就直接搬过来即可

随后来注册一些注册键

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

然后在bootstrap方法中注册放置特征

1
2
3
4
5
6
7
8
9
PlacementUtils.register(context, ICE_ETHER_ORE_PLACED_KEY, holdergetter.getOrThrow(ModConfiguredFeatures.ICE_ETHER_ORE_KEY),
commonOrePlacement(10,
HeightRangePlacement.uniform(VerticalAnchor.absolute(-64), VerticalAnchor.absolute(128))));
PlacementUtils.register(context, NETHER_ICE_ETHER_ORE_PLACED_KEY, holdergetter.getOrThrow(ModConfiguredFeatures.NETHER_ICE_ETHER_ORE_KEY),
commonOrePlacement(12,
HeightRangePlacement.uniform(VerticalAnchor.absolute(20), VerticalAnchor.absolute(80))));
PlacementUtils.register(context, END_ICE_ETHER_ORE_PLACED_KEY, holdergetter.getOrThrow(ModConfiguredFeatures.END_ICE_ETHER_ORE_KEY),
commonOrePlacement(8,
HeightRangePlacement.uniform(VerticalAnchor.absolute(0), VerticalAnchor.absolute(80))));

这里我都选择了平均分布,每个维度下,单位区块中生成的次数大家可以按照自己的需求去编写

生物群系修改

最后我们回到ModBiomeModifiers类中,先添加一些注册键

1
2
3
public static final ResourceKey<BiomeModifier> ADD_ICE_ETHER_ORE = registerKey("add_ice_ether_ore");
public static final ResourceKey<BiomeModifier> ADD_NETHER_ICE_ETHER_ORE = registerKey("add_nether_ice_ether_ore");
public static final ResourceKey<BiomeModifier> ADD__END_ICE_ETHER_ORE = registerKey("add_end_ice_ether_ore");

然后在其中的bootstrap方法,添加下面的语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
context.register(ADD_ICE_ETHER_ORE, new ForgeBiomeModifiers.AddFeaturesBiomeModifier(
biomes.getOrThrow(BiomeTags.IS_OVERWORLD),
HolderSet.direct(placedFeatures.getOrThrow(ModPlacedFeatures.ICE_ETHER_ORE_PLACED_KEY)),
GenerationStep.Decoration.UNDERGROUND_ORES));

context.register(ADD_NETHER_ICE_ETHER_ORE, new ForgeBiomeModifiers.AddFeaturesBiomeModifier(
biomes.getOrThrow(BiomeTags.IS_NETHER),
HolderSet.direct(placedFeatures.getOrThrow(ModPlacedFeatures.NETHER_ICE_ETHER_ORE_PLACED_KEY)),
GenerationStep.Decoration.UNDERGROUND_ORES));

context.register(ADD__END_ICE_ETHER_ORE, new ForgeBiomeModifiers.AddFeaturesBiomeModifier(
biomes.getOrThrow(BiomeTags.IS_END),
HolderSet.direct(placedFeatures.getOrThrow(ModPlacedFeatures.END_ICE_ETHER_ORE_PLACED_KEY)),
GenerationStep.Decoration.UNDERGROUND_ORES));

这里也是和之前的差不多,不同维度的矿石选择的群系标签也不一样

测试

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