Flandre923
1814 words
9 minutes
40 流体

参考#

https://boson.v2mcdev.com/fluid/firstfluid.html

流体#

这次我们来说怎么创建流体。

在游戏中你看到的流体一把是在世界中存在的流动的和源头的两中,不过还有就是机器中处理的,桶这样的形势存在的。

当在桶内装的时候,是一个BucketItem的类,而在世界中不断的流动的就是一种特殊的方块,你可以尝试删除某个 流体的贴图你会发发现都是黑紫块了。

说明流体在世界中就是一种特殊的方块,不过这种特殊的方块有着类似方块和方块实体的这样的特殊关系,方块和对应的流体关联起来。

对于流体有两种状态我们需要创建这两种装填的流体,流动的flow,和源头source以及流体的type,这三个类是我们需要处理的,以及将创建的流体和对于的方块做出关联。

我们先来看下流体的type怎么处理,由于原版的fluidtype直接使用不方便,这里就给他包裹一层,然后我们使用我们创建的这个basefluidtype创建流体的type。

下面我们来看看这个类都提供了什么功能,和流体的那些属性有关。

// 用于定义了流体类型
public class BaseFluidType extends FluidType {
    // 定义了源source的纹理图片,流动的纹理图片,以及流体覆盖层的图片(指的是颜色,例如水的蓝色纹理,岩浆的红色纹理,你可以到原版对应的位置看看是什么图片就知道了)
    private final ResourceLocation stillTexture;
    private final ResourceLocation flowingTexture;
    private final ResourceLocation overlayTexture;
    // 流体的着色颜色
    private final int tintColor;
    // 从流体中看外面的雾的颜色
    private final Vector3f fogColor;

    //构造函数
    public BaseFluidType(final ResourceLocation stillTexture, final ResourceLocation flowingTexture, final ResourceLocation overlayTexture,
                         final int tintColor, final Vector3f fogColor, final Properties properties) {
        super(properties);
        this.stillTexture = stillTexture;
        this.flowingTexture = flowingTexture;
        this.overlayTexture = overlayTexture;
        this.tintColor = tintColor;
        this.fogColor = fogColor;
    }
    // 对应的get函数
    public ResourceLocation getStillTexture() {
        return stillTexture;
    }

    public ResourceLocation getFlowingTexture() {
        return flowingTexture;
    }

    public int getTintColor() {
        return tintColor;
    }

    public ResourceLocation getOverlayTexture() {
        return overlayTexture;
    }

    public Vector3f getFogColor() {
        return fogColor;
    }

    // 对于我们的几个纹理,如果如果想生效的话,就需要重写这个方法,在对于的方法将我们的RL的资源定位的图片返回。
    @Override
    public void initializeClient(Consumer<IClientFluidTypeExtensions> consumer) {
        consumer.accept(new IClientFluidTypeExtensions() {
            @Override
            public ResourceLocation getStillTexture() {
                return stillTexture;
            }

            @Override
            public ResourceLocation getFlowingTexture() {
                return flowingTexture;
            }

            @Override
            public @Nullable ResourceLocation getOverlayTexture() {
                return overlayTexture;
            }

            @Override
            public int getTintColor() {
                return tintColor;
            }

            // 修改从流体中看雾的颜色
            @Override
            public @NotNull Vector3f modifyFogColor(Camera camera, float partialTick, ClientLevel level,
                                                    int renderDistance, float darkenWorldAmount, Vector3f fluidFogColor) {
                return fogColor;
            }
            // 液体中的能见度 或者 说雾的范围
            @Override
            public void modifyFogRender(Camera camera, FogRenderer.FogMode mode, float renderDistance, float partialTick,
                                        float nearDistance, float farDistance, FogShape shape) {
                RenderSystem.setShaderFogStart(1f);
                RenderSystem.setShaderFogEnd(6f); // distance when the fog starts
            }
        });
    }

}

好了我们可以看到流体类型fluidType类和流体的颜色,贴图等属性有关。

我们接下来看怎么使用BaseFluidType创建我们的流体类型。并注册到总线。


public class ModFluidType {
    // 图片的位置,这里的source和flow,直接使用的原版的,所以没有第一个参数传入modid。
    public static final ResourceLocation WATER_STILL_RL = new ResourceLocation("block/water_still");
    public static final ResourceLocation WATER_FLOWING_RL = new ResourceLocation("block/water_flow");
    // 这里的流体的overlay的图片使用是自己的图片,直接原版的water修改的。
    public static final ResourceLocation MY_FLUID_RL = new ResourceLocation(ExampleMod.MODID, "misc/my_fluid");

    // 怎么获得流体类型FluidType的注册器,有一点特殊,不是直接在Registires类下,而是在NeoForgeRegistries.Keys下
    // 这应该是因为原版并没有流体类型FluidType这样的概念。
    public static final DeferredRegister<FluidType> FLUID_TYPES =
            DeferredRegister.create(NeoForgeRegistries.Keys.FLUID_TYPES, ExampleMod.MODID);

    // 我们看到使用了register这个方法,这个方法是我们自己写的。
    // 对于register在下面介绍,我们来看参数
    // 第一参数name,没什么好说的,第二个参数是对流体的类型的属性进行一些设置  FluidType.Properties这个类就是对属性的一些设置。
    // create返回一个properties实例,lightlevel设置亮度等级2,density设置密度,viscosity设置粘度,sound设置流体声音。
    // 这些数值是直接复制的水的,对于其中的一些具体的效果,就自己调试看看效果把。也可以大家弹幕评论补充
    public static final Supplier<FluidType> MY_FLUID_TYPE = register("my_fluid",
            FluidType.Properties.create().lightLevel(2).density(15).viscosity(5).sound(SoundAction.get("drink"),
                    SoundEvents.HONEY_DRINK));

    // 这个是我们自己写的注册的方法
    // 并没有什么特殊的,直接返回了new baseFludType的supplier方法。
    private static Supplier<FluidType> register(String name, FluidType.Properties properties) {
        return FLUID_TYPES.register(name, () -> new BaseFluidType(WATER_STILL_RL, WATER_FLOWING_RL, MY_FLUID_RL,
                0xA1E038D0, new Vector3f(224f / 255f, 56f / 255f, 208f / 255f), properties));
    }
    // 记得注册到总线
    public static void register(IEventBus eventBus) {
        FLUID_TYPES.register(eventBus);
    }

}

好了,流体类型说完了,我们来看怎么处理流体了,包含了流体的source和flow,其实source和flow都是继承FlowingFluid类的。不过Neoforge为我们提供了BaseFlowingFluid类以及Source和Flowing子类帮助我们创建对应的实例。


public class ModFluids {
    // 流体注册器
    public static final DeferredRegister<Fluid> FLUIDS = DeferredRegister.create(Registries.FLUID, ExampleMod.MODID);

    // 注册对应流体的source和flow,使用NeoForge提供的BaseFlowingFluid来注册
    // 其中source和flow都需要填入一个参数,这个参数是流体的属性,在下面定义
    public static Supplier<FlowingFluid> MY_SOURCE_FLUID_BLOCK = FLUIDS.register("my_fluid", () -> new BaseFlowingFluid.Source(ModFluids.MY_FLUID_PROPERTIES));
    public static Supplier<FlowingFluid> MY_FLOWING_FLUID_BLOCK = FLUIDS.register("my_fluid_flow", () -> new BaseFlowingFluid.Flowing(ModFluids.MY_FLUID_PROPERTIES));
    // 定义流体的属性
    // 这个流体的属性要传入的内容比较多,我们挨个介绍,我们使用了BaseFlowingFluid的Properties内部类创建对应的Properties,其中第一个参数是对应的流体的类体类型FluidType,然后第二个参数是对应的source流体,第三个参数是flow流体,都是我们刚刚写过的,看起来比较绕,大家自己理清下关系。
    // 通过bucket这个设置流体和对应的流体桶的绑定,等会我们注册这个bucketitem
    // 通过block绑定对应的流体和方块的绑定,这个方块等会我们注册。
    // slopeFindDistance寻找斜坡的距离
    // levelDecreasePerBlock 每个方块流体的减少量。
    // 后两个数据是用于流体的流动的,主要是斜坡时候优先流,不会扩散。
    // 以及流体最多能流多远,例如原版的水是8格
    // 可以自己调试这几个数值试试,也可以去wiki看看具体的含义。
    private static final BaseFlowingFluid.Properties MY_FLUID_PROPERTIES = new BaseFlowingFluid.Properties(ModFluidType.MY_FLUID_TYPE, ModFluids.MY_SOURCE_FLUID_BLOCK, ModFluids.MY_FLOWING_FLUID_BLOCK).bucket(ModItems.MY_FLUID_BUCKET).slopeFindDistance(2).levelDecreasePerBlock(2).block(ModBlocks.MY_FLUID_BLOCK);
    // 别忘记注册到总线
    public static void register(IEventBus eventBus) {
        FLUIDS.register(eventBus);
    }
}

下面我们来看对应的流体的方块和物品。

先看方块。

// 注册方块,不过我们没有使用我么自己的那个方法,而是直接使用BLOCK的register方法,主要是我们不需要提供对应的item。因为我们还要注册对应的bucketitem。
    public static final Supplier<LiquidBlock> MY_FLUID_BLOCK = BLOCKS.register("my_fluid_block",
            ()->new LiquidBlock(ModFluids.MY_SOURCE_FLUID_BLOCK,BlockBehaviour.Properties.ofFullCopy(Blocks.WATER)));

流体桶

// 流体桶,craftRemainder表示合成之后保留桶
    public static final Supplier<Item> MY_FLUID_BUCKET = register("my_fluid_bucket", ()->new BucketItem(ModFluids.MY_SOURCE_FLUID_BLOCK,new Item.Properties().craftRemainder(Items.BUCKET).stacksTo(1)));

流体的渲染,由于我们的流体是一个半透明的材质,所以指定流体的渲染为:translucent

对于source和flow都需要指定。

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD,value = Dist.CLIENT)
public class ModClientEventHandler {
    @SubscribeEvent
    public static void onClientEvent(FMLClientSetupEvent event){
        event.enqueueWork(()->{
            //fluid
            ItemBlockRenderTypes.setRenderLayer(ModFluids.MY_SOURCE_FLUID_BLOCK.get(), RenderType.translucent());
            ItemBlockRenderTypes.setRenderLayer(ModFluids.MY_FLOWING_FLUID_BLOCK.get(), RenderType.translucent());

        });

    }

好了,别忘记了注册到总线,然后你就可以进入游戏中看看了。

40 流体
https://fuwari.vercel.app/posts/minecraft1_20_4/out_40-流体/
Author
Flandre923
Published at
2024-04-14