.nbt
中生成您的StructureFeature
文件,大多数普通的StructureFeatures都简单地覆盖了给定的Piece类中的generate
方法拼图非常适合用于地牢和村庄等高级结构,并且可以让您花费更多的时间在实际构建内容上,而不是将其与程序生成代码搞混。
可以找到带有完成代码的存储库 here.
StructureFeature
是高级的Feature
:它跟踪其位置和范围,并具有从结构文件中生成自身的能力1)。 如果有帮助,您可以将其视为结构
+功能
。 我们需要为拼图生成的结构创建一个。 首先,创建一个扩展StructureFeature <DefaultFeatureConfig>
的类2) 特征命名约定为“结构名称” +“特征”; 一些典型的示例是EndCityFeature
,OceanRuinFeature
和VillageFeature
。
注意:虽然Feature是世界上生成的事物的专有名称,但我们将添加的内容称为Structure。 这是为了区分StructureFeature和标准Feature。
我们将按原样保留构造函数。Function <Dynamic <?»
参数是结构配置-如果您没有任何计划将其弄乱,则可以简单地将DefaultFeatureConfig :: deserialize
传递给super:
public ExampleFeature(Function<Dynamic<?>, ? extends DefaultFeatureConfig> config) { super(config); }
shouldStartAt回答问题“我应该在给定的块中开始生成吗?” VanillaTempleFeature
是vanilla提供的一个类,它为这个问题提供了答案:它可以确保每个结构与其他相同类型的结构隔开。 标准乡村功能使用相同的逻辑,但不是该类别的子级。 通过返回true,每个块都将具有您的功能。 这对于测试非常有用,因此我们现在将使用该路由。
@Override public boolean shouldStartAt(ChunkGenerator<?> chunkGenerator, Random random, int x, int z) { return true; }
getName 是您的结构的名称。 它用于几件事,包括:
原版约定是适当大写的标题。Igloo
,Village
和Fortress
都是有效的。3)
@Override public String getName() { return "Example Feature"; }
getRadius的确切用法仍在讨论中,但是(作为作者)我的猜测是它与块内的翻译有关。 半径为4的结构可以在块边界内移动,而半径为8的结构将始终居中。 这可能是错误的。 目前,半径应为结构的一半; 如果您的结构将占用多个块(或全部填充),则返回8。
@Override public int getRadius() { return 2; }
最后,getStructureStartFactory是您的StructureFeature的生成部分。 您必须返回一个工厂方法来创建新的StructureStarts -我们可以简单地通过方法引用构造函数。 我们的实现将如下所示,ExampleStructureStart
是本教程的下一步:
@Override public StructureStartFactory getStructureStartFactory() { return ExampleStructureStart::new; }
我们完成的ExampleFeature
类:
import com.mojang.datafixers.Dynamic; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.feature.DefaultFeatureConfig; import net.minecraft.world.gen.feature.StructureFeature; import java.util.Random; import java.util.function.Function; public class ExampleFeature extends StructureFeature<DefaultFeatureConfig> { public ExampleFeature(Function<Dynamic<?>, ? extends DefaultFeatureConfig> config) { super(config); } @Override return true; } @Override public StructureStartFactory getStructureStartFactory() { return ExampleStructureStart::new; } @Override return "Example Feature"; } @Override public int getRadius() { return 2; } }
StructureStart
就像在世界上生成我们的结构的初始化阶段。 现在,使用构造函数创建一个简单的子类,该构造函数还将覆盖initialize:
public class ExampleStructureStart extends StructureStart { ExampleStructureStart(StructureFeature<?> feature, int x, int z, Biome biome, MutableIntBoundingBox box, int int_3, long seed) { super(feature, x, z, biome, box, int_3, seed); } @Override public void initialize(ChunkGenerator<?> chunkGenerator, StructureManager structureManager, int x, int z, Biome biome) { } }
要了解这里发生的情况,我们必须深入研究拼图 (https://minecraft.gamepedia.com/Jigsaw_Block) and 结构方块(https://minecraft.gamepedia.com/Structure_Block).
结构放块是一种将结构保存到.nbt文件以供将来使用的简单方法。 拼图是结构块的组成部分,将多个结构组装成一个结构。 与普通的拼图游戏类似,结构的每一块都在拼图块处连接,就像拼图块中的连接楔子一样。 我们假设您熟悉保存结构-如果不熟悉,请先阅读结构块页面,然后再进行任何操作。
拼图菜单包含3个:
将其视为难题时,目标池就是您可以搜索的一组难题. 如果您共有10件,那么一个目标池可能有7件. 拼图是如何在此字段中指定的:“嗨,我希望B组的一块可以和我联系!” 就一个村庄而言,这可能是一条路,说:“给我房子!” 2个拼图的目标池不必匹配:请求者可以决定从中选择谁。 不是定义给定的拼图块是什么类型,而是应该定义另一种类型/.
附件类型可以看作是目标池中更具体的过滤器-拼图只能连接到具有相同附件类型的其他拼图。 这就像拼图块上的连接器类型。 用法更加具体.
最后,“turns into”字段只是拼图找到匹配项后所替换的内容。 如果拼图位于您的鹅卵石地板内,它可能会变成原石. 这是一个示例实现:给定的拼图将从 tutorial:my_pool 结构池中绘制,查找具有 tutorial:any 类型的所有拼图,并在完成后变成原石。
Our finalized structure will consist of multiple colored squares connecting to each other. It will have a white or a black square in the center, and orange, magenta, light blue, and lime squares branching off on the sides randomly. Here is the setup of our 2 initial squares:
This jigsaw will ask for any other jigsaw that: * is in the tutorial:color_pool target pool * has an attachment type of tutorial:square_edge It then turns into white concrete to match the rest of the platform.
For demo purposes, we've made 2 starting platforms: one is white, and one is black. The only difference is what they turn into. We'll save these as structure files using structure blocks:
For our randomized edge platforms, we've made 4 extra squares of different colors. Again, despite being used for a different purpose, the jigsaw construction is the same aside from the “turns into” field.
We now have 6 saved .nbt
files. These can be found in our world save folder under generated
:
For usage, we'll move these to resources/data/tutorial/structures
, where “tutorial” is your modid:
The setup is complete! We now have 6 total squares. Let's briefly recap the goal: * have a white or black square selected as the center for our structure * have a pool of the 4 other colors * branch off from the center square with our 4 extra colors
Let's head back to our ExampleStructureStart
class. First, we'll need 2 Identifiers to label our 2 pools (black&white, 4 colors):
private static final Identifier BASE_POOL = new Identifier("tutorial:base_pool"); private static final Identifier COLOR_POOL = new Identifier("tutorial:color_pool");
Remember: every jigsaw ends up searching through the color pool, but we still have a base pool! This is to keep our black & white squares out of the outside generated squares. It's also going to be our origin pool, where we randomly select 1 structure from to begin our generation.
In a static block at the bottom of our class, we're going to register our structure pools using StructurePoolBasedGenerator.REGISTRY
:
static { StructurePoolBasedGenerator.REGISTRY.add( new StructurePool( BASE_POOL, new Identifier("empty"), ImmutableList.of( Pair.of(new SinglePoolElement("tutorial:black_square"), 1), Pair.of(new SinglePoolElement("tutorial:white_square"), 1) ), StructurePool.Projection.RIGID ) ); StructurePoolBasedGenerator.REGISTRY.add( new StructurePool( COLOR_POOL, new Identifier("empty"), ImmutableList.of( Pair.of(new SinglePoolElement("tutorial:lime_square"), 1), Pair.of(new SinglePoolElement("tutorial:magenta_square"), 1), Pair.of(new SinglePoolElement("tutorial:orange_square"), 1), Pair.of(new SinglePoolElement("tutorial:light_blue_square"), 1) ), StructurePool.Projection.RIGID ) ); }
在这里,我们要注册2个池(基础和颜色),然后将它们各自的子级添加到它们中。 StructurePool构造函数如下: *池的注册表名称,与竖锯顶部的目标池相同
对于元素列表,我们添加池元素和整数的Pairs4)。 传递到元素的字符串是结构在数据目录中的位置,而int是元素在整个目标池中的权重。 对每个元素使用1可以确保每个元素被均匀地拾取.
投影是如何将池放置在世界上的。 刚性表示将直接按原样放置,而地形匹配则表示将其弯曲以位于地形顶部。 后者可能适合随地形变化的麦田结构,而前者则适合具有坚固地板的房屋。
现在,我们要做的就是在initialize
方法中添加起始片段:
@Override public void initialize(ChunkGenerator<?> chunkGenerator, StructureManager structureManager, int x, int z, Biome biome) { StructurePoolBasedGenerator.addPieces(BASE_POOL, 7, ExamplePiece::new, chunkGenerator, structureManager, new BlockPos(x * 16, 150, z * 16), children, random); setBoundingBoxFromChildren(); }
Identifier是可供选择的起始池,int是整个结构的大小(其中7为“7 squares out”),第3个参数是我们将在第二秒注册的零件的工厂.
这部分非常简单。 一块代表整个结构中的一个部分或元素。 您需要创建一个基本的计件类,稍后我们将进行注册:
public class ExamplePiece extends PoolStructurePiece { ExamplePiece(StructureManager structureManager_1, StructurePoolElement structurePoolElement_1, BlockPos blockPos_1, int int_1, BlockRotation blockRotation_1, MutableIntBoundingBox mutableIntBoundingBox_1) { super(ExampleMod.EXAMPLE_PIECE, structureManager_1, structurePoolElement_1, blockPos_1, int_1, blockRotation_1, mutableIntBoundingBox_1); } public ExamplePiece(StructureManager manager, CompoundTag tag) { super(manager, tag, ExampleMod.EXAMPLE_PIECE); } }
其中ExampleMod.EXAMPLE_PIECE
是对我们注册件的引用。
我们需要将结构既注册为特征和还是结构特征,并注册我们的作品。 将结构注册为StructureFeature是可选的,并且用于将其保存到块中。 如果世界在您的结构加载过程中途中止,则在重新打开世界后对其进行注册将使其继续存在。 如果未将其注册到结构特征中,并且发生了这种情况,则该结构将中途停止(大多数情况下只会发生在较大的,多块宽的结构中)。
Registry.FEATURE, new Identifier("tutorial", "example_feature"), new ExampleFeature(DefaultFeatureConfig::deserialize) ); public static final StructureFeature<DefaultFeatureConfig> EXAMPLE_STRUCTURE_FEATURE = Registry.register( Registry.STRUCTURE_FEATURE, new Identifier("tutorial", "example_structure_feature"), EXAMPLE_FEATURE ); Registry.STRUCTURE_PIECE, new Identifier("tutorial", "example_piece"), ExamplePiece::new );
最后,我们必须生成我们的结构。 将其添加到每个生物群系的一个基本示例是:
Registry.BIOME.forEach(biome -> { biome.addFeature(GenerationStep.Feature.RAW_GENERATION, Biome.configureFeature(EXAMPLE_FEATURE, new DefaultFeatureConfig(), Decorator.NOPE, DecoratorConfig.DEFAULT)); biome.addStructureFeature(EXAMPLE_FEATURE, new DefaultFeatureConfig()); });