跳转至

自定义方块

自定义方块是Allay里风险较高但也最强的一组高级接口。它允许你为方块生成客户端定义,控制几何体、材质、变换和按状态切换的渲染结果。

先确认前提

这套接口需要访问Allay的服务端模块,所以构建配置里必须关闭apiOnly

build.gradle.kts
allay {
    apiOnly = false
}

内部API风险

apiOnly = false意味着你的插件开始接触Allay服务端实现层,而不是只依赖稳定API层。这类接口在版本升级时更容易发生破坏性变更。

最简单的写法:只给一个纹理

如果你只想先做一个简单的自定义方块,直接从ofTexture()开始:

import org.allaymc.server.block.type.AllayBlockType;
import org.allaymc.server.block.type.CustomBlockDefinitionGenerator;

AllayBlockType.builder(MyBlockImpl.class)
    .identifier("myplugin:ruby_block")
    .blockDefinitionGenerator(
        CustomBlockDefinitionGenerator.ofTexture("ruby_block"))
    .build();

这适合“外观简单,只要一个贴图”的方块。

给方块指定固定几何体和材质

如果你已经有资源包里的模型,可以使用ofConstant()

import org.allaymc.server.block.type.BlockStateDefinition;
import org.allaymc.server.block.type.BlockStateDefinition.Geometry;
import org.allaymc.server.block.type.BlockStateDefinition.Materials;
import org.allaymc.server.block.type.CustomBlockDefinitionGenerator;

AllayBlockType.builder(MyLampImpl.class)
    .identifier("myplugin:custom_lamp")
    .blockDefinitionGenerator(
        CustomBlockDefinitionGenerator.ofConstant(
            BlockStateDefinition.builder()
                .geometry(Geometry.of("geometry.custom_lamp"))
                .materials(Materials.builder().any("lamp_texture"))
                .displayName("自定义灯")
                .build()))
    .build();

这里最核心的对象是BlockStateDefinition。它描述的是“某个方块状态在客户端应该怎样渲染”。

按方块状态切换外观

如果一个方块会随状态变化切换模型或贴图,就用of(state -> ...)

import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.server.block.type.BlockStateDefinition;
import org.allaymc.server.block.type.BlockStateDefinition.Geometry;
import org.allaymc.server.block.type.BlockStateDefinition.Materials;
import org.allaymc.server.block.type.CustomBlockDefinitionGenerator;

AllayBlockType.builder(MyDoorImpl.class)
    .identifier("myplugin:custom_door")
    .setProperties(BlockPropertyTypes.OPEN_BIT)
    .blockDefinitionGenerator(
        CustomBlockDefinitionGenerator.of(state -> {
            boolean open = state.getPropertyValue(BlockPropertyTypes.OPEN_BIT);
            return BlockStateDefinition.builder()
                .geometry(Geometry.of(open ? "geometry.door_open" : "geometry.door_closed"))
                .materials(Materials.builder().any("door_texture"))
                .build();
        }))
    .build();

这类写法很适合门、机器、作物、指示灯等“状态决定表现”的方块。

材质怎么配

Materials.builder()可以按面分配贴图:

import org.allaymc.api.block.data.BlockFace;
import org.allaymc.server.block.type.BlockStateDefinition.Materials;

Materials.builder()
    .any("log_side")
    .face(BlockFace.UP, "log_top")
    .face(BlockFace.DOWN, "log_top");

如果你需要透明、双面或更复杂的渲染方式,就改用MaterialInstance

import org.allaymc.server.block.type.BlockStateDefinition.MaterialInstance;
import org.allaymc.server.block.type.BlockStateDefinition.Materials;

Materials.builder().any(MaterialInstance.alphaTest("leaves_texture"));

常见工厂方法包括:

  • opaque(...)
  • alphaTest(...)
  • alphaTestSingleSided(...)
  • blend(...)
  • doubleSided(...)

变换和旋转

如果模型需要旋转、缩放或平移,可以设置Transformation

import org.allaymc.server.block.type.BlockStateDefinition.Transformation;

Transformation.builder()
    .ry(90)
    .sx(1.0f).sy(1.0f).sz(1.0f)
    .tx(0.0f).ty(0.0f).tz(0.0f)
    .build();

旋转值限制

给出的旋转值是090180270。超出这个集合时,客户端渲染结果并没有可靠保证。

几何体还能做什么

除了最简单的Geometry.of("geometry.xxx")外,高级写法还支持:

  • 控制骨骼可见性。
  • 设置剔除规则与剔除图层。
  • 对全部或指定骨骼启用UV锁定。

这些能力更适合已经有成熟资源包模型、并且确实需要按状态隐藏某些骨骼的插件。

记住:渲染定义不等于物理行为

BlockStateDefinition主要描述客户端渲染。碰撞箱、选择框、发光、摩擦等物理性质并不是在这里写死的,而是来自方块本身的数据与组件。

实战建议

  • 第一次做自定义方块时,先用ofTexture()跑通整条链路。
  • 只有真的需要按状态切模型时,再升级到of(state -> ...)
  • 几何体标识符和贴图名必须和资源包里的实际文件保持一致。
  • 如果你还没熟悉基础方块接口,先回头看方块API

评论