想要实现一个有流体储存功能的方块。请教大家几个问题:
1.TileEntity中怎么实现流体数据的存储(以mB为单位),以及自动输入输出流体?
2.如何在GUI中绘制流体材质,以显示其内部的流体储量?
1.TileEntity中怎么实现流体数据的存储(以mB为单位),以及自动输入输出流体?
2.如何在GUI中绘制流体材质,以显示其内部的流体储量?
可以去看看相关的mod,比如热力系列的源代码
本帖最后由 3TUSK 于 2020-3-10 06:12 编辑
所以我们首先需要让 TileEntity 有一个保存流体信息的字段。这里我们选 FluidTank,原因一会再说:
复制代码
既然说“存储”,那么我们希望它在退出游戏时,数据会保存起来。所以:
复制代码
FluidTank 默认以 mB 为单位,所以不用担心。
也就是说我们要支持其他 Mod 的流体自动化设备。这需要借助 Forge 提供的标准化的流体 Capability 接口,IFluidHandler。
上文中使用的 FluidTank 已经实现了 IFluidHandler 所以不用担心。我们只需要通过 Capability 系统,向外界宣布“这个方块支持流体交互”即可。
复制代码
把这个丢进你继承 GuiContainer 的类里。
复制代码
节选自 https://github.com/3TUSK/FrogCra ... leFrog.java#L71-L88
使用方法:在 GuiScreen/GuiContainer.drawGuiContainerBackgroundLayer 方法中调用。x 的正方向朝右,y 的正方向朝下,原点是 GUI 的左上角。
上文给出的 helper method 会自动按流体储量缩放绘制的流体纹理高度。你也可以选择在 drawGuiContainerForegroundLayer(int mouseX, int mouseY) 中调用 GuiScreen.drawHoveringText 来绘制 Tooltip。
但,不论选哪一个,都涉及到一个问题:你的客户端上没有流体的数据。对于你的客户端来说,除非你的服务器告诉客户端“这个流体罐子里有 X 流体 Y mb”,你的客户端是什么都不知道的。
所以我们需要一个方法告诉客户端“这个罐子里有什么流体,有多少”。这个方法可以是 SimpleNetworkWrapper。
SimpleNetworkWrapper 的相关资料:
https://harbinger.covertdragon.t ... etwork-wrapper.html
https://mcforge.readthedocs.io/en/1.12.x/networking/simpleimpl/
具体来说,我们要同步的数据有:1. 目标 TileEntity 所在维度(World.provider.getDimension())和坐标(TileEntity.getPos()) 2. 那个 FluidTank 里的关键信息(FluidTank.getFluid() 和 FluidTank.getCapacity())。
也可以是利用 Container。你的 GUI 如果要显示玩家背包,那 Container 是必不可少的存在。原版熔炉使用 Container 类下的几个方法同步熔炉的进度条数据。
万事开头难。是的,楼主你描述的需求实现起来确实有这样复杂。习惯了就好。
TileEntity中怎么实现流体数据的存储
所以我们首先需要让 TileEntity 有一个保存流体信息的字段。这里我们选 FluidTank,原因一会再说:
- import net.minecraft.tileentity.TileEntity;
- import net.minecraftforge.fluids.FluidTank;
- public class MyTank extends TileEntity {
- FluidTank tank;
- }
既然说“存储”,那么我们希望它在退出游戏时,数据会保存起来。所以:
- import net.minecraft.nbt.NBTTagCompound;
- import net.minecraft.tileentity.TileEntity;
- import net.minecraftforge.fluids.FluidTank;
- public class MyTank extends TileEntity {
- FluidTank tank = new FluidTank(8000); // 上限 8000 mB
- @Override
- public NBTTagCompound writeToNBT(NBTTagCompound data) { // 写数据进存档
- data.setTag("Tank", this.tank.writeToNBT(new NBTTagCompound()));
- return super.writeToNBT(data);
- }
- @Override
- public void readFromNBT(NBTTagCompound data) { // 从存档读数据
- super.readFromNBT(data);
- this.tank.readFromNBT(data.getCompoundTag("Tank"));
- }
- }
(以mB为单位)
FluidTank 默认以 mB 为单位,所以不用担心。
以及自动输入输出流体
也就是说我们要支持其他 Mod 的流体自动化设备。这需要借助 Forge 提供的标准化的流体 Capability 接口,IFluidHandler。
上文中使用的 FluidTank 已经实现了 IFluidHandler 所以不用担心。我们只需要通过 Capability 系统,向外界宣布“这个方块支持流体交互”即可。
- import net.minecraft.nbt.NBTTagCompound;
- import net.minecraft.tileentity.TileEntity;
- import net.minecraftforge.fluids.FluidTank;
- import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
- public class MyTank extends TileEntity {
- FluidTank tank = new FluidTank(8000); // 上限 8000 mB
- @Override
- public NBTTagCompound writeToNBT(NBTTagCompound data) { // 写数据进存档
- data.setTag("Tank", this.tank.writeToNBT(new NBTTagCompound()));
- return super.writeToNBT(data);
- }
- @Override
- public void readFromNBT(NBTTagCompound data) { // 从存档读数据
- super.readFromNBT(data);
- this.tank.readFromNBT(data.getCompoundTag("Tank"));
- }
-
- @Override
- public boolean hasCapability(Capability<?> cap, EnumFacing face) {
- return cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || super.hasCapability(cap, face);
- }
-
- @Override
- public <T> T getCapability(Capability<T> cap, EnumFacing face) {
- if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
- return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this.tank);
- }
- return super.getCapability(cap, face);
- }
- }
如何在GUI中绘制流体材质
把这个丢进你继承 GuiContainer 的类里。
- /**
- * Draw a fluid tank with given parameters. If the given tank is null, it will does nothing.
- * @param tank The {@link IFluidTank} instance, will keep unmodified
- * @param x The starting x-coordinate of tank
- * @param y The starting y-coordinate of tank
- * @param tankWidth The fluid tank full width in the GUI texture
- * @param tankHeight The fluid tank full height in the GUI texture
- */
- public void renderFluidTank(final IFluidTank tank, final int x, final int y, final int tankWidth, final int tankHeight) {
- if (tank == null || tank.getFluid() == null)
- return;
-
- TextureAtlasSprite fluidSprite = this.mc.getTextureMapBlocks().getAtlasSprite(tank.getFluid().getFluid().getStill().toString());
- this.mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
-
- int scaledHeight = tankHeight * tank.getFluidAmount() / tank.getCapacity();
- this.drawTexturedModalRect(x, y + tankHeight - scaledHeight, fluidSprite, tankWidth, scaledHeight);
- }
节选自 https://github.com/3TUSK/FrogCra ... leFrog.java#L71-L88
使用方法:在 GuiScreen/GuiContainer.drawGuiContainerBackgroundLayer 方法中调用。x 的正方向朝右,y 的正方向朝下,原点是 GUI 的左上角。
以显示其内部的流体储量
上文给出的 helper method 会自动按流体储量缩放绘制的流体纹理高度。你也可以选择在 drawGuiContainerForegroundLayer(int mouseX, int mouseY) 中调用 GuiScreen.drawHoveringText 来绘制 Tooltip。
但,不论选哪一个,都涉及到一个问题:你的客户端上没有流体的数据。对于你的客户端来说,除非你的服务器告诉客户端“这个流体罐子里有 X 流体 Y mb”,你的客户端是什么都不知道的。
所以我们需要一个方法告诉客户端“这个罐子里有什么流体,有多少”。这个方法可以是 SimpleNetworkWrapper。
SimpleNetworkWrapper 的相关资料:
https://harbinger.covertdragon.t ... etwork-wrapper.html
https://mcforge.readthedocs.io/en/1.12.x/networking/simpleimpl/
具体来说,我们要同步的数据有:1. 目标 TileEntity 所在维度(World.provider.getDimension())和坐标(TileEntity.getPos()) 2. 那个 FluidTank 里的关键信息(FluidTank.getFluid() 和 FluidTank.getCapacity())。
也可以是利用 Container。你的 GUI 如果要显示玩家背包,那 Container 是必不可少的存在。原版熔炉使用 Container 类下的几个方法同步熔炉的进度条数据。
万事开头难。是的,楼主你描述的需求实现起来确实有这样复杂。习惯了就好。
3TUSK 发表于 2020-3-10 06:07
所以我们首先需要让 TileEntity 有一个保存流体信息的字段。这里我们选 FluidTank,原因一会再说:
既然 ...
大神,我想请教一下
如果我想通过按一个按钮来对物品槽进行操作
由于按钮按下是在GuiContainer这个客户端上操作的
如果不给服务器发包的话怎么才能同步Container呢?
ArcoWave_龙梦 发表于 2020-3-11 13:02
大神,我想请教一下
如果我想通过按一个按钮来对物品槽进行操作
由于按钮按下是在GuiContainer这个客户端 ...
不给服务器发包
那是不可能的。你不从客户端向服务器发包,就好像是你写完了作业不告诉老师一样,老师也只能当你没写。
按钮按下去的的逻辑大约是 GuiContainer.actionPerformed。
https://github.com/TeamCovertDra ... Packet.java#L84-L95
666666666666666666666666