当风过时
本帖最后由 当风过时 于 2013-1-15 15:31 编辑

被吐槽标题第一眼看成是“过时的教程”了……是“当风过时的教程”,不是“过时的教程”啊喂= =

上一个教程地址:http://www.mcbbs.net/thread-59702-1-1.html
经过上一个教程后,我们可以做出个拥有特定功能带GUI的方块了,不过显示出的GUI还存在着不能显示进度条问题,这次的教程就讲这个问题。

这次主要进行修改的文件是Container和Gui。
先是Gui文件rtGuiRepairTable:
在drawGuiContainerBackgroundLayer里增加一些东西
  1. int var4 = this.mc.renderEngine.getTexture("/gui/RepairTable.png");
  2.                 GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
  3.                 this.mc.renderEngine.bindTexture(var4);
  4.                 int var5 = (this.width - this.xSize) / 2;
  5.                 int var6 = (this.height - this.ySize) / 2;
  6.                 this.drawTexturedModalRect(var5, var6, 0, 0, this.xSize, this.ySize);
  7.                 // 下面是新增的东西
  8.         int b = tile.tableBurnTime; // 取得Tile内的燃料燃烧时间
  9.         float maxBurnTime = tile.maxBurnTime*1.0F;// 取得最大燃料燃烧时间,用float,不用的话得不出百分比
  10.         if (b > 0 && maxBurnTime > 0) // 确定描绘的时机
  11.         {
  12.                 // 描绘火焰图像
  13.             this.drawTexturedModalRect(this.guiLeft + 81, this.guiTop + 37 + (int)(14 - 14 * ((float)b / maxBurnTime)), 176, (int)(14 - 14 * ((float)b / maxBurnTime)), 14, (int)(14 * ((float)b / maxBurnTime)));
  14.         }
复制代码
描绘火焰图像那里,我姑且引用一下szszss大神的GUI教程的东西吧,当初我也是看那里知道怎么绘制这些进度条的
http://www.mcbbs.net/thread-23681-1-1.html
drawTexturedModalRect(x,y,u,v,w,h) 截取渲染引擎中绑定的纹理,并渲染到屏幕上.这个是最常用的方法.
它的使用很特殊,你需要向渲染引擎绑定纹理,首先你需要使用渲染引擎的getTexture方法来载入一个纹理并获得它的ID,之后你要将ID作为参数来调用渲染引擎的bindTexture方法来将它绑定上.绑定完毕后你便可以使用drawTexturedModalRect来绘制纹理.这里有6个参数作用的图解.


x,y : 将要在用户屏幕上绘制的纹理的左上角坐标.
u,v : 被绑定纹理中,需要绘制部分的左上角坐标.
w,h : 纹理的宽和高.
绘制的流程可以这样理解:
渲染引擎会截取被绑定的纹理,将纹理中一个以(u,v)为左上角,(u+w,v+h)为右下角的矩形区域截下来,并将其绘制到用户屏幕上,其左上角位于点(x,y).

简单的说,在我们的GUI里就是根据取得的燃烧数据,从右侧的燃烧条按燃烧时间和最大燃烧时间的百分比截取出图像,将截得的火焰图像渲染到GUI的对应位置(空的火焰的那个地方)。其实GUI本身的绘制都是基于这个方式。

之后就可以进行调试了,这时你会发现,在还保留着燃烧时间的时候退出,然后再进入游戏时,显示燃烧时间的火苗不见了,意味着时间被清零了,但TileEntity中是已经写好保存燃烧时间了的,其实这个时候燃烧时间还是存在的,只是存在于服务端里,启动游戏时服务端还没有把燃烧时间发送给客户端,这时我们就需要对客户端和服务端进行同步。

接下来就是Container开大招的时刻了!
rtContainerRepairTable添加如下代码
  1. @Override
  2.         public void addCraftingToCrafters(ICrafting par1iCrafting) {
  3.                 // TODO Auto-generated method stub
  4.                 super.addCraftingToCrafters(par1iCrafting);
  5.                 par1iCrafting.sendProgressBarUpdate(this, 0, this.tile.tableBurnTime);
  6.                 par1iCrafting.sendProgressBarUpdate(this, 1, this.tile.maxBurnTime);
  7.         }
  8.         @SideOnly(Side.CLIENT)
  9.     public void updateProgressBar(int par1, int par2)
  10.     {
  11.                 if (par1 == 0)
  12.         {
  13.             this.tile.tableBurnTime = par2;
  14.         }
  15.         if (par1 == 1)
  16.         {
  17.             this.tile.maxBurnTime = par2;
  18.         }
  19.     }
  20.         @Override
  21.     public void detectAndSendChanges()
  22.     {
  23.         // TODO Auto-generated method stub
  24.         super.detectAndSendChanges();
  25.         Iterator var1 = this.crafters.iterator();
  26.         while (var1.hasNext())
  27.         {
  28.             ICrafting var2 = (ICrafting)var1.next();

  29.             if (this.lastTableBurnTime != this.tile.tableBurnTime)
  30.             {
  31.                 var2.sendProgressBarUpdate(this, 0, this.tile.tableBurnTime);
  32.             }

  33.             if (this.lastMaxBurnTime  != this.tile.maxBurnTime)
  34.             {
  35.                 var2.sendProgressBarUpdate(this, 1, this.tile.maxBurnTime);
  36.             }
  37.         }
  38.         this.lastTableBurnTime = this.tile.tableBurnTime;
  39.         this.lastMaxBurnTime = this.tile.maxBurnTime;
  40.     }
复制代码
addCraftingToCrafters是添加监视器初始化的函数(这个地方我还不太敢确定,不过功能大概就是这样),返回的ICrafting的sendProgressBarUpdate就是将服务器上的数据发送给客户端第一个参数为Container本身,第二个是识别的ID,第三个是数据
updateProgressBar是客户端取得数据后进行同步的函数返回的第一个参数是ID,第二个是要同步的数据。
detectAndSendChanges是服务端发送数据的函数,当服务端上发现数据有变化时,就会将数据变化后的数据发送给客户端。

完成上述步骤后整个GUI就可以正常工作了

只是正常工作,还有一个比较重要的细节要说,那就是shift键对应的动作,大家应该都知道shift可以很方便的传送物品,把物品堆放到指定的槽位中,但在没有设定的情况下按下shift键很容易报错。
要定义shift键功能需要重写transferStackInSlot函数,我们这样改写:
  1. @Override
  2.     public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int par2)
  3.     {
  4.         ItemStack var3 = null;
  5.         Slot var4 = (Slot)this.inventorySlots.get(par2);
  6.         if (var4 != null && var4.getHasStack())
  7.         {
  8.             ItemStack var5 = var4.getStack();
  9.             var3 = var5.copy();
  10.             // 点击到Slot的ID为0-2之间的时候,将物品送回玩家的背包中,这个地方是
  11.             if (par2 >= 0 && par2 <= 2)
  12.             {
  13.                 if (!this.mergeItemStack(var5, 3, 30, false))
  14.                 {
  15.                     return null;
  16.                 }
  17.                 var4.onSlotChange(var5, var3);
  18.             }
  19.             // 点击到玩家的背包的时候将物品送到玩家的快捷栏中
  20.             else if (par2 > 3 && par2 < 30)
  21.             {
  22.                 if (!this.mergeItemStack(var5, 30, 39, false))
  23.                 {
  24.                     return null;
  25.                 }
  26.             }
  27.             // 点击到玩家的快捷栏的时候将物品送到背包中
  28.             else if (par2 >= 30 && par2 < 39)
  29.             {
  30.                 if (!this.mergeItemStack(var5, 3, 30, false))
  31.                 {
  32.                     return null;
  33.                 }
  34.             }
  35.             if (var5.stackSize == 0)
  36.             {
  37.                 var4.putStack((ItemStack)null);
  38.             }
  39.             else
  40.             {
  41.                 var4.onSlotChanged();
  42.             }
  43.             if (var5.stackSize == var3.stackSize)
  44.             {
  45.                 return null;
  46.             }
  47.             var4.onPickupFromSlot(par1EntityPlayer, var5);
  48.         }
  49.         return var3;
  50.     }
复制代码
mergeItemStack是传送物品栈的函数,它能将物品栈传送到指定的ID范围内,第一个参数物品栈,第二个是被传送到的起始ID,第三个是被传送到的结束ID,第四个是正序或逆序传送(就是优先从开头数还是从末尾数起)。这个的ID范围没设定好的话很容易造成复制物品的bug,我的mod就曾经被这个bug弄得很惨……在这个函数中,你可以定义相应物品的传送方式,比如熔炉,它能将可被冶炼的物品通过shift键放入冶炼槽中,将燃料放入燃料槽中,等等。

以上这些就是制作GUI的全部步骤和注意事项了。

于是乎整个GUI方块的教程就完事了。

附上整个工程的源码,稍微对TileEntity进行了一些规则bug的修改,以及升级到1.4.6了:
mod_RepairTable-src.zip (5.68 KB, 下载次数: 66)

尼克
果断占领前排,当风大神大爱
太棒咯

yudia
这样的技术帖居然才1回复?没错我就是来挖坟的。。。正在做1.7.2版本的mod,楼主的帖子帮了我很多,感激不尽!

WildernessRgr
yudia 发表于 2014-7-19 06:26
这样的技术帖居然才1回复?没错我就是来挖坟的。。。正在做1.7.2版本的mod,楼主的帖子帮了我很多,感激不 ...

9494,技术贴啊

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