本篇教程的视频
本篇教程的源代码
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_REPLACEABLES和DEEPSLATE_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坐标为0到192之间梯形分布,
每个区块尝试生成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));
|
这里我们用9、12、10来代表不同维度下矿石矿脉的规模,也就是一个矿脉中最多会有多少个矿石
注册放置特征
接下来我们来到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));
|
这里也是和之前的差不多,不同维度的矿石选择的群系标签也不一样
测试
最后我们就可以跑数据生成,而后就可以进入游戏进行测试了