概述

Forge针对流体,提供了一套独立的机制,这个机制包括流体的注册、对应方块的注册、以及对应桶和对应流体罐的注册。

本部分和下一部分将通过添加水银这个流体、以及相关方块和物品,来讲述流体的注册、对应方块的注册、和对应桶的注册方式。

Fluid类与其实例化

Forge提供的流体机制的核心,就是Fluid类。

新建包com.github.ustc_zzzz.fmltutor.fluid,并在其下新建一个文件FluidMercury.java

src/main/java/com/github/ustc_zzzz/fmltutor/fluid/FluidMercury.java:

package com.github.ustc_zzzz.fmltutor.fluid;

import com.github.ustc_zzzz.fmltutor.FMLTutor;

import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.Fluid;

public class FluidMercury extends Fluid
{
    public static final ResourceLocation still = new ResourceLocation(FMLTutor.MODID + ":" + "fluid/mercury_still");
    public static final ResourceLocation flowing = new ResourceLocation(FMLTutor.MODID + ":" + "fluid/mercury_flow");

    public FluidMercury()
    {
        super("mercury", FluidMercury.still, FluidMercury.flowing);
        this.setUnlocalizedName("fluidMercury");
        this.setDensity(13600);
        this.setViscosity(750);
        this.setLuminosity(0);
        this.setTemperature(300);
    }
}

我们看一下Fluid类的构造方法:

public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing)
  • 第一个参数表示的是这个流体的名称,使用这个流体的现实生活中的名称就可以了
  • 第二个参数表示的是这个流体静止的时候使用的贴图位置,我们通过实例化一个ResourceLocation类来完成
  • 第三个参数表示的是这个流体流动的时候使用的贴图位置,和第二个参数同理

Forge规定的流体,有着五个常见的属性,供对应的方块、桶、和一些Mod使用:

  • setDensity方法用于设置这个流体的密度,单位为千克每立方米,默认为水的密度,也就是1000
  • setViscosity方法用于设置这个流体的粘度,单位为千分之一平方米每秒,使用运动粘度,默认为水的粘度,也就是1000
  • setLuminosity方法用于设置这个流体的亮度,也就是在Minecraft中的亮度,默认为水的亮度,也就是0
  • setTemperature方法用于设置这个流体的温度,使用热力学温标,也就是开尔文,默认为室温,也就是300
  • setGaseous方法用于标注这个流体是否为气体,默认不是

这里设置的属性和水银的状态还是契合的。

所以,我们现在补充一下流体的材质,新建文件夹src/main/resources/assets/fmltutor/textures/fluid,并在其中放入文件mercury_still.pngmercury_flow.png(刷屏开始):

src/main/resources/assets/fmltutor/textures/fluid/mercury_still.png:

mercury_still

src/main/resources/assets/fmltutor/textures/fluid/mercury_flow.png:

mercury_flow

(不要吐槽这团乳白色液体了。。。不会美工没有救啊!如果谁愿意重制这两张图我他日必有重谢)

(刷屏结束)我们注意到,上面两张图的高度比宽度高很多,这是因为流体有着流动动画,高度与宽度的比值是多少,这个动画便有着多少帧。所以,我们该如何描述这种动画呢?

在Minecraft自带的引擎中,流体、火焰等动画的描述是在同一个文件夹下的一个比材质文件名多了一个名为.mcmeta的后缀的文件中定义的:

src/main/resources/assets/fmltutor/textures/fluid/mercury_still.png.mcmeta:

{
  "animation": {
    "frametime": 2
  }
}

src/main/resources/assets/fmltutor/textures/fluid/mercury_flow.png.mcmeta:

{
  "animation": {}
}

前面的文件表示的是两gametick一帧,而后面的表示的是一gametick一帧,具体的配置方式请参照原版。

这里建议表示流动的材质宽度为32px,表示静止的材质宽度为16px,因为流体的流动包括斜向的流动,总而言之,流动的材质宽度应该是静止的两倍。

现在我们注册这个流体。我们在包com.github.ustc_zzzz.fmltutor.fluid下新建文件FluidLoader.java

src/main/java/com/github/ustc_zzzz/fmltutor/fluid/FluidLoader.java:

package com.github.ustc_zzzz.fmltutor.fluid;

import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;

public class FluidLoader
{
    public static Fluid fluidMercury = new FluidMercury();

    public FluidLoader(FMLPreInitializationEvent event)
    {
        if (FluidRegistry.isFluidRegistered(fluidMercury))
        {
            event.getModLog().info("Found fluid {}, the registration is canceled. ", fluidMercury.getName());
            fluidMercury = FluidRegistry.getFluid(fluidMercury.getName());
        }
        else
        {
            FluidRegistry.registerFluid(fluidMercury);
        }
    }
}

src/main/java/com/github/ustc_zzzz/fmltutor/common/CommonProxy.java(部分):

    public void preInit(FMLPreInitializationEvent event)
    {
        new ConfigLoader(event);
        new CreativeTabsLoader(event);
        new FluidLoader(event);
        new ItemLoader(event);
        new BlockLoader(event);
        new PotionLoader(event);
    }

我们通过调用FluidRegistry类的名为registerFluid的静态方法注册这个类。

不过,这里我们需要做一下检查,也就是检查这个流体是否被注册,如果这个流体已经被注册(比如其他Mod),那么我们就使用被注册过的流体,并顺道扔出一条日志信息。

最后,我们需要丰富一下语言文件:

src/main/resources/assets/fmltutor/lang/en_US.lang(部分):

fluid.mercury=Mercury

src/main/resources/assets/fmltutor/lang/zh_CN.lang(部分):

fluid.mercury=水银

注册流体对应的方块

注册一个流体对应的方块其实很简单,Forge提供了一个名为BlockFluidClassic的类,我们只需要简单继承这个类就行了。在包com.github.ustc_zzzz.fmltutor.block下新建文件BlockFluidMercury.java

src/main/java/com/github/ustc_zzzz/fmltutor/block/BlockFluidMercury.java:

package com.github.ustc_zzzz.fmltutor.block;

import com.github.ustc_zzzz.fmltutor.creativetab.CreativeTabsLoader;
import com.github.ustc_zzzz.fmltutor.fluid.FluidLoader;

import net.minecraft.block.material.Material;
import net.minecraftforge.fluids.BlockFluidClassic;

public class BlockFluidMercury extends BlockFluidClassic
{
    public BlockFluidMercury()
    {
        super(FluidLoader.fluidMercury, Material.water);
        this.setUnlocalizedName("fluidMercury");
        this.setCreativeTab(CreativeTabsLoader.tabFMLTutor);
    }
}

BlockFluidClassic类的构造方法有两个参数,分别是对应的流体、和这种流体方块使用的材质,这里的材质使用水就好了。

然后我们丰富一下语言文件:

src/main/resources/assets/fmltutor/lang/en_US.lang(部分):

tile.fluidMercury.name=Mercury

src/main/resources/assets/fmltutor/lang/zh_CN.lang(部分):

tile.fluidMercury.name=水银

完成注册:

src/main/java/com/github/ustc_zzzz/fmltutor/block/BlockLoader.java(部分):

    public BlockLoader(FMLPreInitializationEvent event)
    {
        register(grassBlock, "grass_block");
        register(fluidMercury, "fluid_mercury");
    }

注意,这里不需要像一般的方块一样注册这个方块的渲染模型,因为流体方块的渲染方法和普通方块不一样,下面就是对渲染模型注册方式的说明。

注册流体方块的渲染

然而,即便我们在刚刚注册了流体的静止和流动对应的材质,在实际游戏显示的时候,流体仍旧是一堆两种颜色相间的方块,甚至什么都没有。

这是因为我们没有注册流体对应的模型,换句话说,Minecraft压根不知道流体该怎么应用我们刚刚注册的贴图来渲染。我们在FluidLoader类中添加下面的代码:

src/main/java/com/github/ustc_zzzz/fmltutor/fluid/FluidLoader.java(部分):

    @SideOnly(Side.CLIENT)
    public static void registerRenders()
    {
        registerFluidRender((BlockFluidBase) BlockLoader.fluidMercury, "fluid_mercury");
    }

    @SideOnly(Side.CLIENT)
    public static void registerFluidRender(BlockFluidBase blockFluid, String blockStateName)
    {
        final String location = FMLTutor.MODID + ":" + blockStateName;
        final Item itemFluid = Item.getItemFromBlock(blockFluid);
        ModelLoader.setCustomMeshDefinition(itemFluid, new ItemMeshDefinition()
        {
            @Override
            public ModelResourceLocation getModelLocation(ItemStack stack)
            {
                return new ModelResourceLocation(location, "fluid");
            }
        });
        ModelLoader.setCustomStateMapper(blockFluid, new StateMapperBase()
        {
            @Override
            protected ModelResourceLocation getModelResourceLocation(IBlockState state)
            {
                return new ModelResourceLocation(location, "fluid");
            }
        });
    }

上面这段代码的核心,就在后面两句。这两句话都传入了一个分别覆写了一个方法的实例,这两个方法都返回了一个相同的值,也就是new ModelResourceLocation(location, "fluid")

这段代码注册了一个自定义的方块模型文件,这里提供的位置是fmltutor:fluid_mercury,换算到文件位置中就是assets.fmltutor.blockstates包下的fluid_mercury.json

src/main/resources/assets/fmltutor/blockstates/fluid_mercury.json:

{
  "forge_marker": 1,
  "variants": {
    "fluid": {
      "model": "forge:fluid",
      "custom": { "fluid": "mercury" }
    }
  }
}

这里model对应的fluid,把这个方块的模型引向了一个Forge自建的模型,也就是forge:fluid,这里我们就不用去管它了。

然后我们要保证registerRenders这个方法能够被调用到,而且只被客户端调用到。我们修改一下ItemRenderLoader.java

src/main/java/com/github/ustc_zzz/fmltutor/client/fluid/ItemRenderLoader.java:

package com.github.ustc_zzzz.fmltutor.client;

import com.github.ustc_zzzz.fmltutor.block.BlockLoader;
import com.github.ustc_zzzz.fmltutor.fluid.FluidLoader;
import com.github.ustc_zzzz.fmltutor.item.ItemLoader;

public class ItemRenderLoader
{
    public ItemRenderLoader()
    {
        ItemLoader.registerRenders();
        BlockLoader.registerRenders();
        FluidLoader.registerRenders();
    }
}

使其在preInit阶段被注册,然后我们就可以打开游戏来看看了`。

results matching ""

    No results matching ""