User Tools

Site Tools


zh_cn:tutorial:jigsaw

拼图

拼图非常适合用于地牢和村庄等高级结构,并且可以让您花费更多的时间在实际构建内容上,而不是将其与程序生成代码搞混。

可以找到带有完成代码的存储库 here.

创建一个结构特征

StructureFeature是高级的Feature:它跟踪其位置和范围,并具有从结构文件中生成自身的能力1)。 如果有帮助,您可以将其视为结构 +功能。 我们需要为拼图生成的结构创建一个。 首先,创建一个扩展StructureFeature <DefaultFeatureConfig>的类2) 特征命名约定为“结构名称” +“特征”; 一些典型的示例是EndCityFeatureOceanRuinFeatureVillageFeature

注意:虽然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 是您的结构的名称。 它用于几件事,包括:

  • 序列化,并从一大块反序列化的结构
  • 找到您的结构
  • 标记您的结构

原版约定是适当大写的标题。IglooVillageFortress都是有效的。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类:

  1. import com.mojang.datafixers.Dynamic;
  2. import net.minecraft.world.gen.chunk.ChunkGenerator;
  3. import net.minecraft.world.gen.feature.DefaultFeatureConfig;
  4. import net.minecraft.world.gen.feature.StructureFeature;
  5.  
  6. import java.util.Random;
  7. import java.util.function.Function;
  8.  
  9. public class ExampleFeature extends StructureFeature<DefaultFeatureConfig> {
  10.  
  11. public ExampleFeature(Function<Dynamic<?>, ? extends DefaultFeatureConfig> config) {
  12. super(config);
  13. }
  14.  
  15. @Override
  16. public boolean shouldStartAt(ChunkGenerator<?> chunkGenerator, Random random, int x, int z) {
  17. return true;
  18. }
  19.  
  20. @Override
  21. public StructureStartFactory getStructureStartFactory() {
  22. return ExampleStructureStart::new;
  23. }
  24.  
  25. @Override
  26. public String getName() {
  27. return "Example Feature";
  28. }
  29.  
  30. @Override
  31. public int getRadius() {
  32. return 2;
  33. }
  34. }

创建一个StructureStart类

StructureStart就像在世界上生成我们的结构的初始化阶段。 现在,使用构造函数创建一个简单的子类,该构造函数还将覆盖initialize:

  1. public class ExampleStructureStart extends StructureStart {
  2.  
  3. ExampleStructureStart(StructureFeature<?> feature, int x, int z, Biome biome, MutableIntBoundingBox box, int int_3, long seed) {
  4. super(feature, x, z, biome, box, int_3, seed);
  5. }
  6.  
  7. @Override
  8. public void initialize(ChunkGenerator<?> chunkGenerator, StructureManager structureManager, int x, int z, Biome biome) {
  9.  
  10. }
  11. }

要了解这里发生的情况,我们必须深入研究拼图 (https://minecraft.gamepedia.com/Jigsaw_Block) and 结构方块(https://minecraft.gamepedia.com/Structure_Block).

结构放块是一种将结构保存到.nbt文件以供将来使用的简单方法。 拼图是结构块的组成部分,将多个结构组装成一个结构。 与普通的拼图游戏类似,结构的每一块都在拼图块处连接,就像拼图块中的连接楔子一样。 我们假设您熟悉保存结构-如果不熟悉,请先阅读结构块页面,然后再进行任何操作。

拼图菜单包含3个:

  • 目标池
  • 附件类型
  • 转成

Blank Jigsaw

将其视为难题时,目标池就是您可以搜索的一组难题. 如果您共有10件,那么一个目标池可能有7件. 拼图是如何在此字段中指定的:“嗨,我希望B组的一块可以和我联系!” 就一个村庄而言,这可能是一条路,说:“给我房子!” 2个拼图的目标池不必匹配:请求者可以决定从中选择谁。 不是定义给定的拼图块是什么类型,而是应该定义另一种类型/.

附件类型可以看作是目标池中更具体的过滤器-拼图只能连接到具有相同附件类型的其他拼图。 这就像拼图块上的连接器类型。 用法更加具体.

最后,“turns into”字段只是拼图找到匹配项后所替换的内容。 如果拼图位于您的鹅卵石地板内,它可能会变成原石. 这是一个示例实现:给定的拼图将从 tutorial:my_pool 结构池中绘制,查找具有 tutorial:any 类型的所有拼图,并在完成后变成原石。

Example Finished Jigsaw

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: 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: Finalized Initial Squares

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. Colored Squares

We now have 6 saved .nbt files. These can be found in our world save folder under generated: Saved NBT files

For usage, we'll move these to resources/data/tutorial/structures, where “tutorial” is your modid: Moved NBT files

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:

  1. static {
  2. StructurePoolBasedGenerator.REGISTRY.add(
  3. new StructurePool(
  4. BASE_POOL,
  5. new Identifier("empty"),
  6. ImmutableList.of(
  7. Pair.of(new SinglePoolElement("tutorial:black_square"), 1),
  8. Pair.of(new SinglePoolElement("tutorial:white_square"), 1)
  9. ),
  10. StructurePool.Projection.RIGID
  11. )
  12. );
  13.  
  14. StructurePoolBasedGenerator.REGISTRY.add(
  15. new StructurePool(
  16. COLOR_POOL,
  17. new Identifier("empty"),
  18. ImmutableList.of(
  19. Pair.of(new SinglePoolElement("tutorial:lime_square"), 1),
  20. Pair.of(new SinglePoolElement("tutorial:magenta_square"), 1),
  21. Pair.of(new SinglePoolElement("tutorial:orange_square"), 1),
  22. Pair.of(new SinglePoolElement("tutorial:light_blue_square"), 1)
  23. ),
  24. StructurePool.Projection.RIGID
  25. )
  26. );
  27. }

在这里,我们要注册2个池(基础和颜色),然后将它们各自的子级添加到它们中。 StructurePool构造函数如下: *池的注册表名称,与竖锯顶部的目标池相同

  • @Draylar,如果您知道这是做什么的
  • 池元素列表
  • 池的投影类型

对于元素列表,我们添加池元素和整数的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是可选的,并且用于将其保存到块中。 如果世界在您的结构加载过程中途中止,则在重新打开世界后对其进行注册将使其继续存在。 如果未将其注册到结构特征中,并且发生了这种情况,则该结构将中途停止(大多数情况下只会发生在较大的,多块宽的结构中)。

  1. public static final StructureFeature<DefaultFeatureConfig> EXAMPLE_FEATURE = Registry.register(
  2. Registry.FEATURE,
  3. new Identifier("tutorial", "example_feature"),
  4. new ExampleFeature(DefaultFeatureConfig::deserialize)
  5. );
  6.  
  7. public static final StructureFeature<DefaultFeatureConfig> EXAMPLE_STRUCTURE_FEATURE = Registry.register(
  8. Registry.STRUCTURE_FEATURE,
  9. new Identifier("tutorial", "example_structure_feature"),
  10. EXAMPLE_FEATURE
  11. );
  12.  
  13. public static final StructurePieceType EXAMPLE_PIECE = Registry.register(
  14. Registry.STRUCTURE_PIECE,
  15. new Identifier("tutorial", "example_piece"),
  16. ExamplePiece::new
  17. );

生成我们的结构

最后,我们必须生成我们的结构。 将其添加到每个生物群系的一个基本示例是:

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());
});

完成!

如您所见,我们在中心有一个白色正方形,框在边缘之外。 请注意,此屏幕截图中的半径已增加到14,而不是本教程中使用的7。 搞定

1)
虽然您可以从.nbt中生成您的StructureFeature文件,大多数普通的StructureFeatures都简单地覆盖了给定的Piece类中的generate方法
2)
AbstractTempleFeature是另一种选择。它会自动将类似于现有庙宇的结构隔开-村庄也使用此逻辑。
3)
请勿使用TranslatableText:意图很强的时候,事情会崩溃的。
4)
com.mojang.datafixers.util
zh_cn/tutorial/jigsaw.txt · Last modified: 2020/02/24 02:59 by lightcolour