User Tools

Site Tools


tutorial:fluids

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
tutorial:fluids [2019/09/24 11:28] – client side operations (rendering) alexiytutorial:fluids [2022/08/17 22:38] – Fixed small typos clomclem
Line 1: Line 1:
 ====== Creating a fluid ====== ====== Creating a fluid ======
 +
 ===== Overview ===== ===== 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. 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 ===== ===== Making an abstract fluid =====
-Vanilla fluids extend **net.minecraft.fluid.BaseFluid** class, and so will our abstract fluidIt can be like this: +Vanilla fluids extend ''<yarn net.minecraft.class_3609>'', and so shall we
-<code java> +<yarncode java [enable_line_numbers="true"]
-public abstract class BasicFluid extends BaseFluid +public abstract class TutorialFluid extends class_3609 
-+ /** 
-    /** +  * @return whether the given fluid an instance of this fluid 
-     * @return does it produce infinite fluid (like water)? +  */ 
-     */ + @Override 
-    @Override + public boolean method_15780(class_3611 fluid) { 
-    protected boolean isInfinite() + return fluid == method_15751() || fluid == method_15750()
-    + }
-        return false+
-    }+
  
-    // make it transparent + /** 
-    @Override + * @return whether the fluid is infinite like water 
-    protected BlockRenderLayer getRenderLayer() + *
-    + @Override 
-        return BlockRenderLayer.TRANSLUCENT+ protected boolean method_15737() { 
-    }+ return false
 + }
  
-    /** + /** 
-     +  Perform actions when the fluid flows into a replaceable block. Water drops 
-     @return an associated item that "holdsthis fluid +  the block's loot table. Lava plays the "block.lava.extinguishsound. 
-     */ +  */ 
-    @Override + @Override 
-    public abstract Item getBucketItem();+ protected void method_15730(class_1936 world, class_2338 pos, class_2680 state
 + final class_2586 blockEntity = state.method_31709() ? world.method_8321(pos) : null; 
 + class_2248.method_9610(state, world, pos, blockEntity); 
 + }
  
-    /** + /** 
-     +  * Lava returns true if it's FluidState is above a certain height and the 
-     * @return a blockstate of the associated {@linkplain net.minecraft.block.FluidBlock} with {@linkplain net.minecraft.block.FluidBlock#LEVEL} + * Fluid is Water. 
-     */ +  *  
-    @Override +  * @return whether the given Fluid can flow into this FluidState 
-    protected abstract BlockState toBlockState(FluidState var1);+  */ 
 + @Override 
 + protected boolean method_15777(class_3610 fluidState, class_1922 blockView, class_2338 blockPos, class_3611 fluid, class_2350 direction
 + return false; 
 + }
  
-    /** + /** 
-     +  Possibly related to the distance checks for flowing into nearby holes? 
-     @return flowing static instance of this fluid +  Water returns 4. Lava returns 2 in the Overworld and 4 in the Nether. 
-     */ +  */ 
-    @Override + @Override 
-    public abstract Fluid getFlowing();+ protected int method_15733(class_4538 worldView
 + return 4; 
 + }
  
-    /** + /** 
-     +  Water returns 1. Lava returns 2 in the Overworld and 1 in the Nether. 
-     * @return still static instance of this fluid +  */ 
-     */ + @Override 
-    @Override + protected int method_15739(class_4538 worldView
-    public abstract Fluid getStill();+ return 1; 
 + }
  
-    // how much does the height of the fluid block decreases + /** 
-    @Override + * Water returns 5. Lava returns 30 in the Overworld and 10 in the Nether. 
-    protected int getLevelDecreasePerBlock(ViewableWorld var1) + */ 
-    + @Override 
-        return 1+ public int method_15789(class_4538 worldView) { 
-    }+ return 5
 + }
  
-    /** + /** 
-      +  Water and Lava both return 100.0F. 
-     * @return update rate +  */ 
-     */ + @Override 
-    @Override + protected float method_15784() { 
-    public int getTickRate(ViewableWorld var1) + return 100.0F
-    { + }
-        return 5; +
-    } +
- +
-    @Override +
-    protected float getBlastResistance() +
-    +
-        return 100+
-    } +
- +
-    // this seems to determine fluid's spread speed (higher value means faster) +
-    @Override +
-    protected int method_15733(ViewableWorld var1) +
-    { +
-        return 4; +
-    } +
- +
-    // I don't know what this does, but it's present in the water fluid +
-    @Override +
-    protected void beforeBreakingBlock(IWorld iWorld_1, BlockPos blockPos_1, BlockState blockState_1) { +
-        BlockEntity blockEntity_1 = blockState_1.getBlock().hasBlockEntity() ? iWorld_1.getBlockEntity(blockPos_1) : null; +
-        Block.dropStacks(blockState_1, iWorld_1.getWorld(), blockPos_1, blockEntity_1); +
-    } +
- +
-    // also don't know what it does +
-    public boolean method_15777(FluidState fluidState_1, BlockView blockView_1, BlockPos blockPos_1, Fluid fluid_1, Direction direction_1) { +
-        return direction_1 == Direction.DOWN; +
-    } +
- +
-    /** +
-     * +
-     * @return is given fluid instance of this fluid? +
-     */ +
-    @Override +
-    public abstract boolean matchesType(Fluid fluid_1); +
- +
-    /** +
-     * Required for entities to behave in this fluid like in water +
-     */ +
-    @Override +
-    public boolean matches(Tag<Fluid> tag_1) +
-    { +
-        return tag_1 == FluidTags.WATER+
-    }+
 } }
-</code>+</yarncode>
  
 ===== Implementation ===== ===== Implementation =====
-Now let's make an actual fluid; it will have a //still// and //flowing// variantswill name it "Acid":+Now let's make an actual fluid which will have still and flowing variants. For this tutorial, we will call it Acid. The missing references will be filled in shortly.
  
-<code java> +<yarncode java [enable_line_numbers="true"]
-public abstract class Acid extends BasicFluid +public abstract class AcidFluid extends TutorialFluid 
-+ @Override 
-    @Override + public class_3611 method_15751() { 
-    public Item getBucketItem() + return YOUR_STILL_FLUID_HERE
-    { + }
-        return null; +
-    } +
-    @Override +
-    protected BlockState toBlockState(FluidState var1) +
-    +
-        return null+
-    }+
  
-    @Override + @Override 
-    public Fluid getFlowing() + public class_3611 method_15750() { 
-    + return YOUR_FLOWING_FLUID_HERE
-        return null+ }
-    }+
  
-    @Override + @Override 
-    public Fluid getStill() + public class_1792 method_15774() { 
-    + return YOUR_BUCKET_ITEM_HERE
-        return null+ }
-    }+
  
-    @Override + @Override 
-    public boolean matchesType(Fluid fluid_1) + protected class_2680 method_15790(class_3610 fluidState) { 
-    + return YOUR_FLUID_BLOCK_HERE.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState))
-        return false+ }
-    }+
  
-    // still acid + public static class Flowing extends AcidFluid { 
-    public static class Still extends Acid + @Override 
-    {+ protected void method_15775(class_2689.class_2690<class_3611, class_3610> builder) { 
 + super.method_15775(builder); 
 + builder.method_11667(field_15900); 
 + }
  
-        @Override + @Override 
-        public boolean isStill(FluidState fluidState) + public int method_15779(class_3610 fluidState) { 
-        + return fluidState.method_11654(field_15900)
-            return true+ }
-        }+
  
-        /** + @Override 
-         * @return height of the fluid block + public boolean method_15793(class_3610 fluidState) { 
-         */ + return false
-        @Override +
-        public int getLevel(FluidState fluidState) + }
-        +
-            return 8+
-        +
-    }+
  
-    // flowing acid + public static class Still extends AcidFluid { 
-    public static class Flowing extends  Acid + @Override 
-    {+ public int method_15779(class_3610 fluidState) { 
 + return 8; 
 + }
  
-        @Override + @Override 
-        public boolean isStill(FluidState fluidState) + public boolean method_15793(class_3610 fluidState) { 
-        + return true
-            return false+ } 
-        }+
 +
 +</yarncode>
  
-        /** +Next, we'll make static instances of still and flowing acid variants, and an acid bucketIn your ''ModInitializer'':
-         * @return height of the fluid block +
-         */ +
-        @Override +
-        public int getLevel(FluidState fluidState) +
-        { +
-            return fluidState.get(LEVEL); +
-        }+
  
-        @Override +<yarncode java [enable_line_numbers="true"]> 
-        protected void appendProperties(StateFactory.Builder<Fluid, FluidState> stateFactory$Builder_1) +public static class_3609 STILL_ACID; 
-        +public static class_3609 FLOWING_ACID; 
-            super.appendProperties(stateFactory$Builder_1); +public static class_1792 ACID_BUCKET; 
-            stateFactory$Builder_1.add(LEVEL); +  
-        } +@Override 
-    }+public void onInitialize() { 
 + STILL_ACID = class_2378.method_10230(class_2378.field_11154, new class_2960(MOD_ID, "acid"), new AcidFluid.Still()); 
 + FLOWING_ACID = class_2378.method_10230(class_2378.field_11154, new class_2960(MOD_ID, "flowing_acid"), new AcidFluid.Flowing()); 
 + ACID_BUCKET = class_2378.method_10230(class_2378.field_11142, new class_2960(MOD_ID, "acid_bucket"),  
 +        new class_1755(STILL_ACID, new class_1792.class_1793().method_7896(class_1802.field_8550).method_7889(1))); 
 +  
 + // ... 
 +} 
 +  
 +// ... 
 +</yarncode> 
 + 
 +To make a custom fluid behave more like water or lava, you must add it to a corresponding fluid tag: For water, make a ''data/minecraft/tags/fluids/water.json'' file and write the identifiers of your fluids in there: 
 +<code json [enable_line_numbers="true"]> 
 +
 + "replace": false, 
 + "values": 
 +
 + "your_mod_id:acid", 
 + "your_mod_id:flowing_acid" 
 + ]
 } }
 </code> </code>
  
-Next, we'll make static instances of still and flowing acid variants, and an acid bucket. In your **ModInitializer**:+===== Making a fluid block ===== 
 +Next we need to create a block which will represent acid in the world. ''<yarn net.minecraft.class_2404>'' is the class we need to use, but since its constructor is protected, we can't construct it directly. Some ways to use it are to make a subclass or an anonymous subclass. Here we will be showing the latter. In your ''ModInitializer'':
  
-<code java>+<yarncode java [enable_line_numbers="true"]> 
 +public static class_2248 ACID;
  
-     +@Override 
-    public static Acid stillAcid; +public void onInitialize() { 
-    public static Acid flowingAcid+ ACID = class_2378.method_10230(class_2378.field_11146, new class_2960(MOD_ID, "acid"), new class_2404(STILL_ACID, FabricBlockSettings.method_9630(class_2246.field_10382)){})
-     +  
-    public static BucketItem acidBucket;+ // ... 
 +}  
 +</yarncode>
  
-    @Override +Now that we have these static objectswe can go back to ''AcidFluid'' and fill in the overridden methods:
-    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>+
  
-==== Making a fluid block ==== +<yarncode java [enable_line_numbers="true"]> 
-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 abstract class AcidFluid extends TutorialFluid { 
 + @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 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses 
 + return TutorialMod.ACID.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState)); 
 + }
  
-<code java> + public static class Flowing extends AcidFluid 
-public class BaseFluidBlock extends FluidBlock + @Override 
-+ protected void method_15775(class_2689.class_2690<class_3611class_3610> builder) { 
-    public BaseFluidBlock(BaseFluid fluidSettings settings) + super.method_15775(builder); 
-    + builder.method_11667(field_15900); 
-        super(fluid, settings); + }
-    } +
-} +
-</code>+
  
-Now make a static block instance:+ @Override 
 + public int method_15779(class_3610 fluidState) { 
 + return fluidState.method_11654(field_15900); 
 + }
  
-<code java> + @Override 
-    ... + public boolean method_15793(class_3610 fluidState) { 
-     + return false; 
-    public static FluidBlock acid;+
 + }
  
-    @Override + public static class Still extends AcidFluid { 
-    public void onInitialize() + @Override 
-    + public int method_15779(class_3610 fluidState) { 
-     + return 8
-        ... + }
-         +
-        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:+ @Override 
 + public boolean method_15793(class_3610 fluidState) { 
 + return true; 
 +
 +
 +}   
 +</yarncode>
  
-<code java> +===== Rendering setup ===== 
-public abstract class Acid extends BasicFluid +For your fluids to have textures or be tinted with a color, you will need to register a ''FluidRenderHandler'' for themHere, we will reuse water's textures and just change the tint color applied to themTo make sure the textures are rendered as translucent, you can use Fabric's ''BlockRenderLayerMap''.
-+
-    @Override +
-    public Item getBucketItem() +
-    { +
-        return [ModInitializer].acidBucket; +
-    } +
-     +
-    @Override +
-    protected BlockState toBlockState(FluidState fluidState) +
-    { +
-        //don't ask me what **method_15741** does..+
-        return [ModInitializer].acid.getDefaultState().with(FluidBlock.LEVEL,method_15741(fluidState)); +
-    }+
  
-    @Override +<yarncode java [enable_line_numbers="true"]> 
-    public Fluid getFlowing() +public class TutorialModClient implements ClientModInitializer {
-    { +
-        return [ModInitializer].flowingAcid; +
-    }+
  
-    @Override + @Override 
-    public Fluid getStill() + public void onInitializeClient() { 
-    + FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new SimpleFluidRenderHandler( 
-        return [ModInitializer].stillAcid; + new class_2960("minecraft:block/water_still"), 
-    }+ new class_2960("minecraft:block/water_flow"), 
 + 0x4CC248 
 + ));
  
-    @Override + BlockRenderLayerMap.INSTANCE.putFluids(class_1921.method_23583(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID);
-    public boolean matchesType(Fluid fluid_1) +
-    { +
-        return fluid_1==[ModInitializer].flowingAcid || fluid_1==[ModInitializer].stillAcid; +
-    } +
-     +
-    ... +
-     +
-}     +
-</code>+
  
-Now we can assert that the Acid class is complete.+ //if you want to use custom textures they needs to be registered. 
 + //In this example this is unnecessary because the vanilla water textures are already registered. 
 + //To register your custom textures use this method. 
 + //ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).register((atlasTexture, registry) -> { 
 + //    registry.register(new Identifier("modid:block/custom_fluid_still")); 
 + //    registry.register(new Identifier("modid:block/custom_fluid_flowing")); 
 + //});
  
-===== Rendering setup =====+ // ... 
 +
 +
 +</yarncode>
  
-Time to do client-side things. In your **ClientModInitializer** you need to specify locations of sprites for your fluids and define their renderingI will reuse water textures and just change the color applied to them.+If you want to use your own fluid textures, you can refer to vanilla's assets ((''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'')) as a template.
  
-<code java> +===== Generation in the world ===== 
-    @Override +To make lakes of acid generate in the world, you can create a ''<yarn net.minecraft.class_3085>'' in your ''ModInitializer'' and then add it to the biomes you want it to generate in:
-    public void onInitializeClient() +
-    { +
-        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); +
-         +
-        // adding the sprites to the block texture atlas +
-        ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register((spriteAtlasTexture, registry) -+
-            registry.register(stillAcidSprite); +
-            registry.register(dynamicAcidSprite); +
-        });+
  
-        // this renderer is responsible for drawing fluids in a world +<code java [enable_line_numbers="true"]> 
-        FluidRenderHandler acidRenderHandler=new FluidRenderHandler() +public static LakeFeature ACID_LAKE;
-        { +
-            // 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 
-            @Override +public void onInitialize() { 
-            public int getFluidColor(ExtendedBlockView viewBlockPos posFluidState state+ ACID_LAKE = Registry.register(Registry.FEATUREnew Identifier(MOD_ID"acid_lake"), new LakeFeature(SingleStateFeatureConfig::deserialize)); 
-            { +  
-                return 0x4cc248+ // generate in swamps, similar to water lakes, but with a chance of 40 (the higher the number, the lower the generation chance) 
-            + Biomes.SWAMP.addFeature( 
-        };+ GenerationStep.Feature.LOCAL_MODIFICATIONS, 
 + ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) 
 + .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))) 
 + )
 +
 +</code>
  
-        // registering the same renderer for both fluid variants is intentional 
- 
-        FluidRenderHandlerRegistry.INSTANCE.register([ModInitializer].stillAcid,acidRenderHandler); 
-        FluidRenderHandlerRegistry.INSTANCE.register([ModInitializer].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. 
tutorial/fluids.txt · Last modified: 2023/05/04 11:31 by solidblock