User Tools

Site Tools


zh_cn:tutorial:fluids

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
Next revisionBoth sides next revision
zh_cn:tutorial:fluids [2019/12/19 10:16] – created lightcolourzh_cn:tutorial:fluids [2023/05/04 11:27] – [制作一个流体方块] solidblock
Line 1: Line 1:
-====== Creating a fluid ====== +====== 创建流体 ====== 
-===== Overview ===== +===== 概述 ===== 
-Here we'll cover creation of a custom fluid. If you plan to create several fluids, it is recommended to make an abstract basic fluid class where you'll set necessary defaults that will be shared in its subclasses. We'll also make it generate in the world like lakes. +在这里,我们将介绍自定义流体的创建。如果计划创建多个流体,建议创建一个抽象的基本流体类,在其中设置必要的默认值,这些默认值将在其子类中共享。我们还将使其像湖泊一样在世界中生成。 
-===== Making an abstract fluid ===== +===== 创建抽象流体 ===== 
-Vanilla fluids extend **net.minecraft.fluid.BaseFluid** class, and so will our abstract fluid. It can be like this: +原版流体继承了 ''<yarn net.minecraft.class_3609>'',我们也应如此。 
-<code java> +<yarncode java [enable_line_numbers="true"]
-public abstract class BasicFluid extends BaseFluid +public abstract class TutorialFluid extends class_3609 
-+ /** 
-    /** +  * @return 给定的流体是否为该流体的实例? 
-     * @return does it produce infinite fluid (like water)? + */ 
-     */ + @Override 
-    @Override + public boolean method_15780(class_3611 fluid) { 
-    protected boolean isInfinite() + return fluid == getStill() || fluid == getFlowing(); 
-    +
-        return false; +  
-    }+ /** 
 + * @return 流体是否可以像无限刷水的方法一样无限生成?在原版,这取决于游戏规则。 
 +  */ 
 + @Override 
 + protected boolean method_15737() { 
 + return false; 
 +
 +  
 + /** 
 + * 流体流入一个可替换的方块时的行为。 
 + * 水会掉落方块的战利品表。熔岩会播放“block.lava.extinguish”音效。 
 + */ 
 + @Override 
 + protected void method_15730(class_1936 world, class_2338 pos, class_2680 state) 
 + final BlockEntity blockEntity = state.getBlock().hasBlockEntity() ? world.getBlockEntity(pos) : null; 
 + Block.dropStacks(state, world, pos, blockEntity); 
 +
 +  
 + /** 
 + * 熔岩在其 FluidState 高于指定的高度且该流体为水时返回 true。 
 + *  
 + * @return 给定的流体能否流入它的 FluidState? 
 + */ 
 + @Override 
 + protected boolean method_15777(class_3610 fluidState, class_1922 blockView, class_2338 blockPos, class_3611 fluid, class_2350 direction) { 
 + return false; 
 + } 
 +  
 + /** 
 + * 或许与流入周围附近凹洞的距离检查有关? 
 + * 水返回4。熔岩在主世界返回2,而在下界返回4。 
 + */ 
 + @Override 
 + protected int method_15733(class_4538 worldView) { 
 + return 4; 
 +
 +  
 + /** 
 + * 返回每次流动一格,其等级减少的数值。水返回1,熔岩在主世界返回2,在下界返回1。 
 + */ 
 + @Override 
 + protected int method_15739(class_4538 worldView) { 
 + return 1; 
 +
 +  
 + /** 
 + * 返回每流一格需要花费的时间(按刻计算)。水返回5。熔岩在主世界返回30,在下界返回10。 
 + */ 
 + @Override 
 + public int method_15789(class_4538 worldView) { 
 + return 5; 
 +
 +  
 + /** 
 + * 返回爆炸抗性。水和熔岩都返回100.0F。 
 + */ 
 + @Override 
 + protected float method_15784() { 
 + return 100.0F; 
 +
 +
 +</yarncode>
  
-    // make it transparent +===== 实现 ===== 
-    @Override +现在让我们制作一个拥有静止和流动两个变种的实际流体。在此教程中,我们将其称为“酸”。缺失的引用稍后补全。
-    protected BlockRenderLayer getRenderLayer() +
-    { +
-        return BlockRenderLayer.TRANSLUCENT; +
-    }+
  
-    /** +<yarncode java [enable_line_numbers="true"]> 
-     * +public abstract class AcidFluid extends TutorialFluid { 
-     * @return an associated item that "holdsthis fluid + @Override 
-     */ + public class_3611 method_15751() 
-    @Override + return YOUR_STILL_FLUID_HERE; 
-    public abstract Item getBucketItem();+ }
  
-    /** + @Override 
-     * + public class_3611 method_15750() 
-     * @return a blockstate of the associated {@linkplain net.minecraft.block.FluidBlock} with {@linkplain net.minecraft.block.FluidBlock#LEVEL} + return YOUR_FLOWING_FLUID_HERE; 
-     */ + }
-    @Override +
-    protected abstract BlockState toBlockState(FluidState var1);+
  
-    /** + @Override 
-     * + public class_1792 method_15774() 
-     * @return flowing static instance of this fluid + return YOUR_BUCKET_ITEM_HERE; 
-     */ + }
-    @Override +
-    public abstract Fluid getFlowing();+
  
-    /** + @Override 
-     * + protected class_2680 method_15790(class_3610 fluidState) { 
-     * @return still static instance of this fluid + return YOUR_FLUID_BLOCK_HERE.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState)); 
-     */ + }
-    @Override +
-    public abstract Fluid getStill();+
  
-    // how much does the height of the fluid block decreases + public static class Flowing extends AcidFluid { 
-    @Override + @Override 
-    protected int getLevelDecreasePerBlock(ViewableWorld world) + protected void method_15775(class_2689.class_2690<class_3611, class_3610> builder) { 
-    + super.method_15775(builder); 
-        return 1+ builder.method_11667(field_15900)
-    }+ }
  
-    /** + @Override 
-     *  + public int method_15779(class_3610 fluidState) { 
-     * @return update rate + return fluidState.method_11654(field_15900)
-     */ + }
-    @Override +
-    public int getTickRate(ViewableWorld world) +
-    +
-        return 5+
-    }+
  
-    @Override + @Override 
-    protected float getBlastResistance() + public boolean method_15793(class_3610 fluidState) { 
-    + return false
-        return 100+
-    }+ }
  
-    // this seems to determine fluid's spread speed (higher value means faster) + public static class Still extends AcidFluid { 
-    @Override + @Override 
-    protected int method_15733(ViewableWorld world) + public int method_15779(class_3610 fluidState) { 
-    + return 8
-        return 4+ }
-    }+
  
-    /I don't know what this does, but it's present in the water fluid + @Override 
-    @Override + public boolean method_15793(class_3610 fluidState) { 
-    protected void beforeBreakingBlock(IWorld world, BlockPos blockPos, BlockState blockState) { + return true; 
-        BlockEntity blockEntity blockState.getBlock().hasBlockEntity() ? world.getBlockEntity(blockPos: null+
-        Block.dropStacks(blockState, world.getWorld(), blockPos, blockEntity); +
-    } +
- +</yarncode> 
-    // also don't know what it does +接下来,我们将制作静态和动态酸变体的静态实例,以及一个酸桶。在您的 ''ModInitializer'' 中: 
-    public boolean method_15777(FluidState fluidStateBlockView blockViewBlockPos blockPosFluid fluid, Direction direction) { +<yarncode java [enable_line_numbers="true"]> 
-        return direction == Direction.DOWN+public static class_3609 STILL_ACID; 
-    } +public static class_3609 FLOWING_ACID; 
- +public static class_1792 ACID_BUCKET; 
-    /** +  
-     * +@Override 
-     * @return is given fluid instance of this fluid? +public void onInitialize() { 
-     *+ STILL_ACID class_2378.method_10230(class_7923.field_41173, new class_2960("tutorial", "acid"), new AcidFluid.Still())
-    @Override + FLOWING_ACID = class_2378.method_10230(class_7923.field_41173, new class_2960("tutorial", "flowing_acid"), new AcidFluid.Flowing()); 
-    public abstract boolean matchesType(Fluid fluid);+ ACID_BUCKET = class_2378.method_10230(class_7923.field_41178new class_2960("tutorial""acid_bucket"),  
 +        new class_1755(STILL_ACID, new class_1792.class_1793().method_7896(class_1802.field_8550).method_7889(1)))
 +  
 + // ... 
 +} 
 +  
 +// ... 
 +</yarncode>
  
 +为了使自定义流体表现得像水或熔岩,您必须将其添加到相应的流体标签中:对于水,制作 ''data/minecraft/tags/fluids/water.json'' 文件,并在其中写入流体 id:
 +<code json [enable_line_numbers="true"]>
 +{
 + "replace": false,
 + "values":
 + [
 + "tutorial:acid",
 + "tutorial:flowing_acid"
 + ]
 } }
 </code> </code>
  
-===== Implementation ===== 
-Now let's make an actual fluid; it will have a //still// and //flowing// variants; will name it "Acid": 
  
-<code java> +==== 制作一个流体方块 ==== 
-public abstract class Acid extends BasicFluid +接下来,我们需要在世界中创建表示酸的方块。''<yarn net.minecraft.class_2404>'' 是我们需要使用的类,但由于其构造器受保护,我们不能直接构造它。一种解决方法是制作子类或者匿名子类。这里我们展示后一种方式。在您的 ''ModInitializer'' 中: 
-+<yarncode java [enable_line_numbers="true"]
-    @Override +public static class_2248 ACID;
-    public Item getBucketItem() +
-    { +
-        return null; +
-    } +
-    @Override +
-    protected BlockState toBlockState(FluidState var1) +
-    { +
-        return null; +
-    }+
  
-    @Override +@Override 
-    public Fluid getFlowing() +public void onInitialize() { 
-    + ACID = class_2378.method_10230(class_7923.field_41175, new class_2960("tutorial", "acid"), new class_2404(STILL_ACID, FabricBlockSettings.method_9630(class_2246.field_10382)){})
-        return null+  
-    }+ // ... 
 + 
 +</yarncode>
  
-    @Override +既然我们有了这些静态对象,我们回到 ''AcidFluid'' 并补全被重写的方法: 
-    public Fluid getStill() +<yarncode java [enable_line_numbers="true"]> 
-    +public abstract class AcidFluid extends TutorialFluid { 
-        return null+ @Override 
-    }+ public class_3611 method_15751() { 
 + return TutorialMod.STILL_ACID; 
 +
 +  
 + @Override 
 + public class_3611 method_15750() 
 + return TutorialMod.FLOWING_ACID; 
 +
 +  
 + @Override 
 + public class_1792 method_15774() { 
 + return TutorialMod.ACID_BUCKET; 
 +
 +  
 + @Override 
 + protected class_2680 method_15790(class_3610 fluidState) { 
 + // method_15741 将流体状态的 LEVEL_1_8 转换为流体方块使用的 LEVEL_15 
 + return TutorialMod.ACID.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState))
 + }
  
-    @Override + public static class Flowing extends AcidFluid { 
-    public boolean matchesType(Fluid fluid) + @Override 
-    + protected void method_15775(class_2689.class_2690<class_3611, class_3610> builder) { 
-        return false+ super.method_15775(builder); 
-    }+ builder.method_11667(field_15900)
 + }
  
-    // still acid + @Override 
-    public static class Still extends Acid + public int method_15779(class_3610 fluidState) { 
-    {+ return fluidState.method_11654(field_15900); 
 + }
  
-        @Override + @Override 
-        public boolean isStill(FluidState fluidState) + public boolean method_15793(class_3610 fluidState) { 
-        + return false
-            return true+
-        }+ }
  
-        /** + public static class Still extends AcidFluid { 
-         * @return height of the fluid block + @Override 
-         */ + public int method_15779(class_3610 fluidState) { 
-        @Override + return 8; 
-        public int getLevel(FluidState fluidState) + }
-        +
-            return 8; +
-        } +
-    }+
  
-    // flowing acid + @Override 
-    public static class Flowing extends  Acid + public boolean method_15793(class_3610 fluidState) { 
-    {+ return true; 
 +
 +
 +  
 +</yarncode> 
 +===== 渲染设置 ===== 
 +为了让流体拥有纹理,或者与一个颜色绑定,你需要为其注册一个''FluidRenderHandler''。这里,我们重用水的纹理,并仅仅改变用于其上的颜色。为确保纹理渲染为半透明的,你可以使用Fabric的''BlockRenderLayerMap''
  
-        @Override +<code java [enable_line_numbers="true"]> 
-        public boolean isStill(FluidState fluidState+public class TutorialModClient implements ClientModInitializer 
-        +
-            return false+ // ... 
-        +  
- + @Override 
-        /** + public void onInitializeClient() 
-         @return height of the fluid block +
-         */ + // ... 
-        @Override +  
-        public int getLevel(FluidState fluidState+ setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new Identifier("minecraft", "water"), 0x4CC248); 
-        + BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID); 
-            return fluidState.get(LEVEL); +  
-        + // ... 
- +
-        @Override +  
-        protected void appendProperties(StateFactory.Builder<Fluid, FluidState> stateFactoryBuilder+ // ... 
-        +  
-            super.appendProperties(stateFactoryBuilder); + public static void setupFluidRendering(final Fluid still, final Fluid flowing, final Identifier textureFluidId, final int color) 
-            stateFactoryBuilder.add(LEVEL); +
-        + 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 fluidId = Registry.FLUID.getId(still); 
 + 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); 
 +
 +  
 + // ...
 } }
 </code> </code>
  
-Next, we'll make static instances of still and flowing acid variants, and an acid bucketIn your **ModInitializer**:+如果你需要使用你自己的流体纹理,你可以参考原版资源包((''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''))作为一个模板。
  
-<code java>+===== 在世界中生成 ===== 
 +为使得酸湖在世界中生成,你可以在你的''ModInitializer''中创建一个''net.minecraft.world.gen.feature.LakeFeature'',然后将其添加到你需要让它生成的生物群系中:
  
-     +<code java [enable_line_numbers="true"]> 
-    public static Acid stillAcid; +// ...
-    public static Acid flowingAcid; +
-     +
-    public static BucketItem acidBucket;+
  
-    @Override +public static LakeFeature ACID_LAKE;
-    public void onInitialize() +
-    { +
-     +
-        stillAcid = Registry.register(Registry.FLUID, new Identifier(MODID,"acid_still"), new Acid.Still()); +
-        flowingAcid = Registry.register(Registry.FLUID, new Identifier(MODID,"acid_flowing"), new Acid.Flowing()); +
-         +
-        acidBucket = new BucketItem(stillAcid, new Item.Settings().maxCount(1)); +
-        Registry.register(Registry.ITEM, new Identifier(MODID,"acid_bucket"), acidBucket); +
-    }     +
-</code>+
  
-To make the custom fluid behave like water or lava, you must add it to a corresponding fluid tag: make a file "data/minecraft/tags/fluids/water.json" and write identifiers of your fluids in there: +// ...
-<code json> +
-+
-  "replace": false, +
-  "values":+
-    "modid:acid_still", +
-    "modid:acid_flowing" +
-  ] +
-+
-</code>+
  
-==== Making a fluid block ==== +@Override 
-Next we need to create a block which will represent acid in the world. **net.minecraft.block.FluidBlock** is the class we need to use, but for "mojang" reasons its constructor is protected. The solution is well-known - make a subclass of it and change the visibility of the constructor: +public void onInitialize()
- +
-<code java> +
-public class BaseFluidBlock extends FluidBlock+
 { {
-    public BaseFluidBlock(BaseFluid fluidSettings settings+ // ... 
-    { +  
-        super(fluidsettings); + ACID_LAKE = Registry.register(Registry.FEATUREnew Identifier(MOD_ID, "acid_lake"), new LakeFeature(SingleStateFeatureConfig::deserialize)); 
-    }+  
 + // 在沼泽中生成,类似于水湖,但是概率为40(数字越高,生成几率越低) 
 + Biomes.SWAMP.addFeature( 
 + GenerationStep.Feature.LOCAL_MODIFICATIONS, 
 + ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) 
 + .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))) 
 + ); 
 +  
 + // ...
 } }
-</code> 
  
-Now make a static block instance: +// ...
- +
-<code java> +
-    ... +
-     +
-    public static FluidBlock acid; +
- +
-    @Override +
-    public void onInitialize() +
-    { +
-     +
-        ... +
-         +
-        acid = new BaseFluidBlock(stillAcid, FabricBlockSettings.of(Material.WATER).dropsNothing().build()); +
-        Registry.register(Registry.BLOCK, new Identifier(MODID, "acid_block"), acid); +
-    }     +
-</code> +
- +
-Now when we have these static objects, we go back to **Acid** class and complete the overridden methods: +
- +
-<code java> +
-public abstract class Acid extends BasicFluid +
-+
-    @Override +
-    public Item getBucketItem() +
-    { +
-        return Mod.acidBucket; +
-    } +
-     +
-    @Override +
-    protected BlockState toBlockState(FluidState fluidState) +
-    { +
-        //don't ask me what **method_15741** does... +
-        return Mod.acid.getDefaultState().with(FluidBlock.LEVEL, method_15741(fluidState)); +
-    } +
- +
-    @Override +
-    public Fluid getFlowing() +
-    { +
-        return Mod.flowingAcid; +
-    } +
- +
-    @Override +
-    public Fluid getStill() +
-    { +
-        return Mod.stillAcid; +
-    } +
- +
-    @Override +
-    public boolean matchesType(Fluid fluid_1) +
-    { +
-        return fluid_1==Mod.flowingAcid || fluid_1==Mod.stillAcid; +
-    } +
-     +
-    ... +
-     +
-}     +
-</code> +
- +
-Now we can assert that the Acid class is complete. +
- +
-===== Rendering setup ===== +
- +
-Time to do client-side things. In your **ClientModInitializer** you need to specify locations of sprites for your fluids and define their rendering. I will reuse water textures and just change the color applied to them. +
- +
-<code java> +
-    @Override +
-    public void onInitializeClient() +
-    { +
-         +
-        // adding the sprites to the block texture atlas +
-        ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register((spriteAtlasTexture, registry) -> { +
-         +
-            Identifier stillSpriteLocation = new Identifier("block/water_still"); +
-            Identifier dynamicSpriteLocation = new Identifier("block/water_flow"); +
-            // here I tell to use only 16x16 area of the water texture +
-            FabricSprite stillAcidSprite = new FabricSprite(stillSpriteLocation, 16, 16); +
-            // same, but 32 +
-            FabricSprite dynamicAcidSprite = new FabricSprite(dynamicSpriteLocation, 32, 32); +
-         +
-            registry.register(stillAcidSprite); +
-            registry.register(dynamicAcidSprite); +
-             +
-             +
-            // this renderer is responsible for drawing fluids in a world +
-            FluidRenderHandler acidRenderHandler = new FluidRenderHandler() +
-            { +
-                // return the sprites: still sprite goes first into the array, flowing/dynamic goes last +
-                @Override +
-                public Sprite[] getFluidSprites(ExtendedBlockView extendedBlockView, BlockPos blockPos, FluidState fluidState) +
-                { +
-                    return new Sprite[] {stillAcidSprite, dynamicAcidSprite}; +
-                } +
- +
-                // apply light green color +
-                @Override +
-                public int getFluidColor(ExtendedBlockView view, BlockPos pos, FluidState state) +
-                { +
-                    return 0x4cc248; +
-                } +
-            }; +
- +
-            // registering the same renderer for both fluid variants is intentional +
- +
-            FluidRenderHandlerRegistry.INSTANCE.register(Mod.stillAcid, acidRenderHandler); +
-            FluidRenderHandlerRegistry.INSTANCE.register(Mod.flowingAcid, acidRenderHandler); +
-        }); +
- +
-</code> +
- +
-Then what's left to do is to create necessary Json files and textures, but you should know how to do that at this point. +
- +
-===== Generation in a world ===== +
- +
-To make acid lakes generate in the world, you can use **net.minecraft.world.gen.feature.LakeFeature**, which you create in the ModInitializer: +
-<code java> +
-         +
-        LakeFeature acidFeature = Registry.register(Registry.FEATURE, new Identifier(MODID,"acid_lake"), new LakeFeature(dynamic -> new LakeFeatureConfig(acid.getDefaultState()))); +
- +
-</code> +
-Then put it into desired biomes to generate: +
-<code java> +
-        // I tell it to generate like water lakes, with a rarity of 40 (the higher is the number, the lesser is the generation chance): +
-        Biomes.FOREST.addFeature(GenerationStep.Feature.LOCAL_MODIFICATIONS, Biome.configureFeature(acidFeature, new LakeFeatureConfig(acid.getDefaultState()), Decorator.WATER_LAKE, new LakeDecoratorConfig(40)));+
 </code> </code>
-This is the end of the tutorial. 
  
zh_cn/tutorial/fluids.txt · Last modified: 2023/05/04 11:31 by solidblock