Fabric 教程
安装
- 启用 log4j 调试信息(英文)
基础
- 约定和术语
- 注册
- 开发工具
物品
方块和方块实体
数据生成
世界生成
命令
事件
实体
流体
Mixin 和 ASM
杂项
Yarn
贡献 Fabric
- FabLabs - 在提交 PR 之前起草新功能的试验场
在这里,我们将介绍自定义流体的创建。如果计划创建多个流体,建议创建一个抽象的基本流体类,在其中设置必要的默认值,这些默认值将在其子类中共享。我们还将使其像湖泊一样在世界中生成。
原版流体扩展了net.minecraft.fluid.FlowableFluid
,我们也应如此。
public abstract class TutorialFluid extends FlowableFluid { /** * @return 给定的流体是否为该流体的实例? */ @Override public boolean matchesType(Fluid fluid) { return fluid == getStill() || fluid == getFlowing(); } /** * @return 流体是否可以像无限刷水的方法一样无限生成? */ @Override protected boolean isInfinite() { return false; } /** * 流体流入一个可替换的方块时的行为。 * 水会掉落方块的战利品表。熔岩会播放“block.lava.extinguish”音效。 */ @Override protected void beforeBreakingBlock(WorldAccess world, BlockPos pos, BlockState state) { final BlockEntity blockEntity = state.getBlock().hasBlockEntity() ? world.getBlockEntity(pos) : null; Block.dropStacks(state, world, pos, blockEntity); } /** * Lava returns true if its FluidState is above a certain height and the * Fluid is Water. * * @return 给定的流体能否流入它的FluidState? */ @Override protected boolean canBeReplacedWith(FluidState fluidState, BlockView blockView, BlockPos blockPos, Fluid fluid, Direction direction) { return false; } /** * 或许与流入周围附近凹洞的距离检查有关? * 水返回4。熔岩在主世界返回2,而在下界返回4。 */ @Override protected int getFlowSpeed(WorldView worldView) { return 4; } /** * 返回每次流动一格,其等级减少的数值。水返回1,熔岩在主世界返回2,在下界返回1。 */ @Override protected int getLevelDecreasePerBlock(WorldView worldView) { return 1; } /** * 返回每流一格需要花费的时间(按刻计算)。水返回5。熔岩在主世界返回30,在下界返回10。 */ @Override public int getTickRate(WorldView worldView) { return 5; } /** * 返回爆炸抗性。水和熔岩都返回100.0F。 */ @Override protected float getBlastResistance() { return 100.0F; } }
现在让我们制作一个拥有静止和流动两个变种的实际流体。在此教程中,我们将其称为“酸”。缺失的引用稍后补全。
public abstract class AcidFluid extends TutorialFluid { @Override public Fluid getStill() { return <YOUR_STILL_FLUID_HERE>; } @Override public Fluid getFlowing() { return <YOUR_FLOWING_FLUID_HERE>; } @Override public Item getBucketItem() { return <YOUR_BUCKET_ITEM_HERE>; } @Override protected BlockState toBlockState(FluidState fluidState) { // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses return <YOUR_FLUID_BLOCK_HERE>.getDefaultState().with(Properties.LEVEL_15, method_15741(fluidState)); } public static class Flowing extends AcidFluid { @Override protected void appendProperties(StateManager.Builder<Fluid, FluidState> builder) { super.appendProperties(builder); builder.add(LEVEL); } @Override public int getLevel(FluidState fluidState) { return fluidState.get(LEVEL); } @Override public boolean isStill(FluidState fluidState) { return false; } } public static class Still extends AcidFluid { @Override public int getLevel(FluidState fluidState) { return 8; } @Override public boolean isStill(FluidState fluidState) { return true; } } }
接下来,我们将制作静态和动态酸变体的静态实例,以及一个酸桶。 在您的ModInitializer
中:
// ... public static FlowableFluid STILL_ACID; public static FlowableFluid FLOWING_ACID; public static Item ACID_BUCKET; // ... @Override public void onInitialize() { // ... // ... } // ...
为了使自定义流体表现得像水或熔岩,您必须将其添加到相应的流体标签中:对于水,制作data/minecraft/tags/fluids/ water.json
文件,并在其中写入流体的标识符:
{ "replace": false, "values": [ "your_mod_id:acid", "your_mod_id:flowing_acid" ] }
接下来,我们需要在世界中创建表示酸的方块。net.minecraft.block.FluidBlock
是我们需要使用的类,但由于其构造器受保护,我们不能直接构造它。一种解决方法是制作子类或者匿名子类。这里我们展示后一种方式。在您的ModInitializer
中:
// ... public static Block ACID; // ... @Override public void onInitialize() { // ... // ... } // ...
既然我们有了这些静态对象,我们回到AcidFluid
并补全被重写的方法:
public abstract class AcidFluid extends TutorialFluid { @Override public Fluid getStill() { return TutorialMod.STILL_ACID; } @Override public Fluid getFlowing() { return TutorialMod.FLOWING_ACID; } @Override public Item getBucketItem() { return TutorialMod.ACID_BUCKET; } @Override protected BlockState toBlockState(FluidState fluidState) { // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses } // ... }
为了让流体拥有纹理,或者与一个颜色绑定,你需要为其注册一个FluidRenderHandler
。这里,我们重用水的纹理,并仅仅改变用于其上的颜色。为确保纹理渲染为半透明的,你可以使用Fabric的BlockRenderLayerMap
。
public class TutorialModClient implements ClientModInitializer { // ... @Override public void onInitializeClient() { // ... setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new Identifier("minecraft", "water"), 0x4CC248); BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID); // ... } // ... public static void setupFluidRendering(final Fluid still, final Fluid flowing, final Identifier textureFluidId, final int color) { final Identifier stillSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_still"); final Identifier flowingSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_flow"); // If they're not already present, add the sprites to the block atlas ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register((atlasTexture, registry) -> { registry.register(stillSpriteId); registry.register(flowingSpriteId); }); final Identifier listenerId = new Identifier(fluidId.getNamespace(), fluidId.getPath() + "_reload_listener"); final Sprite[] fluidSprites = { null, null }; ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() { @Override public Identifier getFabricId() { return listenerId; } /** * Get the sprites from the block atlas when resources are reloaded */ @Override public void apply(ResourceManager resourceManager) { final Function<Identifier, Sprite> atlas = MinecraftClient.getInstance().getSpriteAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEX); fluidSprites[0] = atlas.apply(stillSpriteId); fluidSprites[1] = atlas.apply(flowingSpriteId); } }); // The FluidRenderer gets the sprites and color from a FluidRenderHandler during rendering final FluidRenderHandler renderHandler = new FluidRenderHandler() { @Override public Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState state) { return fluidSprites; } @Override public int getFluidColor(BlockRenderView view, BlockPos pos, FluidState state) { return color; } }; FluidRenderHandlerRegistry.INSTANCE.register(still, renderHandler); FluidRenderHandlerRegistry.INSTANCE.register(flowing, renderHandler); } // ... }
如果你需要使用你自己的流体纹理,你可以参考原版资源包1)作为一个模板。
为使得酸湖在世界中生成,你可以在你的ModInitializer
中创建一个net.minecraft.world.gen.feature.LakeFeature
,然后将其添加到你需要让它生成的生物群系中:
// ... public static LakeFeature ACID_LAKE; // ... @Override public void onInitialize() { // ... // 在沼泽中生成,类似于水湖,但是概率为40(数字越高,生成几率越低) Biomes.SWAMP.addFeature( GenerationStep.Feature.LOCAL_MODIFICATIONS, ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))) ); // ... } // ...
assets/minecraft/blockstates/water.json
assets/minecraft/models/block/water.json
assets/minecraft/textures/block/water_still.png
assets/minecraft/textures/block/water_still.png.mcmeta
assets/minecraft/textures/block/water_flow.png
assets/minecraft/textures/block/water_flow.png.mcmeta