zh_cn:tutorial:fluids
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
zh_cn:tutorial:fluids [2019/12/19 10:19] – lightcolour | zh_cn:tutorial:fluids [2023/05/04 11:27] – [制作一个流体方块] solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== 创建流体 ====== | ====== 创建流体 ====== | ||
- | ===== Overview | + | ===== 概述 |
- | 在这里,我们将介绍自定义流体的创建。 如果计划创建多个流体,建议创建一个抽象的基本流体类,在其中设置必要的默认值,这些默认值将在其子类中共享。 我们还将使其像湖泊一样在世界上产生。 | + | 在这里,我们将介绍自定义流体的创建。如果计划创建多个流体,建议创建一个抽象的基本流体类,在其中设置必要的默认值,这些默认值将在其子类中共享。我们还将使其像湖泊一样在世界中生成。 |
- | ===== 使抽象流畅 ===== | + | ===== 创建抽象流体 ===== |
- | 香草流体扩展了**net.minecraft.fluid.BaseFluid**类,我们的抽象流体也将扩展。 可能是这样的: | + | 原版流体继承了 ''< |
- | <code java> | + | <yarncode |
- | public abstract class BasicFluid | + | public abstract class TutorialFluid |
- | { | + | /** |
- | /** | + | * @return |
- | | + | */ |
- | | + | @Override |
- | @Override | + | public boolean method_15780(class_3611 |
- | protected boolean | + | return fluid == getStill() || fluid == getFlowing(); |
- | { | + | } |
- | return false; | + | |
- | } | + | /** |
+ | * @return 流体是否可以像无限刷水的方法一样无限生成?在原版,这取决于游戏规则。 | ||
+ | */ | ||
+ | @Override | ||
+ | protected boolean | ||
+ | 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, | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 熔岩在其 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; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
- | // make it transparent | + | ===== 实现 ===== |
- | | + | 现在让我们制作一个拥有静止和流动两个变种的实际流体。在此教程中,我们将其称为“酸”。缺失的引用稍后补全。 |
- | protected BlockRenderLayer getRenderLayer() | + | |
- | { | + | |
- | return BlockRenderLayer.TRANSLUCENT; | + | |
- | } | + | |
- | /** | + | < |
- | * | + | public abstract class AcidFluid extends TutorialFluid { |
- | * @return an associated item that "holds" | + | @Override |
- | */ | + | public |
- | @Override | + | return YOUR_STILL_FLUID_HERE; |
- | public | + | } |
- | /** | + | @Override |
- | * | + | public class_3611 method_15750() { |
- | * @return a blockstate of the associated {@linkplain net.minecraft.block.FluidBlock} with {@linkplain net.minecraft.block.FluidBlock# | + | return YOUR_FLOWING_FLUID_HERE; |
- | */ | + | } |
- | | + | |
- | | + | |
- | /** | + | @Override |
- | * | + | public |
- | * @return flowing static instance of this fluid | + | return YOUR_BUCKET_ITEM_HERE; |
- | */ | + | } |
- | | + | |
- | public | + | |
- | /** | + | @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, |
- | */ | + | } |
- | | + | |
- | | + | |
- | // how much does the height of the fluid block decreases | + | public static class Flowing extends AcidFluid { |
- | @Override | + | @Override |
- | protected | + | protected |
- | | + | super.method_15775(builder); |
- | | + | builder.method_11667(field_15900); |
- | } | + | } |
- | /** | + | @Override |
- | * | + | public int method_15779(class_3610 fluidState) { |
- | * @return update rate | + | return |
- | */ | + | } |
- | | + | |
- | public int getTickRate(ViewableWorld world) | + | |
- | | + | |
- | return | + | |
- | } | + | |
- | | + | @Override |
- | | + | public boolean method_15793(class_3610 fluidState) { |
- | | + | return |
- | return | + | } |
- | } | + | } |
- | // this seems to determine fluid' | + | public static class Still extends AcidFluid { |
- | @Override | + | @Override |
- | | + | public |
- | | + | return |
- | return | + | } |
- | } | + | |
- | // I don't know what this does, but it's present in the water fluid | + | @Override |
- | @Override | + | public boolean method_15793(class_3610 fluidState) { |
- | | + | return true; |
- | | + | } |
- | Block.dropStacks(blockState, world.getWorld(), blockPos, blockEntity); | + | } |
- | } | + | } |
- | + | </yarncode> | |
- | // also don't know what it does | + | 接下来,我们将制作静态和动态酸变体的静态实例,以及一个酸桶。在您的 |
- | public boolean method_15777(FluidState fluidState, BlockView blockView, BlockPos blockPos, Fluid fluid, Direction direction) { | + | < |
- | | + | 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 |
- | */ | + | STILL_ACID |
- | | + | FLOWING_ACID = class_2378.method_10230(class_7923.field_41173, |
- | public abstract boolean matchesType(Fluid fluid); | + | ACID_BUCKET = class_2378.method_10230(class_7923.field_41178, new class_2960(" |
+ | | ||
+ | |||
+ | // ... | ||
+ | } | ||
+ | |||
+ | // ... | ||
+ | </ | ||
+ | 为了使自定义流体表现得像水或熔岩,您必须将其添加到相应的流体标签中:对于水,制作 '' | ||
+ | <code json [enable_line_numbers=" | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | [ | ||
+ | " | ||
+ | " | ||
+ | ] | ||
} | } | ||
</ | </ | ||
- | ===== Implementation ===== | ||
- | Now let's make an actual fluid; it will have a //still// and //flowing// variants; will name it " | ||
- | <code java> | + | ==== 制作一个流体方块 ==== |
- | public | + | 接下来,我们需要在世界中创建表示酸的方块。'' |
- | { | + | < |
- | @Override | + | public |
- | public Item getBucketItem() | + | |
- | { | + | |
- | return null; | + | |
- | } | + | |
- | @Override | + | |
- | protected BlockState toBlockState(FluidState var1) | + | |
- | { | + | |
- | return null; | + | |
- | } | + | |
- | | + | @Override |
- | public | + | public |
- | | + | ACID = class_2378.method_10230(class_7923.field_41175, |
- | | + | |
- | } | + | // ... |
+ | } | ||
+ | </ | ||
- | | + | 既然我们有了这些静态对象,我们回到 '' |
- | public | + | < |
- | { | + | public abstract class AcidFluid extends TutorialFluid { |
- | return | + | @Override |
- | } | + | public |
+ | return TutorialMod.STILL_ACID; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public class_3611 method_15750() | ||
+ | return | ||
+ | } | ||
+ | |||
+ | @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, | ||
+ | } | ||
- | | + | public static class Flowing extends AcidFluid { |
- | | + | @Override |
- | | + | protected void method_15775(class_2689.class_2690< |
- | | + | super.method_15775(builder); |
- | } | + | builder.method_11667(field_15900); |
+ | } | ||
- | // still acid | + | @Override |
- | public | + | public |
- | | + | return fluidState.method_11654(field_15900); |
+ | } | ||
- | | + | @Override |
- | public boolean | + | public boolean |
- | | + | return |
- | return | + | } |
- | } | + | } |
- | /** | + | public static class Still extends AcidFluid { |
- | * @return height of the fluid block | + | @Override |
- | */ | + | public int method_15779(class_3610 |
- | | + | return 8; |
- | public int getLevel(FluidState | + | } |
- | | + | |
- | return 8; | + | |
- | } | + | |
- | | + | |
- | // flowing acid | + | @Override |
- | public | + | public |
- | { | + | return true; |
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ===== 渲染设置 ===== | ||
+ | 为了让流体拥有纹理,或者与一个颜色绑定,你需要为其注册一个'' | ||
- | | + | <code java [enable_line_numbers=" |
- | public | + | public class TutorialModClient implements ClientModInitializer |
- | { | + | { |
- | return | + | // ... |
- | } | + | |
- | + | @Override | |
- | /** | + | public |
- | | + | { |
- | | + | // ... |
- | @Override | + | |
- | public | + | setupFluidRendering(TutorialMod.STILL_ACID, |
- | { | + | BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), |
- | | + | |
- | } | + | // ... |
- | + | } | |
- | @Override | + | |
- | | + | // ... |
- | { | + | |
- | super.appendProperties(stateFactoryBuilder); | + | public static void setupFluidRendering(final Fluid still, final Fluid flowing, final Identifier textureFluidId, |
- | | + | { |
- | } | + | final Identifier stillSpriteId = new Identifier(textureFluidId.getNamespace(), |
- | } | + | final Identifier flowingSpriteId = new Identifier(textureFluidId.getNamespace(), |
+ | |||
+ | // If they' | ||
+ | ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register((atlasTexture, | ||
+ | { | ||
+ | registry.register(stillSpriteId); | ||
+ | registry.register(flowingSpriteId); | ||
+ | }); | ||
+ | |||
+ | final Identifier fluidId = Registry.FLUID.getId(still); | ||
+ | final Identifier listenerId = new Identifier(fluidId.getNamespace(), | ||
+ | |||
+ | final Sprite[] fluidSprites = { null, null }; | ||
+ | |||
+ | ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() | ||
+ | { | ||
+ | @Override | ||
+ | public Identifier getFabricId() | ||
+ | { | ||
+ | return | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Get the sprites from the block atlas when resources are reloaded | ||
+ | */ | ||
+ | @Override | ||
+ | public | ||
+ | { | ||
+ | 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 Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState | ||
+ | { | ||
+ | return fluidSprites; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public int getFluidColor(BlockRenderView view, BlockPos pos, FluidState state) | ||
+ | { | ||
+ | return color; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | FluidRenderHandlerRegistry.INSTANCE.register(still, renderHandler); | ||
+ | FluidRenderHandlerRegistry.INSTANCE.register(flowing, renderHandler); | ||
+ | } | ||
+ | |||
+ | // ... | ||
} | } | ||
</ | </ | ||
- | Next, we'll make static instances of still and flowing acid variants, and an acid bucket. In your **ModInitializer**: | + | 如果你需要使用你自己的流体纹理,你可以参考原版资源包(('' |
- | <code java> | + | ===== 在世界中生成 ===== |
+ | 为使得酸湖在世界中生成,你可以在你的'' | ||
- | | + | <code java [enable_line_numbers=" |
- | | + | // ... |
- | public static Acid flowingAcid; | + | |
- | + | ||
- | public static BucketItem acidBucket; | + | |
- | @Override | + | public |
- | | + | |
- | { | + | |
- | + | ||
- | stillAcid = Registry.register(Registry.FLUID, | + | |
- | flowingAcid = Registry.register(Registry.FLUID, | + | |
- | + | ||
- | acidBucket = new BucketItem(stillAcid, | + | |
- | Registry.register(Registry.ITEM, | + | |
- | } | + | |
- | </ | + | |
- | 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/ | + | // ... |
- | <code json> | + | |
- | { | + | |
- | " | + | |
- | " | + | |
- | " | + | |
- | " | + | |
- | ] | + | |
- | } | + | |
- | </ | + | |
- | ==== 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 " | + | public |
- | + | ||
- | <code java> | + | |
- | public | + | |
{ | { | ||
- | public BaseFluidBlock(BaseFluid fluid, Settings settings) | + | // ... |
- | { | + | |
- | super(fluid, settings); | + | ACID_LAKE = Registry.register(Registry.FEATURE, new Identifier(MOD_ID, |
- | } | + | |
+ | // 在沼泽中生成,类似于水湖,但是概率为40(数字越高,生成几率越低) | ||
+ | Biomes.SWAMP.addFeature( | ||
+ | GenerationStep.Feature.LOCAL_MODIFICATIONS, | ||
+ | ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) | ||
+ | .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))) | ||
+ | ); | ||
+ | |||
+ | // ... | ||
} | } | ||
- | </ | ||
- | Now make a static block instance: | + | // ... |
- | + | ||
- | <code java> | + | |
- | ... | + | |
- | + | ||
- | public static FluidBlock acid; | + | |
- | + | ||
- | @Override | + | |
- | public void onInitialize() | + | |
- | { | + | |
- | + | ||
- | ... | + | |
- | + | ||
- | acid = new BaseFluidBlock(stillAcid, | + | |
- | Registry.register(Registry.BLOCK, | + | |
- | } | + | |
- | </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) | + | |
- | { | + | |
- | | + | |
- | return Mod.acid.getDefaultState().with(FluidBlock.LEVEL, | + | |
- | } | + | |
- | + | ||
- | @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; | + | |
- | } | + | |
- | + | ||
- | ... | + | |
- | + | ||
- | } | + | |
- | </ | + | |
- | + | ||
- | 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, | + | |
- | + | ||
- | Identifier stillSpriteLocation = new Identifier(" | + | |
- | Identifier dynamicSpriteLocation = new Identifier(" | + | |
- | // here I tell to use only 16x16 area of the water texture | + | |
- | FabricSprite stillAcidSprite = new FabricSprite(stillSpriteLocation, | + | |
- | // same, but 32 | + | |
- | FabricSprite dynamicAcidSprite = new FabricSprite(dynamicSpriteLocation, | + | |
- | + | ||
- | 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/ | + | |
- | @Override | + | |
- | public Sprite[] getFluidSprites(ExtendedBlockView extendedBlockView, | + | |
- | { | + | |
- | return new Sprite[] {stillAcidSprite, | + | |
- | } | + | |
- | + | ||
- | // 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, | + | |
- | FluidRenderHandlerRegistry.INSTANCE.register(Mod.flowingAcid, | + | |
- | }); | + | |
- | + | ||
- | </ | + | |
- | + | ||
- | 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**, | + | |
- | <code java> | + | |
- | + | ||
- | LakeFeature acidFeature = Registry.register(Registry.FEATURE, | + | |
- | + | ||
- | </ | + | |
- | 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, | + | |
</ | </ | ||
- | This is the end of the tutorial. | ||
zh_cn/tutorial/fluids.txt · Last modified: 2023/05/04 11:31 by solidblock