zh_cn:tutorial:blocks

添加方块

将方块添加到你的模组过程与添加物品类似。你可以创建 Block 或自定义类的实例,然后将其注册到 Registries.BLOCK(1.19.3 以上版本)或 Registry.BLOCK(1.19.2 以下)注册表。你还需要提供纹理和方块状态或模型文件以提供方块外观。如需了解方块模型格式的更多信息,请参考Minecraft Wiki 模型页面

创建方块

:!: 使用你使用的是 1.21.2 或者之后的版本,请直接看 在 1.21.2+ 中注册方块

首先创建 Block 的实例。该实例可以存储在任何地方,但是我们会在 ModInitializer 的顶部开始。Block 构造器函数需要一个 AbstractBlock.Settings 实例,也就是用于配置方块属性的构造器。Fabric 提供一个 FabricBlockSettings 构造器类以及更多可用的选项。

ExampleMod.java
  1. public class ExampleMod implements ModInitializer {
  2.  
  3. /* 声明和初始化我们的自定义方块实例。
  4.   我们将方块材质(material)设置为 METAL(金属)。
  5.  
  6.   `strength` 会将方块的硬度和抗性设为同一个值。
  7.   硬度决定了方块需要多久挖掘,抗性决定了方块抵御爆破伤害(如爆炸)的能力。
  8.   石头的硬度为 1.5f,抗性为 6.0f,黑曜石的硬度为 50.0f,抗性为 1200.0f。
  9.  
  10.   可以在`Blocks`类中查找所有原版方块,你可以以此作为参考。
  11.   */
  12.  
  13. // 对于 1.20 以下版本:
  14. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.of(Material.METAL).strength(4.0f));
  15. // 对于 1.20.5 以下版本:
  16. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.create().strength(4.0f));
  17. // 对于自 1.20.5 之后、1.21.2 以前的版本:
  18. public static final Block EXAMPLE_BLOCK = new Block(Block.Settings.create().strength(4.0f));
  19.  
  20. @Override
  21. public void onInitialize() {
  22.  
  23. }
  24. }

注册方块

方块应该注册在 Registries.BLOCK 注册表中。和 物品 类似,调用 Registry.register 并传递适当的参数。你可以直接在 onInitialize 方法中注册方块,也可以在静态部分创建方块实例时将其注册,因为 register 方法会返回这个方块实例自身。

如果你使用的是 1.19.2 以下的版本,请将 Registries.BLOCK 替换为 Registry.BLOCK

ExampleMod.java
  1. public class ExampleMod implements ModInitializer {
  2.  
  3. // 对于 1.20 以下版本:
  4. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.of(Material.METAL).strength(4.0f));
  5. // 对于 1.20.5 以下版本:
  6. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.create().strength(4.0f));
  7. // 对于自 1.20.5 之后、1.21.2 以前的版本:
  8. public static final Block EXAMPLE_BLOCK = new Block(Block.Settings.create().strength(4.0f));
  9.  
  10. @Override
  11. public void onInitialize() {
  12. // 对于 1.21 以下版本:
  13. // Registry.register(Registries.BLOCK, new Identifier("tutorial", "example_block"), EXAMPLE_BLOCK);
  14. // 对于 1.21 之后的版本:
  15. Registry.register(Registries.BLOCK, Identifier.of("tutorial", "example_block"), EXAMPLE_BLOCK);
  16. }
  17. }

你的自定义方块不能作为物品存入背包,但可以通过使用 /setblock <位置> tutorial:example_block 在游戏中看到。

为方块注册物品

在大多数情况下,您希望能够拿着物品放置方块。为此,您需要在物品注册表中注册一个相应的物品。您可以通过在 Registries.ITEM 下注册 BlockItem 的实例来执行此操作。该物品的注册表名称通常应与该方块的注册表名称相同。

ExampleMod.java
  1. public class ExampleMod implements ModInitializer {
  2.  
  3. // 对于 1.20 以下版本:
  4. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.of(Material.METAL).strength(4.0f));
  5. // 对于 1.20.5 以下版本:
  6. // public static final Block EXAMPLE_BLOCK = new Block(FabricBlockSettings.create().strength(4.0f));
  7. // 对于自 1.20.5 之后、1.21.2 之前的版本:
  8. public static final Block EXAMPLE_BLOCK = new Block(Block.Settings.create().strength(4.0f));
  9.  
  10. @Override
  11. public void onInitialize() {
  12. Registry.register(Registries.BLOCK, new Identifier("tutorial", "example_block"), EXAMPLE_BLOCK);
  13. // 对于 1.20.5 以下版本:
  14. // Registry.register(Registries.ITEM, new Identifier("tutorial", "example_block"), new BlockItem(EXAMPLE_BLOCK, new FabricItemSettings()));
  15. // 对于 1.21 以下版本:
  16. // Registry.register(Registries.ITEM, new Identifier("tutorial", "example_block"), new BlockItem(EXAMPLE_BLOCK, new Item.Settings()));
  17. // 对于 1.21 之后的版本:
  18. Registry.register(Registries.ITEM, Identifier.of("tutorial", "example_block"), new BlockItem(EXAMPLE_BLOCK, new Item.Settings()));
  19. }
  20. }

注册方块的最佳实践

:!: 本段不适用于 1.21.2 之后的版本。

有时你的模组有许多方块。如果这样注册,你会为每个方块都写这样复杂的代码,代码就会显乱。所以,类似于注册物品,我们为方块创建单独的类,以及一个实用方法以注册方块和物品。

TutorialBlocks.java
public final class TutorialBlocks {
    public static final Block EXAMPLE_BLOCK = register("example_block", new Block(Block.Settings.create().strength(4.0f)));
 
    private static <T extends Block> T register(String path, T block) {
        Registry.register(Registries.BLOCK, Identifier.of("tutorial", path), block);
        Registry.register(Registries.ITEM, Identifier.of("tutorial", path), new BlockItem(block, new Item.Settings()));
        return block;
    }
 
    public static void initialize() {
    }
}

记得要在 ModInitializer 类中初始化 TutorialBlocks 类。

ExampleMod.java
public class ExampleMod implements ModInitializer {
    @Override
    public void onInitialize() {
        TutorialBlocks.initialize();
    }
}

在 1.21.2+ 中注册方块

在 1.21.2+ 中,要把 RegistryKey 添加到方块的 AbstractBlock.Settings 中,和物品的 Item.Settings 一起。看上去有点麻烦,不过好在 Minecraft 的 Blocks.registerItems.register 可以帮你做这些。

public class ExampleMod implements ModInitializer {
  public static final Block EXAMPLE_BLOCK = register("example_block", Block::new, Block.Settings.create().strength(4.0f));
 
  private static Block register(String path, Function<AbstractBlock.Settings, Block> factory, AbstractBlock.Settings settings) {
    final Identifier identifier = Identifier.of("tutorial", path);
    final RegistryKey<Block> registryKey = RegistryKey.of(RegistryKeys.BLOCK, identifier);
 
    final Block block = Blocks.register(registryKey, factory, settings);
    Items.register(block);
    return block;
  }
}

上面的代码中,Blocks.register 会帮助你将注册表键先写到 AbstractBlock.Settings 中,然后创建方块实例再注册。Items.register 会直接创建简单的 BlockItem 实例,使用和方块一样的 ID,然后注册。如果需要更加复杂的制作,例如创建 BlockItem 的子类,可以调用其他一些也叫做 Items.register 但是有不同方法签名的方法。

给予方块外观

你可能已经注意到,新的方块只是游戏中紫色和黑色棋盘格图案。这表明 Minecraft 加载方块资源或外观时出错。运行客户端时,完整的问题列表会输出在你的日志中。你需要以下文件来给予方块外观:

  • 方块状态文件
  • 方块模型文件
  • 纹理
  • 物品模型文件(如果方块有与之关联的物品)

这些文件位于:

  • 方块状态:src/main/resources/assets/tutorial/blockstates/example_block.json
  • 方块模型:src/main/resources/assets/tutorial/models/block/example_block.json
  • 物品模型:src/main/resources/assets/tutorial/models/item/example_block.json
  • 方块纹理:src/main/resources/assets/tutorial/textures/block/example_block.png

方块状态文件根据其方块装填确定该方块应使用的模型。由于我们的方块没有所谓状态,所以我们用空字符串表示所有:

src/main/resources/assets/tutorial/blockstates/example_block.json
{
  "variants": {
    "": { "model": "tutorial:block/example_block" }
  }
}

方块模型文件定义了方块的形状和纹理。我们将使用block/cube_all作为父模型,这将使我们能够轻松地在方块的所有面上设置相同的纹理。

src/main/resources/assets/tutorial/models/block/example_block.json
{
  "parent": "block/cube_all",
  "textures": {
    "all": "tutorial:block/example_block"
  }
}

在大多数情况下,您想让方块作为物品时外观还是这个方块。为此,您可以制作一个从方块模型文件继承的项目文件,这会使得该物品外观和方块相同:

src/main/resources/assets/tutorial/models/item/example_block.json
{
  "parent": "tutorial:block/example_block"
}

加载Minecraft,您的方块最终应该具有外观!

配置方块掉落物

该方块必须有战利品表,以便在该方块被破坏时掉落物品。以下文件会使方块被破坏时掉落其本身。

对于 1.21 之后的版本,路径为 src/main/resources/data/tutorial/loot_table/blocks/example_block.json(注意是单数)。对于 1.21 之前的版本,路径为 src/main/resources/data/tutorial/loot_tables/blocks/example_block.json(复数)。

src/main/resources/data/tutorial/loot_table/blocks/example_block.json
{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "tutorial:example_block"
        }
      ],
      "conditions": [
        {
          "condition": "minecraft:survives_explosion"
        }
      ]
    }
  ]
}

条件 minecraft:survives_explosion 的意思是,如果方块是在带有损耗的爆炸(例如苦力怕的爆炸,而非 TNT 的爆炸)中破坏的,有可能不会掉落。如果没有这个条件,那么方块在带有损耗的爆炸中总是会掉落。

在 1.17,破坏方块有所改变,定义采集工具和采集等级需要使用标签,请参考标签教程。我们需要将方块添加到以下标签:

* 采集工具:src/main/resources/data/minecraft/tags/block/mineable/<tooltype>.json,其中 <tooltype> 可以是 axepickaxeshovelhoe(对于 1.21 以下的版本,把“block”替换为“blocks”) * 采集等级:src/main/resources/data/minecraft/tags/block/needs_<tier>_tool.json,其中 <tier> 可以是:stoneirondiamond不包括 netherite)(对于 1.21 以下的版本,把“block”替换为“blocks”)

src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
{
  "replace": false,
  "values": [
    "tutorial:example_block"
  ]
}
src/main/resources/data/minecraft/tags/blocks/needs_stone_tool.json
{
  "replace": false,
  "values": [
    "tutorial:example_block"
  ]
}

要采集等级标签(needs_stone_toolneeds_iron_toolneeds_diamond_tool)生效,在方块定义中将 requiresTool()Block.Settings(以 1.20.5 以上版本为例):

  1. public static final Block EXAMPLE_BLOCK = new ExampleBlock(Block.Settings.create().strength(4.0f).requiresTool());

创建自定义方块类

当创建一个简单的方块时,上述方法效果很好,但是有时您想要一个具有独特机制的方块。我们将创建一个单独的继承 Block 的类来执行此操作。该类需要一个带有 AbstractBlock.Settings 参数的构造器:

ExampleBlock.java
  1. public class ExampleBlock extends Block {
  2. public ExampleBlock(Settings settings) {
  3. super(settings);
  4. }
  5. }

你可以在方块类中覆盖方法以实现特殊功能。这里是 onUse 方法的实现,右键单击方块时会调用此方法。我们检查交互是否在服务器进行,并向玩家发送消息,“Hello, world!”

ExampleBlock.java
  1. public class ExampleBlock extends Block {
  2.  
  3. public ExampleBlock(Settings settings) {
  4. super(settings);
  5. }
  6.  
  7. // 对于 1.20.5 以下版本,方法参数应该是“BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit”
  8. @Override
  9. public ActionResult onUse(World world, PlayerEntity player, BlockHitResult hit) {
  10. if (!world.isClient) {
  11. player.sendMessage(Text.literal("Hello, world!"), false);
  12. }
  13.  
  14. return ActionResult.SUCCESS;
  15. }
  16. }

要使用自定义方块类,请在注册时将 new Block 替换为 new ExampleBlock

TutorialBlocks.java
  1. public final class TutorialBlocks {
  2.  
  3. public static final Block EXAMPLE_BLOCK = register("example_block", new ExampleBLock(Block.Settings.create().strength(4.0f)));
  4.  
  5. // ...
  6. }

自定义形状

当使用不能完整填充一个方块的方块模型(例如铁砧、台阶、楼梯)而其形状却仍是完整的时,邻近的方块的隐藏面就会暴露:

要解决这个问题,我们需要定义方块的 VoxelShape

ExampleBlock.java
public class ExambleBlock extends Block {
    [...]
    @Override
    public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext context) {
        return VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1.0f, 0.5f);
    }
}

注意方块的碰撞形状默认为其描边形状,如果未被指定。

你也可以定义其他几类方块形状,方块形状的类型包括:

  • 外观形状(outline shape):方块大多数类型的形状都使用这个值作为默认。在世界中,当你指向这个形状时,会根据此形状绘制道明的黑色边框。大多数时候,这个形状不应该是空的。
  • 碰撞形状(collision shape):用于计算碰撞的形状。实体(包括玩家)移动时,其碰撞箱通常不能与方块的碰撞形状重合。一些方块,例如栅栏和墙,碰撞形状高于一格。一些方块,例如花,碰撞形状是空的。除了修改 getCollisionShape 方法外,你也可以在创建方块时调用 Block.Settings 中的 noCollision
  • raycasting shape:用于计算视线投射(判断你正在指向哪个方块的过程)的形状。通过不需要指定。
  • 相机碰撞形状(camera collision shape):用于计算第三人称视角的相机的位置的形状。玻璃和细雪的相机碰撞形状为空。

下一步

向方块添加简单状态,例如整数和布尔值

给块方块实体,使之能有物品栏之类的的高级状态。此外,还需要像 GUI 和自定义方块渲染。

要让方块可燃(也就是说,可以被火燃烧),可使用 FlammableBlockRegistry

zh_cn/tutorial/blocks.txt · Last modified: 2024/10/29 14:35 by solidblock