自上一次论坛更新已经过了挺久了,现在来重新更新一下GUI的教程,虽然论坛上面的是比较久远的教程,但还好Mojang一直没有对GUI部分进行大的改动,来来去去也是些修改某些方法的参数,改变贴图的载入方式而已。
经过一些事情之后慢慢的发现,服主们的话果然一个字都不能信
论坛帖子地址:http://www.mcbbs.net/thread-59702-1-1.html
Mod基础入门:http://frostmiku.fcteam.net/?p=9没有Mod基础的请先移步Mod入门
GUI不是一个单独组件,是需要靠多个组件互相协作的,因为东西多,我分成了几个部分来说明
第二篇:https://www.windworkshop.cn/?p=437
第三篇:https://www.windworkshop.cn/?p=440
第四篇:https://www.windworkshop.cn/?p=450
废话不多说,直接开始
新建一个mod主文件,文件名为:mod_RepairTable.java,有错误直接修正
@Mod(modid = mod_RepairTable.MODID, version = mod_RepairTable.VERSION)
public class mod_RepairTable {
public Block repairTable;
// 定义mod的ID,这个是用户给代理端识别
public static final String MODID = "newmod";
public static final String VERSION = "1.0";
// 准备mod的静态实例
@Instance(MODID)
public static mod_RepairTable instance;
// 代理配置
@SidedProxy(clientSide = "com.wind.repairtable.ClientProxy", serverSide = "com.wind.repairtable.CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void preInit(FMLPreInitializationEvent e)
{
}
}
代理配置分为CommonProxy和ClientProxy,CommonProxy为服务端的代理,ClientProxy为客户端代理,CommonProxy需要引入IGuiHandler,这个Handler是处理GUI的,用于分发服务端和客户端的相关类如下,这个是服务端的CommonProxy.java:
CommonProxy和ClientProxy一般是需要分开来写的,因为客户端会有一些服务端所没有的东西,例如Render、Model、Gui等等。客户端的代理命名为ClientProxy.java并继承刚刚写的CommonProxy
public class ClientProxy extends CommonProxy {
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world,
int x, int y, int z) {
// TODO Auto-generated method stub
return null;
}
}
然后是创建Block,命名为rtBlockRepairTable.java,Block继承的类型为BlockContainer,这个类型是可以使用TileEntity的Block的类型,只继承Block的自定义Block只有一些Block的基本功能,就和泥土差不多(普通的方块一般就是有随机时间更新,周围方块改变触发等简单的HUD事件):
public class rtBlockRepairTable extends BlockContainer {
protected rtBlockRepairTable(Material p_i45386_1_) {
super(p_i45386_1_);
// TODO Auto-generated constructor stub
}
}
创建Block的TileEntity,命名为rtTileEntityRepairTable.java,拥有TileEntity是Block的后运行的必要条件,重写updateEntity方法,updateEntity是GUI执行的核心,主要的功能都是这里实现的,在里边添上一句System.out.println(“Hello GUI”);来让后台输出“Hello GUI“以表示TileEntity在运行。
public class rtTileEntityRepairTable extends TileEntity {
@Override
public void updateEntity() {
super.updateEntity();
System.out.println("Hello GUI");
}
}
Block和TileEntity完成了,接下来是GUI部分,GUI在Minecraft中由两部组成,一个是在客户端运行的渲染用的Gui,一个是在服务端运行并协调客户端Gui的Container(具体怎么协调后面会提到),前面提到的CommonProxy代理就是用于分发这两个东西的。
首先是Container,命名为rtContainerRepairTable并继承Container。修正之后多出了一个canInteractWith的方法,将这个方法的返回值改成true,这个方法一般是是用于验证打开GUI的人是否合法,换句话说是否拥有打开的权限,如果一直为false的话就会变成一打开就瞬间关闭了。接着添加私有变量tile和构造函数,tile是rtTileEntityRepairTable类型的,用于取得Block的运行参数,构造函数中的par2TileEntityRepairTable用于取得需要同步的Block,而那个par1InventoryPlayer是用于获得玩家背包的,为什么需要获得玩家背包,后面会提到。
public class rtContainerRepairTable extends Container {
private rtTileEntityRepairTable tile;
public rtContainerRepairTable(InventoryPlayer par1InventoryPlayer, rtTileEntityRepairTable par2TileEntityRepairTable)
{
tile = par2TileEntityRepairTable;
}
@Override
public boolean canInteractWith(EntityPlayer var1) {
// TODO Auto-generated method stub
return true;
}
}
然后是是Gui,命名为rtGuiRepairTable.java并继承GuiContainer,继承GuiContainer就说明是需要使用Container组件的,在实例化的super里边就要求提供一个Container,将之前的rtContainerRepairTable实例化添加上去。
public class rtGuiRepairTable extends GuiContainer {
private rtTileEntityRepairTable tile;
public rtGuiRepairTable(InventoryPlayer inventory, rtTileEntityRepairTable tileEntity) {
super(new rtContainerRepairTable(inventory, tileEntity));
// TODO Auto-generated constructor stub
this.tile = tileEntity;
this.doesGuiPauseGame();
}
}
给rtGuiRepairTable复写两个方法,这两个方法是进行渲染的方法。drawGuiContainerForegroundLayer这个方法是专门用于描绘前景文字的,drawGuiContainerBackgroundLayer方法是用于描绘背景图像的。那个StatCollector是国际化组件会从语言库中取得相应键的对应语言文本,不过方法似乎老了点,但是不知道为什么竟然没有被@deprecated掉。drawGuiContainerBackgroundLayer的方法就是绑定纹理并描绘,详细的描绘方式参见szszss的博客http://www.hakugyokurou.net/wordpress/?p=734这里贴出关键的方法:
drawTexturedModalRect(x,y,u,v,w,h)
截取渲染引擎中绑定的纹理,并渲染到屏幕上.这个是最常用的绘图函数.
它的使用很特殊,你需要先向渲染引擎绑定纹理,首先你需要使用渲染引擎的bindTexture方法将一个ResourceLocation所指向的纹理 绑定入渲染引擎.绑定完毕后你才可以使用drawTexturedModalRect来绘制纹理.这里有6个参数作用的图解.
x,y : 将要在用户屏幕上绘制的纹理的左上角坐标.
u,v : 被绑定纹理中,需要绘制部分的左上角坐标.
w,h : 需要绘制的部分的宽和高.
绘制的流程可以这样理解:
渲染引擎会截取被绑定的纹理,将纹理中一个以(u,v)为左上角,(u+w,v+h)为右下角的矩形区域截下来,并将其绘制到用户屏幕上,其左上角位于点(x,y).
需要注意的是,drawTexturedModalRect只能绘制纹理大小为256×256的纹理!这 是因为它内部隐含了两个比例系数,用来将参数调整成适合OpenGL的形式,这两个系数是在假定纹理的长宽都是256的情况下制定的,若不为256的话, 则无法正确地截取纹理,具体的解决方法是使用drawModalRectWithCustomSizedTexture,或func_146110_a.
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2) {
// TODO Auto-generated method stub
super.drawGuiContainerForegroundLayer(par1, par2);
this.fontRendererObj.drawString(StatCollector.translateToLocal("RepairTable"), 65, 6, 4210752);
this.fontRendererObj.drawString(StatCollector.translateToLocal("container.inventory"), 8, this.ySize - 96 + 2, 4210752);
}
@Override
protected void drawGuiContainerBackgroundLayer(float var1, int var2,
int var3) {
// TODO Auto-generated method stub
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.renderEngine.bindTexture(new ResourceLocation("newmod","textures/gui/RepairTable.png"));
int var5 = (this.width - this.xSize) / 2;
int var6 = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(var5, var6, 0, 0, this.xSize, this.ySize);
}
主要的代码已经完成了,最后就是如何打开GUI和代理的分发。
因为是打开Block的GUI,我们重写Block的onBlockActivated,这个方法是玩家右键Block的时候会回调的函数。里边的参数照着位置填进去就好了。
@Override
public boolean onBlockActivated(World par1World, int par2, int par3,
int par4, EntityPlayer par5EntityPlayer, int par6, float par7,
float par8, float par9) {
// TODO Auto-generated method stub
par5EntityPlayer.openGui(mod_RepairTable.instance, 10, par1World, par2, par3, par4);
return true;
}
将CommonProxy的getServerGuiElement修改为如下所示,这个是前面openGui后方法的参数会发给IGuiHandler,IGuiHandler就在CommonProxy中进行处理。这里的服务端需要提供一个Container,我们根据传来的ID返回一个rtContainerRepairTable,这个ID只要自己约定好久可以了。rtContainerRepairTable的第一个参数已经由getServerGuiElement返回的player提供了,后面需要的rtTileEntityRepairTable可以通过world的getTileEntity来提供。
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
switch(ID) {
case 10:
return new rtContainerRepairTable(player.inventory, (rtTileEntityRepairTable)player.worldObj.getTileEntity(x, y, z));
}
return null;
}
改完CommonProxy后再重写ClientProxy,这里重写getClientGuiElement,注意,不是CommonProxy的getServerGuiElement。这里是返回rtGuiRepairTable,构造和rtContainerRepairTable大同小异。
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world,
int x, int y, int z) {
// TODO Auto-generated method stub
switch(ID) {
case 10:
return new rtGuiRepairTable(player.inventory, (rtTileEntityRepairTable)player.worldObj.getTileEntity(x, y, z));
}
return null;
}
最后一步,为上述的组件注册。修改mod主文件,重写preInit方法,注册Block,注册TileEntity。最重要的是注册IGuiHandler:NetworkRegistry.INSTANCE.registerGuiHandler(this, proxy);
epairTable = new rtBlockRepairTable(Material.rock)
.setBlockTextureName(MODID + ":" + "RepairTable")
.setBlockName("RepairTable").setCreativeTab(CreativeTabs.tabBlock);
GameRegistry.registerBlock(repairTable, "RepairTable");
GameRegistry.registerTileEntity(rtTileEntityRepairTable.class, "TileEntityRepairTable");
NetworkRegistry.INSTANCE.registerGuiHandler(this, proxy);
然后运行Client右键你的方块,就可以看到一个GUI展现出来了。
本章节的源码和贴图下载http://pan.baidu.com/s/1mg27nLm
如果教程中的代码有什么问题,以源码中的代码为准。
拥有TileEntity是必要条件吗?TAT
我看到铁砧并没有TileEntity呀。。
TileEntity不是必要,铁砧是通过Slot来实现逻辑功能,和工作台一样,你可以找找ContainerRepair和ContainerWorkbench,最明显的特征就是不能储存物品。
请问MC游戏屏幕中的默认GUI分辨率是多少?一直无法定位x,y这些参数QAQ
在继承GuiContainer的GUI类中,this.width和this.height分别是屏幕的宽和高,this.xSize和this.ySize分别是默认的GUI宽高(这个可以在构造之初进行修改),this.guiLeft和this.guiTop分别是描绘GUI的左上角坐标,常用定位GUI的就是这些参数
大神大神
物品的GUI应该怎么搞 要去看NBT么?
物品的GUI简单粗暴点和Block一样,引入IInventory,东西存ItemStack的NBT中,复杂但是实用点的是在IGuiHandler只返回Client部分,关于这个的教程以后有空了我再补上……
没看到源码啊
底下已经给出地址了啊- -http://pan.baidu.com/s/1mg27nLm
这方块可以当垃圾站啊,当里面有东西的时候,打掉方块不掉里面的东西啊
后续的教程有相关的处理