User Tools

Site Tools


zh_cn:tutorial:fluids

This is an old revision of the document!


创建流体

概述

在这里,我们将介绍自定义流体的创建。如果计划创建多个流体,建议创建一个抽象的基本流体类,在其中设置必要的默认值,这些默认值将在其子类中共享。我们还将使其像湖泊一样在世界中生成。

创建抽象流体

原版流体继承了 net.minecraft.fluid.FlowableFluid,我们也应如此。

  1. public abstract class TutorialFluid extends FlowableFluid {
  2. /**
  3. * @return 给定的流体是否为该流体的实例?
  4. */
  5. @Override
  6. public boolean matchesType(Fluid fluid) {
  7. return fluid == getStill() || fluid == getFlowing();
  8. }
  9.  
  10. /**
  11. * @return 流体是否可以像无限刷水的方法一样无限生成?
  12. */
  13. @Override
  14. protected boolean isInfinite() {
  15. return false;
  16. }
  17.  
  18. /**
  19. * 流体流入一个可替换的方块时的行为。
  20. * 水会掉落方块的战利品表。熔岩会播放“block.lava.extinguish”音效。
  21. */
  22. @Override
  23. protected void beforeBreakingBlock(WorldAccess world, BlockPos pos, BlockState state) {
  24. final BlockEntity blockEntity = state.getBlock().hasBlockEntity() ? world.getBlockEntity(pos) : null;
  25. Block.dropStacks(state, world, pos, blockEntity);
  26. }
  27.  
  28. /**
  29. * Lava returns true if its FluidState is above a certain height and the
  30. * Fluid is Water.
  31. *
  32. * @return 给定的流体能否流入它的FluidState?
  33. */
  34. @Override
  35. protected boolean canBeReplacedWith(FluidState fluidState, BlockView blockView, BlockPos blockPos, Fluid fluid, Direction direction) {
  36. return false;
  37. }
  38.  
  39. /**
  40. * 或许与流入周围附近凹洞的距离检查有关?
  41. * 水返回4。熔岩在主世界返回2,而在下界返回4。
  42. */
  43. @Override
  44. protected int getFlowSpeed(WorldView worldView) {
  45. return 4;
  46. }
  47.  
  48. /**
  49. * 返回每次流动一格,其等级减少的数值。水返回1,熔岩在主世界返回2,在下界返回1。
  50. */
  51. @Override
  52. protected int getLevelDecreasePerBlock(WorldView worldView) {
  53. return 1;
  54. }
  55.  
  56. /**
  57. * 返回每流一格需要花费的时间(按刻计算)。水返回5。熔岩在主世界返回30,在下界返回10。
  58. */
  59. @Override
  60. public int getTickRate(WorldView worldView) {
  61. return 5;
  62. }
  63.  
  64. /**
  65. * 返回爆炸抗性。水和熔岩都返回100.0F。
  66. */
  67. @Override
  68. protected float getBlastResistance() {
  69. return 100.0F;
  70. }
  71. }

进行

现在让我们制作一个拥有静止和流动两个变种的实际流体。在此教程中,我们将其称为“酸”。缺失的引用稍后补全。

  1. public abstract class AcidFluid extends TutorialFluid
  2. {
  3. @Override
  4. public Fluid getStill()
  5. {
  6. return <YOUR_STILL_FLUID_HERE>;
  7. }
  8.  
  9. @Override
  10. public Fluid getFlowing()
  11. {
  12. return <YOUR_FLOWING_FLUID_HERE>;
  13. }
  14.  
  15. @Override
  16. public Item getBucketItem()
  17. {
  18. return <YOUR_BUCKET_ITEM_HERE>;
  19. }
  20.  
  21. @Override
  22. protected BlockState toBlockState(FluidState fluidState)
  23. {
  24. // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses
  25. return <YOUR_FLUID_BLOCK_HERE>.getDefaultState().with(Properties.LEVEL_15, method_15741(fluidState));
  26. }
  27.  
  28. public static class Flowing extends AcidFluid
  29. {
  30. @Override
  31. protected void appendProperties(StateManager.Builder<Fluid, FluidState> builder)
  32. {
  33. super.appendProperties(builder);
  34. builder.add(LEVEL);
  35. }
  36.  
  37. @Override
  38. public int getLevel(FluidState fluidState)
  39. {
  40. return fluidState.get(LEVEL);
  41. }
  42.  
  43. @Override
  44. public boolean isStill(FluidState fluidState)
  45. {
  46. return false;
  47. }
  48. }
  49.  
  50. public static class Still extends AcidFluid
  51. {
  52. @Override
  53. public int getLevel(FluidState fluidState)
  54. {
  55. return 8;
  56. }
  57.  
  58. @Override
  59. public boolean isStill(FluidState fluidState)
  60. {
  61. return true;
  62. }
  63. }
  64. }

接下来,我们将制作静态和动态酸变体的静态实例,以及一个酸桶。 在您的ModInitializer中:

  1. // ...
  2.  
  3. public static FlowableFluid STILL_ACID;
  4. public static FlowableFluid FLOWING_ACID;
  5.  
  6. public static Item ACID_BUCKET;
  7.  
  8. // ...
  9.  
  10. @Override
  11. public void onInitialize()
  12. {
  13. // ...
  14.  
  15. STILL_ACID = Registry.register(Registry.FLUID, new Identifier(MOD_ID, "acid"), new AcidFluid.Still());
  16.  
  17. FLOWING_ACID = Registry.register(Registry.FLUID, new Identifier(MOD_ID, "flowing_acid"), new AcidFluid.Flowing());
  18.  
  19. ACID_BUCKET = Registry.register(Registry.ITEM, new Identifier(MOD_ID, "acid_bucket"), new BucketItem(STILL_ACID, new Item.Settings().recipeRemainder(Items.BUCKET).maxCount(1)));
  20.  
  21. // ...
  22. }
  23.  
  24. // ...

为了使自定义流体表现得像水或熔岩,您必须将其添加到相应的流体标签中:对于水,制作data/minecraft/tags/fluids/ water.json文件,并在其中写入流体的标识符:

  1. {
  2. "replace": false,
  3. "values":
  4. [
  5. "your_mod_id:acid",
  6. "your_mod_id:flowing_acid"
  7. ]
  8. }

制作一个流体方块

接下来,我们需要在世界中创建表示酸的方块。net.minecraft.block.FluidBlock是我们需要使用的类,但由于其构造器受保护,我们不能直接构造它。一种解决方法是制作子类或者匿名子类。这里我们展示后一种方式。在您的ModInitializer中:

  1. // ...
  2.  
  3. public static Block ACID;
  4.  
  5. // ...
  6.  
  7. @Override
  8. public void onInitialize()
  9. {
  10. // ...
  11.  
  12. ACID = Registry.register(Registry.BLOCK, new Identifier(MOD_ID, "acid"), new FluidBlock(STILL_ACID, FabricBlockSettings.copy(Blocks.WATER)){});
  13.  
  14. // ...
  15. }
  16.  
  17. // ...

既然我们有了这些静态对象,我们回到AcidFluid并补全被重写的方法:

  1. public abstract class AcidFluid extends TutorialFluid
  2. {
  3. @Override
  4. public Fluid getStill()
  5. {
  6. return TutorialMod.STILL_ACID;
  7. }
  8.  
  9. @Override
  10. public Fluid getFlowing()
  11. {
  12. return TutorialMod.FLOWING_ACID;
  13. }
  14.  
  15. @Override
  16. public Item getBucketItem()
  17. {
  18. return TutorialMod.ACID_BUCKET;
  19. }
  20.  
  21. @Override
  22. protected BlockState toBlockState(FluidState fluidState)
  23. {
  24. // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses
  25. return TutorialMod.ACID.getDefaultState().with(Properties.LEVEL_15, method_15741(fluidState));
  26. }
  27.  
  28. // ...
  29. }

渲染设置

为了让流体拥有纹理,或者与一个颜色绑定,你需要为其注册一个FluidRenderHandler。这里,我们重用水的纹理,并仅仅改变用于其上的颜色。为确保纹理渲染为半透明的,你可以使用Fabric的BlockRenderLayerMap

  1. public class TutorialModClient implements ClientModInitializer
  2. {
  3. // ...
  4.  
  5. @Override
  6. public void onInitializeClient()
  7. {
  8. // ...
  9.  
  10. setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new Identifier("minecraft", "water"), 0x4CC248);
  11. BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID);
  12.  
  13. // ...
  14. }
  15.  
  16. // ...
  17.  
  18. public static void setupFluidRendering(final Fluid still, final Fluid flowing, final Identifier textureFluidId, final int color)
  19. {
  20. final Identifier stillSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_still");
  21. final Identifier flowingSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_flow");
  22.  
  23. // If they're not already present, add the sprites to the block atlas
  24. ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEX).register((atlasTexture, registry) ->
  25. {
  26. registry.register(stillSpriteId);
  27. registry.register(flowingSpriteId);
  28. });
  29.  
  30. final Identifier fluidId = Registry.FLUID.getId(still);
  31. final Identifier listenerId = new Identifier(fluidId.getNamespace(), fluidId.getPath() + "_reload_listener");
  32.  
  33. final Sprite[] fluidSprites = { null, null };
  34.  
  35. ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener()
  36. {
  37. @Override
  38. public Identifier getFabricId()
  39. {
  40. return listenerId;
  41. }
  42.  
  43. /**
  44. * Get the sprites from the block atlas when resources are reloaded
  45. */
  46. @Override
  47. public void apply(ResourceManager resourceManager)
  48. {
  49. final Function<Identifier, Sprite> atlas = MinecraftClient.getInstance().getSpriteAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEX);
  50. fluidSprites[0] = atlas.apply(stillSpriteId);
  51. fluidSprites[1] = atlas.apply(flowingSpriteId);
  52. }
  53. });
  54.  
  55. // The FluidRenderer gets the sprites and color from a FluidRenderHandler during rendering
  56. final FluidRenderHandler renderHandler = new FluidRenderHandler()
  57. {
  58. @Override
  59. public Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState state)
  60. {
  61. return fluidSprites;
  62. }
  63.  
  64. @Override
  65. public int getFluidColor(BlockRenderView view, BlockPos pos, FluidState state)
  66. {
  67. return color;
  68. }
  69. };
  70.  
  71. FluidRenderHandlerRegistry.INSTANCE.register(still, renderHandler);
  72. FluidRenderHandlerRegistry.INSTANCE.register(flowing, renderHandler);
  73. }
  74.  
  75. // ...
  76. }

如果你需要使用你自己的流体纹理,你可以参考原版资源包1)作为一个模板。

在世界中生成

为使得酸湖在世界中生成,你可以在你的ModInitializer中创建一个net.minecraft.world.gen.feature.LakeFeature,然后将其添加到你需要让它生成的生物群系中:

  1. // ...
  2.  
  3. public static LakeFeature ACID_LAKE;
  4.  
  5. // ...
  6.  
  7. @Override
  8. public void onInitialize()
  9. {
  10. // ...
  11.  
  12. ACID_LAKE = Registry.register(Registry.FEATURE, new Identifier(MOD_ID, "acid_lake"), new LakeFeature(SingleStateFeatureConfig::deserialize));
  13.  
  14. // 在沼泽中生成,类似于水湖,但是概率为40(数字越高,生成几率越低)
  15. Biomes.SWAMP.addFeature(
  16. GenerationStep.Feature.LOCAL_MODIFICATIONS,
  17. ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState()))
  18. .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40)))
  19. );
  20.  
  21. // ...
  22. }
  23.  
  24. // ...
1)
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
zh_cn/tutorial/fluids.1683199094.txt.gz · Last modified: 2023/05/04 11:18 by solidblock