src_resources
想要实现一个有流体储存功能的方块。请教大家几个问题:
1.TileEntity中怎么实现流体数据的存储(以mB为单位),以及自动输入输出流体?
2.如何在GUI中绘制流体材质,以显示其内部的流体储量?



墨之永恒
可以去看看相关的mod,比如热力系列的源代码

3TUSK
本帖最后由 3TUSK 于 2020-3-10 06:12 编辑
TileEntity中怎么实现流体数据的存储

所以我们首先需要让 TileEntity 有一个保存流体信息的字段。这里我们选 FluidTank,原因一会再说:
  1. import net.minecraft.tileentity.TileEntity;
  2. import net.minecraftforge.fluids.FluidTank;

  3. public class MyTank extends TileEntity {
  4.   FluidTank tank;
  5. }
复制代码

既然说“存储”,那么我们希望它在退出游戏时,数据会保存起来。所以:
  1. import net.minecraft.nbt.NBTTagCompound;
  2. import net.minecraft.tileentity.TileEntity;
  3. import net.minecraftforge.fluids.FluidTank;

  4. public class MyTank extends TileEntity {
  5.   FluidTank tank = new FluidTank(8000); // 上限 8000 mB
  6.   @Override
  7.   public NBTTagCompound writeToNBT(NBTTagCompound data) { // 写数据进存档
  8.     data.setTag("Tank", this.tank.writeToNBT(new NBTTagCompound()));
  9.     return super.writeToNBT(data);
  10.   }
  11.   @Override
  12.   public void readFromNBT(NBTTagCompound data) { // 从存档读数据
  13.     super.readFromNBT(data);
  14.     this.tank.readFromNBT(data.getCompoundTag("Tank"));
  15.   }
  16. }
复制代码

(以mB为单位)

FluidTank 默认以 mB 为单位,所以不用担心。

以及自动输入输出流体

也就是说我们要支持其他 Mod 的流体自动化设备。这需要借助 Forge 提供的标准化的流体 Capability 接口,IFluidHandler。
上文中使用的 FluidTank 已经实现了 IFluidHandler 所以不用担心。我们只需要通过 Capability 系统,向外界宣布“这个方块支持流体交互”即可。
  1. import net.minecraft.nbt.NBTTagCompound;
  2. import net.minecraft.tileentity.TileEntity;
  3. import net.minecraftforge.fluids.FluidTank;
  4. import net.minecraftforge.fluids.capability.CapabilityFluidHandler;

  5. public class MyTank extends TileEntity {
  6.   FluidTank tank = new FluidTank(8000); // 上限 8000 mB
  7.   @Override
  8.   public NBTTagCompound writeToNBT(NBTTagCompound data) { // 写数据进存档
  9.     data.setTag("Tank", this.tank.writeToNBT(new NBTTagCompound()));
  10.     return super.writeToNBT(data);
  11.   }
  12.   @Override
  13.   public void readFromNBT(NBTTagCompound data) { // 从存档读数据
  14.     super.readFromNBT(data);
  15.     this.tank.readFromNBT(data.getCompoundTag("Tank"));
  16.   }
  17.   
  18.   @Override
  19.   public boolean hasCapability(Capability<?> cap, EnumFacing face) {
  20.     return cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || super.hasCapability(cap, face);
  21.   }
  22.   
  23.   @Override
  24.   public <T> T getCapability(Capability<T> cap, EnumFacing face) {
  25.     if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
  26.       return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this.tank);
  27.     }
  28.     return super.getCapability(cap, face);
  29.   }
  30. }
复制代码

如何在GUI中绘制流体材质

把这个丢进你继承 GuiContainer 的类里。
  1. /**
  2. * Draw a fluid tank with given parameters. If the given tank is null, it will does nothing.
  3. * @param tank The {@link IFluidTank} instance, will keep unmodified
  4. * @param x The starting x-coordinate of tank
  5. * @param y The starting y-coordinate of tank
  6. * @param tankWidth The fluid tank full width in the GUI texture
  7. * @param tankHeight The fluid tank full height in the GUI texture
  8. */
  9. public void renderFluidTank(final IFluidTank tank, final int x, final int y, final int tankWidth, final int tankHeight) {
  10.   if (tank == null || tank.getFluid() == null)
  11.     return;
  12.                
  13.   TextureAtlasSprite fluidSprite = this.mc.getTextureMapBlocks().getAtlasSprite(tank.getFluid().getFluid().getStill().toString());
  14.   this.mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
  15.   
  16.   int scaledHeight = tankHeight * tank.getFluidAmount() / tank.getCapacity();
  17.   this.drawTexturedModalRect(x, y + tankHeight - scaledHeight, fluidSprite, tankWidth, scaledHeight);
  18. }
复制代码

节选自 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 类下的几个方法同步熔炉的进度条数据。




万事开头难。是的,楼主你描述的需求实现起来确实有这样复杂。习惯了就好。

ArcoWave_龙梦
3TUSK 发表于 2020-3-10 06:07
所以我们首先需要让 TileEntity 有一个保存流体信息的字段。这里我们选 FluidTank,原因一会再说:

既然 ...

大神,我想请教一下
如果我想通过按一个按钮来对物品槽进行操作
由于按钮按下是在GuiContainer这个客户端上操作的
如果不给服务器发包的话怎么才能同步Container呢?

3TUSK
ArcoWave_龙梦 发表于 2020-3-11 13:02
大神,我想请教一下
如果我想通过按一个按钮来对物品槽进行操作
由于按钮按下是在GuiContainer这个客户端 ...
不给服务器发包

那是不可能的。你不从客户端向服务器发包,就好像是你写完了作业不告诉老师一样,老师也只能当你没写。

按钮按下去的的逻辑大约是 GuiContainer.actionPerformed。
https://github.com/TeamCovertDra ... Packet.java#L84-L95

2076959984
666666666666666666666666

第一页 上一页 下一页 最后一页