User Tools

Site Tools


zh_cn:tutorial:blockstate

赋予方块状态

Minecraft 中的每种类型的方块都由一个单独的 Block 实例表示。这样就无法仅通过更改 Block 实例的状态来更改特定方块的状态,因为该类型的其他所有方块都会受到影响!但是,如果您想给出一个单一的方块状态,以便它可以根据某些条件改变,该怎么办?

这就是 BlockState 的目的。假设我们希望一个方块仅在充能后能够召唤闪电。

首先,我们定义方块的布尔值属性——是否充能(小心不要导入错误的 BooleanProperty),并在模组初始化的地方将其注册。(如果你直接在 ChargeableBlock 类的静态字段中注册,模组初始化器可能会忽略它,如果这个类没有被初始化的话。)

事实上你也可以使用原版已有的属性,可以在 Propertiesnet.minecraft.state.property.Properties)中找到。如果你需要定义其他类型的属性,可以使用 IntPropertyEnumProperty

public class ChargeableBlock extends Block {
    public static final BooleanProperty CHARGED = BooleanProperty.of("charged");
 
    // 方块实例。可以放在任何地方。确保这个类已经初始化。
    public static final ChargeableBlock CHARGEABLE_BLOCK = new ChargeableBlock(FabricBlockSettings.copyOf(Blocks.STONE));
}
public class ExampleMod implements ModInitializer {
    @Override
    public void onInitialize() {
        Registry.register(Registries.BLOCK, new Identifier("tutorial", "chargeable_block"), ChargeableBlock.CHARGEABLE_BLOCK);
        Registry.register(Registries.ITEM, new Identifier("tutorial", "chargeable_block"), new BlockItem(ChargeableBlock.CHARGEABLE_BLOCK, new FabricItemSettings());
    }
}

然后,我们需要通过覆盖 appendProperties 并加入 CHARGED 以注册属性:

public class ChargeableBlock extends Block {
    [...]
    @Override
    protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
        builder.add(CHARGED);
    }
 
}

然后,我们需要在方块构造器中设置属性的默认状态(要设置多个属性,请通过调用 with() 来连接):

public class ChargeableBlock extends Block {
    [...]
    public ChargeableBlock(Settings settings) {
        super(settings);
        setDefaultState(getDefaultState().with(CHARGED, false));
    }
 
}

现在,我们需要能够通过 onUse 方法充能方块,在该方法中调用 world.setBlockState()(这个 playSound 是可循的,只是让我们知道方块充能了)。

public class ChargeableBlock extends Block {
    [...]
    @Override
    public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
        player.playSound(SoundEvents.BLOCK_RESPAWN_ANCHOR_CHARGE, 1, 1);
        world.setBlockState(pos, state.with(CHARGED, true));
        return ActionResult.SUCCESS;
    }
}

最后,要使用 CHARGED 属性,我们调用 onSteppedOn,并在里面使用 world.getBlockState(pos).get(CHARGED)

public class ChargeableBlock extends Block {
    [...]
    @Override
    public void onSteppedOn(World world, BlockPos pos, BlockState state, Entity entity) {
        if (world.getBlockState(pos).get(CHARGED)){
            // 在方块的位置召唤闪电
            LightningEntity lightningEntity = (LightningEntity) EntityType.LIGHTNING_BOLT.create(world);
            lightningEntity.refreshPositionAfterTeleport(Vec3d.ofBottomCenter(pos));
            world.spawnEntity(lightningEntity);
        }
 
        world.setBlockState(pos, state.with(CHARGED, false));
        super.onSteppedOn(world, pos, state, entity);
    }
}

为方块状态添加模型

你可能还需要使得纹理和模型能够根据状态来改变,这是通过一个叫做“方块状态 JSON”的 JSON 来完成的。所有的方块都需要一个方块状态 JSON,无论它是否有多个方块状态,但其内容可以简单也可以复杂。如果需要根据方块状态来改变纹理,我们需要添加多个模型。

比如说,你为 ChargeableBlock 注册了 ID 为 tutorial:chargeable_block。Minecraft 会加载地址 src/main/resources/assets/tutorial/blockstates/chargeable_block.json 并从中加载方块状态。如果你不需要让方块根据状态更改模型,方块状态 JSON 可以非常简单,就像这样:

resources/assets/tutorial/blockstates/chargeable_block.json
{
    {
        "variants": {
            "": { "model": "tutorial:block/chargeable_block" }
        }
    }
 
}

让我们分解一下这个简单的例子。JSON 有几个重要的部分:

- “variants” 定义了每个方块状态应该使用什么模型。我们将稍作探讨。 - 名为 “” 的变种将应用于每个方块状态。如果你有 “” 变种,则 JSON 中不应包含任何其他变种,否则 Minecraft 会出问题。 - 分配给 “” 变种的对象可以添加各种属性,例如旋转或纹理操作。请查看下面的链接的“模型”页面,以获取有关可以添加哪些属性的更多文档。所有变种都必须包含 “model”属性。 - “model” 属性总是传递模型的 ID。在这个例子中,游戏将查看位置 src/main/resources/assets/tutorial/models/block/chargeable_block.json。这里的ID可以是任何东西,不需要与方块的ID相同,但是如果只有一个变种,则应当要相同。方块模型具有自己的设置,在下面链接的 Minecraft Wiki 页面上有很好的记录。您可以手动编写 JSON,也可以使用Blockbench之类的程序更轻松地生成它。

如果确实想要为每个方块状态使用不同的模型,则需要添加多个变种。对于我们上面使用的同一个 src/main/resources/assets/tutorial/blockstates/chargeable_block.json 位置,你可能需要这样子:

resources/assets/tutorial/blockstates/chargeable_block.json
    {
        "variants": {
            "charged=false": { "model": "tutorial:block/chargeable_block" },
            "charged=true": { "model": "tutorial:block/chargeable_block_charged" }
        }
    }

在此 JSON中,有两个变种,一种针对我们上面定义的 CHARGED 属性的每种可能性。由于我们在 Java 中为属性指定了字符串 charged,因此我们在这里使用它。布尔值只有两种状态,但是如果您使用基于整数或枚举的属性,则会有更多的变种。

变种基于添加到方块中的属性的可能排列。如果需要,可以在方块状态 JSON 中完全忽略某个属性,例如在第一个方块状态 JSON 中我们忽略了 charged 属性,但如果要在一个变种中包含属性,则必须将其包含在所有变种。如果 tutorial:chargeable_block 还具有一个称为 glowing 的布尔属性,并且您想根据模型是否发光以及是否经过充能来更改模型,则需要四个变种:充能发光,充能不发光,不充能但发光,不充能不发光。如果需要,可以将同一模型分配给多个变种。

这只是对方块状态 JSON 的简单介绍。Minecraft Wiki中记录了有关方块状态和模型 JSON 的所有技巧,以及在原版游戏中使用这些功能的示例。祝你好运!

关于性能的注意事项

游戏开始时会注册方块的所有可能状态。这意味着,如果具有14个布尔属性,则该方块具有2 ^ 14 = 16384个不同的状态,并且会注册这2 ^ 14个状态。因此,方块不应包含太多的方块状态属性。相反,方块状态应主要保留用于视觉效果,需要使用更高级的状态应使用方块实体

由于所有的可能的方块状态都已经构建好了,因此相等的方块状态是同一个对象,with 方法会返回一个存在的对象,而不是创建一个新对象——例如,CHARGEABLE_BLOCK.getDefaultState().with(CHARGED, true) == CHARGEABLE_BLOCK.getDefaultState().with(CHARGED, true) 会返回 true

zh_cn/tutorial/blockstate.txt · Last modified: 2023/11/18 08:13 by solidblock