tutorial:fluids
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:fluids [2021/11/08 18:30] – Removed the Advanced customization section fusion_flux | tutorial:fluids [2026/02/23 14:26] (current) – fixed reference infinitychances | ||
|---|---|---|---|
| Line 5: | Line 5: | ||
| ===== Making an abstract fluid ===== | ===== Making an abstract fluid ===== | ||
| - | Vanilla fluids extend '' | + | Vanilla fluids extend '' |
| - | <yarncode | + | <code java [enable_line_numbers=" |
| - | public abstract class TutorialFluid extends | + | public abstract class TutorialFluid extends |
| /** | /** | ||
| * @return whether the given fluid an instance of this fluid | * @return whether the given fluid an instance of this fluid | ||
| */ | */ | ||
| @Override | @Override | ||
| - | public boolean | + | public boolean |
| - | return fluid == method_15751() || fluid == method_15750(); | + | return fluid == getSource() || fluid == getFlowing(); |
| } | } | ||
| /** | /** | ||
| - | * @return whether the fluid infinite like water | + | * @return whether the fluid is infinite |
| */ | */ | ||
| @Override | @Override | ||
| - | protected boolean | + | protected boolean |
| return false; | return false; | ||
| } | } | ||
| /** | /** | ||
| - | * Perform actions when fluid flows into a replaceable block. Water drops | + | * Perform actions when the fluid flows into a replaceable block. Water drops |
| * the block' | * the block' | ||
| */ | */ | ||
| @Override | @Override | ||
| - | protected void method_15730(class_1936 world, class_2338 | + | protected void beforeDestroyingBlock(LevelAccessor level, BlockPos |
| - | final | + | final |
| - | class_2248.method_9610(state, | + | Block.dropResources(state, |
| } | } | ||
| /** | /** | ||
| - | * Lava returns true if its FluidState is above a certain height and the | + | * Lava returns true if it' |
| * Fluid is Water. | * Fluid is Water. | ||
| * | * | ||
| Line 41: | Line 41: | ||
| */ | */ | ||
| @Override | @Override | ||
| - | protected boolean | + | protected boolean |
| return false; | return false; | ||
| } | } | ||
| Line 50: | Line 50: | ||
| */ | */ | ||
| @Override | @Override | ||
| - | protected int method_15733(class_4538 worldView) { | + | protected int getSlopeFindDistance(LevelReader reader) { |
| return 4; | return 4; | ||
| } | } | ||
| /** | /** | ||
| - | * Water returns 1. Lava returns 2 in the Overworld | + | * Water returns 1. Lava returns 2 in the overworld |
| + | * @return How many levels a fluid loses per block. | ||
| */ | */ | ||
| @Override | @Override | ||
| - | protected int method_15739(class_4538 worldView) { | + | protected int getDropOff(LevelReader reader) { |
| return 1; | return 1; | ||
| } | } | ||
| Line 64: | Line 65: | ||
| /** | /** | ||
| * Water returns 5. Lava returns 30 in the Overworld and 10 in the Nether. | * Water returns 5. Lava returns 30 in the Overworld and 10 in the Nether. | ||
| + | * Seems to return the delay before it updates its state. | ||
| */ | */ | ||
| @Override | @Override | ||
| - | public int method_15789(class_4538 worldView) { | + | public int getTickDelay(LevelReader reader) { |
| return 5; | return 5; | ||
| } | } | ||
| Line 74: | Line 76: | ||
| */ | */ | ||
| @Override | @Override | ||
| - | protected float method_15784() { | + | protected float getExplosionResistance() { |
| return 100.0F; | return 100.0F; | ||
| } | } | ||
| } | } | ||
| - | </yarncode> | + | </code> |
| ===== Implementation ===== | ===== Implementation ===== | ||
| 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. | 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. | ||
| - | <yarncode | + | <code java [enable_line_numbers=" |
| public abstract class AcidFluid extends TutorialFluid { | public abstract class AcidFluid extends TutorialFluid { | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| return YOUR_FLOWING_FLUID_HERE; | return YOUR_FLOWING_FLUID_HERE; | ||
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| return YOUR_BUCKET_ITEM_HERE; | return YOUR_BUCKET_ITEM_HERE; | ||
| } | } | ||
| @Override | @Override | ||
| - | protected | + | protected |
| - | return YOUR_FLUID_BLOCK_HERE.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState)); | + | return YOUR_FLUID_BLOCK_HERE.defaultBlockState().setValue(BlockStateProperties.LEVEL, getLegacyLevel(state)); |
| } | } | ||
| public static class Flowing extends AcidFluid { | public static class Flowing extends AcidFluid { | ||
| @Override | @Override | ||
| - | protected void method_15775(class_2689.class_2690<class_3611, class_3610> builder) { | + | protected void createFluidStateDefinition(StateDefinition.Builder<Fluid, FluidState> builder) { |
| - | super.method_15775(builder); | + | super.createFluidStateDefinition(builder); |
| - | builder.method_11667(field_15900); | + | builder.add(LEVEL); |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| } | } | ||
| Line 125: | Line 127: | ||
| public static class Still extends AcidFluid { | public static class Still extends AcidFluid { | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| } | } | ||
| } | } | ||
| - | </yarncode> | + | </code> |
| Next, we'll make static instances of still and flowing acid variants, and an acid bucket. In your '' | Next, we'll make static instances of still and flowing acid variants, and an acid bucket. In your '' | ||
| - | <yarncode | + | <code java [enable_line_numbers=" |
| - | public static | + | public static |
| - | public static | + | public static |
| - | public static | + | public static |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| - | STILL_ACID | + | ACID_SOURCE |
| - | FLOWING_ACID = class_2378.method_10230(class_2378.field_11154, new class_2960(MOD_ID, " | + | FLOWING_ACID = |
| - | ACID_BUCKET = class_2378.method_10230(class_2378.field_11142, new class_2960(MOD_ID, " | + | ACID_BUCKET = Registry.register(BuiltInRegistries.ITEM, Identifier.fromNamespaceAndPath(" |
| - | new class_1755(STILL_ACID, new class_1792.class_1793().method_7896(class_1802.field_8550).method_7889(1))); | + | new BucketItem(ACID_SOURCE, |
| // ... | // ... | ||
| Line 155: | Line 157: | ||
| // ... | // ... | ||
| - | </yarncode> | + | </code> |
| - | To make a custom fluid behave more like water or lava, you must add it to a corresponding fluid tag: For water, make a '' | + | To make a custom fluid behave more like water or lava, you must add it to a corresponding fluid tag: For water, make a '' |
| <code json [enable_line_numbers=" | <code json [enable_line_numbers=" | ||
| { | { | ||
| Line 163: | Line 165: | ||
| " | " | ||
| [ | [ | ||
| - | "your_mod_id: | + | "tutorial: |
| - | "your_mod_id: | + | "tutorial: |
| ] | ] | ||
| } | } | ||
| Line 170: | Line 172: | ||
| ===== Making a fluid block ===== | ===== Making a fluid block ===== | ||
| - | Next we need to create a block which will represent acid in the world. '' | + | Next we need to create a block which will represent acid in the world. '' |
| - | <yarncode | + | <code java [enable_line_numbers=" |
| - | public static | + | public static |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| - | ACID = class_2378.method_10230(class_2378.field_11146, new class_2960(MOD_ID, " | + | ACID = Registry.register(BuiltInRegistries.BLOCK, Identifier.fromNamespaceAndPath(" |
| // ... | // ... | ||
| } | } | ||
| - | </yarncode> | + | </code> |
| Now that we have these static objects, we can go back to '' | Now that we have these static objects, we can go back to '' | ||
| - | <yarncode | + | <code java [enable_line_numbers=" |
| public abstract class AcidFluid extends TutorialFluid { | public abstract class AcidFluid extends TutorialFluid { | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return TutorialMod.STILL_ACID; | + | return TutorialMod.ACID_SOURCE; |
| } | } | ||
| - | + | ||
| @Override | @Override | ||
| - | public | + | public |
| return TutorialMod.FLOWING_ACID; | return TutorialMod.FLOWING_ACID; | ||
| } | } | ||
| - | + | ||
| @Override | @Override | ||
| - | public | + | public |
| return TutorialMod.ACID_BUCKET; | return TutorialMod.ACID_BUCKET; | ||
| } | } | ||
| - | + | ||
| @Override | @Override | ||
| - | protected | + | protected |
| - | // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses | + | return TutorialMod.ACID.defaultBlockState().setValue(BlockStateProperties.LEVEL, getLegacyLevel(state)); |
| - | return TutorialMod.ACID.method_9564().method_11657(class_2741.field_12538, method_15741(fluidState)); | + | |
| } | } | ||
| public static class Flowing extends AcidFluid { | public static class Flowing extends AcidFluid { | ||
| @Override | @Override | ||
| - | protected void method_15775(class_2689.class_2690<class_3611, class_3610> builder) { | + | protected void createFluidStateDefinition(StateDefinition.Builder<Fluid, FluidState> builder) { |
| - | super.method_15775(builder); | + | super.createFluidStateDefinition(builder); |
| - | builder.method_11667(field_15900); | + | builder.add(LEVEL); |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| } | } | ||
| Line 228: | Line 229: | ||
| public static class Still extends AcidFluid { | public static class Still extends AcidFluid { | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | return | + | return |
| } | } | ||
| } | } | ||
| - | } | + | } |
| - | </yarncode> | + | </code> |
| ===== Rendering setup ===== | ===== Rendering setup ===== | ||
| - | For your fluids to have textures or be tinted with a color, you will need to register a '' | + | For your fluids to have textures or be tinted with a color, you will need to register a '' |
| - | <yarncode | + | <code java [enable_line_numbers=" |
| + | @Environment(EnvType.CLIENT) | ||
| public class TutorialModClient implements ClientModInitializer { | public class TutorialModClient implements ClientModInitializer { | ||
| @Override | @Override | ||
| public void onInitializeClient() { | public void onInitializeClient() { | ||
| - | setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, | + | FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID, |
| - | BlockRenderLayerMap.INSTANCE.putFluids(class_1921.method_23583(), | + | |
| - | // ... | + | BlockRenderLayerMap.putFluids(ChunkSectionLayer.TRANSLUCENT, |
| } | } | ||
| + | } | ||
| + | </ | ||
| + | ===== Adding custom textures to your fluid ===== | ||
| + | To create a fluid with custom textures, you will need at least 2 new textures, with an optional 3rd, overlay texture that is shown when you look at the fluid behind glass or leaves. All textures go in the block textures folder. | ||
| - | public static void setupFluidRendering(final class_3611 still, final class_3611 flowing, final class_2960 textureFluidId, | + | The first texture should be named YOUR_FLUID_NAME_still.png, and can optionally be animated. |
| - | final class_2960 stillSpriteId = new class_2960(textureFluidId.method_12836(), " | + | The second texture should be named YOUR_FLUID_NAME_flowing.png, and should have double the size in the dimensions. For example, if your still texture has frames of 16x16, the flowing should have frames of 32x32. It can also be animated. |
| - | final class_2960 flowingSpriteId = new class_2960(textureFluidId.method_12836(), " | + | The third will be named YOUR_FLUID_NAME_overlay.png, and should not be animated. |
| - | // If they' | + | Using our acid example, the client initializer will look like this: |
| - | ClientSpriteRegistryCallback.event(class_1059.field_5275).register((atlasTexture, | + | <code java [enable_line_numbers=" |
| - | registry.register(stillSpriteId); | + | @Environment(EnvType.CLIENT) |
| - | registry.register(flowingSpriteId); | + | public class TutorialModClient implements ClientModInitializer { |
| - | }); | + | |
| - | final class_2960 fluidId = class_2378.field_11154.method_10221(still); | + | @Override |
| - | final class_2960 listenerId = new class_2960(fluidId.method_12836(), fluidId.method_12832() + "_reload_listener"); | + | public void onInitializeClient() { |
| + | FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.ACID_SOURCE, | ||
| + | Identifier.fromNamespaceAndPath(" | ||
| + | Identifier.fromNamespaceAndPath(" | ||
| + | Identifier.fromNamespaceAndPath("tutorial", " | ||
| - | final class_1058[] fluidSprites = { null, null }; | + | BlockRenderLayerMap.putFluids(ChunkSectionLayer.TRANSLUCENT, TutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID); |
| - | + | ||
| - | ResourceManagerHelper.get(class_3264.field_14188).registerReloadListener(new SimpleSynchronousResourceReloadListener() { | + | |
| - | @Override | + | |
| - | public class_2960 getFabricId() { | + | |
| - | return listenerId; | + | |
| - | } | + | |
| - | + | ||
| - | /** | + | |
| - | * Get the sprites from the block atlas when resources are reloaded | + | |
| - | */ | + | |
| - | @Override | + | |
| - | public void method_14491(class_3300 resourceManager) { | + | |
| - | final Function< | + | |
| - | 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 class_1058[] getFluidSprites(class_1920 view, class_2338 pos, class_3610 state) { | + | |
| - | return fluidSprites; | + | |
| - | } | + | |
| - | + | ||
| - | @Override | + | |
| - | public int getFluidColor(class_1920 view, class_2338 pos, class_3610 state) { | + | |
| - | return color; | + | |
| - | } | + | |
| - | }; | + | |
| - | + | ||
| - | FluidRenderHandlerRegistry.INSTANCE.register(still, | + | |
| - | FluidRenderHandlerRegistry.INSTANCE.register(flowing, | + | |
| } | } | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | If you want to use your own fluid textures, you can refer to vanilla' | ||
| - | |||
| - | ===== Generation in the world ===== | ||
| - | To make lakes of acid generate in the world, you can create a ''< | ||
| - | |||
| - | <code java [enable_line_numbers=" | ||
| - | public static LakeFeature ACID_LAKE; | ||
| - | |||
| - | @Override | ||
| - | public void onInitialize() { | ||
| - | ACID_LAKE = Registry.register(Registry.FEATURE, | ||
| - | |||
| - | // 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))) | ||
| - | ); | ||
| } | } | ||
| </ | </ | ||
| + | You can refer to vanilla' | ||
tutorial/fluids.1636396234.txt.gz · Last modified: 2021/11/08 18:30 by fusion_flux