流体 1.21 Fabric
本篇教程的视频
本篇教程的源代码
Github地址:TutorialMod-Fluid-1.21
介绍
在游戏中,流体也是一个很重要的元素,比如水
、岩浆
在各大工业模组里面,也有各种各样不同的流体,比如石油
、酸
等等
1.20的教程我没讲流体,所以在1.21的教程中,我们还是来讲讲自定义的流体如何添加
查看源代码
这里我们以游戏中的水
为例,我们来添加一个仿水的流体
不过在此之前我们先看看水桶
怎么写的,毕竟我们的水可以通过水桶放置,也可以被空桶舀起
水桶
1 | public static final Item WATER_BUCKET = register("water_bucket", new BucketItem(Fluids.WATER, new Item.Settings().recipeRemainder(BUCKET).maxCount(1))); |
这里的水桶实例化的是BucketItem
,这个类是定义桶
的(其实你也可以再看一眼牛奶的,它实例化的是MilkBucketItem
,因为牛奶不像水或者岩浆这样可以倒出来)
这里的Fluids.WATER
是一个Fluid
类型的字段,它是原版的水
流体
后面的就是水桶的设置,这里设置了最大堆叠数
和配方剩余物
(空桶,用于使用后返回)
Fluids类
接下来我们看看Fluids
这个类,这个类是定义了原版的流体
1 | public class Fluids { |
这个类不长,我们看到这里的FLOWING_WATER
和WATER
这两个字段,顾名思义,一个是流动的水
,一个是静止的水
他们各自实例化的是WaterFluid.Flowing
和WaterFluid.Still
,这两个类是WaterFluid
的内部类
他们的类型都是FlowableFluid
,也就是可以流动的流体
这里的register
方法是用于注册流体的,看了前面那么多的注册方法,这个方法应该不难理解了吧
WaterFluid类
上面写到了WaterFluid
的两个内部类,我们来看看这个两个类
1 | public abstract class WaterFluid extends FlowableFluid { |
这个类是一个抽象类,继承了FlowableFluid
,这个类是用于定义流体的
它的两个内部类Flowing
和Still
分别是流动的水
和静止的水
这两个类都重写了getLevel
和isStill
方法,getLevel
是用于获取流体的高度,isStill
是用于判断流体是否静止
Flowing
类还重写了appendProperties
方法,这个方法是用于添加流体的属性,也就是把LEVEL
这个属性加入到流体的状态中(因为流动状态的水会根据流程长度决定流体的高度,而静止的就直接定义即可)
当然这里其他的方法我们可以先不看,待会我们会进行解释
除了渲染和其他数据文件之外,源代码大体上就是这样
注册流体
创建CustomFluid抽象类
这个也是仿照Wiki上的写法,我们创建一个CustomFluid
抽象类,用于定义我们的流体
如果说你的流体拥有差不多的属性,完全可以写一个抽象类,把一些共有的属性和方法写在这里,这样就不用每次写一个新的流体还得写一堆辅助的方法
然后你在写新的流体的时候,只需要继承这个抽象类,有必要时再重写一些方法就行了
1 | public abstract class CustomFluid extends FlowableFluid { |
我们的抽象类继承了FlowableFluid
,就和水一样
而后,我们开始在这个类中重写一些方法
matchesType
1 |
|
这个方法是用于判断流体是否匹配,这里我们判断的是流体是否是流动的水
或者静止的水
isInfinite
1 |
|
这个方法是用于判断流体是否是无限的,这里我们返回false
,原版的水是无限的,但岩浆不是
实际情况是,你也可以通过gamerule
来设置流体是否无限
beforeBreakingBlock
1 |
|
这个方法是用于破坏方块之前的操作,这里我们是用于破坏方块时掉落物品,比如草被流水破坏会掉落种子
canBeReplacedWith
1 |
|
这个方法是用于判断流体是否可以被替换,这里我们返回false
,原版的岩浆是可以被替换为黑曜石的,具体可见岩浆的这个方法
1 |
|
这个方法是用于获取流体的流动速度,原版的水是4
,岩浆在特定的生物群系(极热生物群系,应该只有下界吧)里面是2
,默认为4
getLevelDecreasePerBlock
1 |
|
这个方法是用于获取流体每个方块的流动速度,原版的水是1
,岩浆在特定的生物群系里面是2
,默认为1
getTickRate
1 |
|
这个方法是用于获取流体的更新速度,原版的水是5
,岩浆在特定的生物群系里面是30
,默认为10
(数值越大更新越慢)
getBlastResistance
1 |
|
这个方法是用于获取流体的爆炸抗性,原版的水和岩浆都是100.0F
总体
1 | public abstract class CustomFluid extends FlowableFluid { |
创建OilFluid抽象类
这个类是用于定义我们的流体的,我们继承CustomFluid
抽象类
1 | public abstract class OilFluid extends CustomFluid { |
当然我们这里还得重写四个方法(其实第四个是为内部类服务的),不过目前还没有完成注册,所以都先返回null
接下来我们写里面的两个类,一个是Flowing
,一个是Still
1 | public static class Flowing extends OilFluid { |
这里的内部类其实也和原版是差不多的,只是我们这里的继承的流体是OilFluid
创建ModFluids类
这个类是用于注册我们的流体的,和原版的Fluids
类一样
1 | public class ModFluids { |
这里我们写三块,一个是注册方法,一块静态代码块(用于获取流体的状态),一个初始化注册方法
这个注册方法和代码块是从原版的Fluids
类中搬过来的,当然注册方法记得改命名空间
随后我们来注册流体
1 | public static final FlowableFluid FLOWING_OIL = register("flowing_oil", new OilFluid.Flowing()); |
这里我们注册了了OIL
分别是静止的和流动的,它们各自实例化的是OilFluid
中对应的内部类
注册流体方块
在我们的OilFluid
类中,还有一个toBlockState
方法,这个方法是用于设置不同LEVEL
的流体方块的方块状态
但在此之前,我们得有个流体方块
1 | public static final Block OIL = Registry.register(Registries.BLOCK, Identifier.of(TutorialMod.MOD_ID, "oil"), |
这里我们注册了一个OIL
,它实例化的是FluidBlock
,这个类是用于定义流体方块的
第一个参数是我们的流体,第二个参数是方块的设置,这里我们用的是WATER
的设置,你也可以自定义
注册流体桶
那么我们的流体已经完成了,接下来我们来创建对应的桶物品
1 | public static final Item OIL_BUCKET = registerItems("oil_bucket", |
这里我们注册了一个OIL_BUCKET
,它实例化的是BucketItem
,后面的设置和原版的水桶一样,也给它设置了最大堆叠数
和配方剩余物
加入物品栏
不要忘了把这个桶加入到物品栏中
1 | entries.add(ModItems.OIL_BUCKET); |
完成方法的重写
那么现在,我们上面返回null
的四个方法就可以填写了
1 |
|
仿照着原版的WaterFluid
,我们填写了这四个方法,这样我们的流体才算彻底写好了
注册流体渲染和流体渲染层
那么我们的水是半透明的对吧?岩浆在某种意义上其实也是半透明的(也仅限于玩家在里面的时候,至少你还能看得见边缘,接近方块的时候)
不过我们这里不管岩浆,我们就用水的渲染
1 | BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), ModFluids.OIL, ModFluids.FLOWING_OIL); |
我们在TutorialModClient
的onInitializeClient
方法中注册我们的流体渲染层
这里用的还是Fabric
的API
,我们把OIL
和FLOWING_OIL
这两个流体放到RenderLayer.getTranslucent()
这个渲染层中,这个也就是半透明的
当然你也可以通过Mixin
去添加
1 | FluidRenderHandlerRegistry.INSTANCE.register(ModFluids.OIL, ModFluids.FLOWING_OIL, |
另外,借助Fabric
的API
,我们还得注册我们的流体渲染,因为流体和一般的方块不一样,它们的渲染是独立开来的
这里我们注册了OIL
和FLOWING_OIL
这两个流体的渲染,我们用的是SimpleFluidRenderHandler
,这个是一个简单的流体渲染处理器
面的两个Identifier
是用于指定流体的静止和流动的贴图,这里我们就借用原版贴图即可
最后一个参数是用于指定流体的颜色,这个颜色是16进制
的,可以根据你的流体的颜色来设置
数据文件
语言文件
1 | translationBuilder.add(ModItems.OIL_BUCKET, "Oil Bucket"); |
这里就写桶物品就好了
模型文件
1 | itemModelGenerator.register(ModItems.OIL_BUCKET, Models.GENERATED); |
模型文件用的也还是GENERATED
,也就是一般的物品模型
water.json
这个文件得写,而且不能写错
路径是resources/data/minecraft/tags/fluids/water.json
没写的话你的流体就不能正常显示了,而写错了的话就会导致原版的水也不正常了(因为我们写的是仿水的流体,除非你完完全全从头到尾重新定义了一个流体)
1 | { |
replace
得写false
,values
里面写我们的流体(命名空间+注册名)
贴图文件
最后也别忘了贴图文件,这个就不用我多说了吧
在此之后就可以进入游戏进行测试了,你可以用对应的桶来放置流体,也可以用空桶来舀起流体
它可以提供浮力,也会冲着你走(流动状态下),也可以破坏草这类的方块