[1.7.10]当风过时的GUI教程(一)

By | 2015年7月16日

自上一次论坛更新已经过了挺久了,现在来重新更新一下GUI的教程,虽然论坛上面的是比较久远的教程,但还好Mojang一直没有对GUI部分进行大的改动,来来去去也是些修改某些方法的参数,改变贴图的载入方式而已。

经过一些事情之后慢慢的发现,服主们的话果然一个字都不能信

论坛帖子地址:http://www.mcbbs.net/thread-59702-1-1.html

Mod基础入门:http://frostmiku.fcteam.net/?p=9没有Mod基础的请先移步Mod入门

GUI不是一个单独组件,是需要靠多个组件互相协作的,因为东西多,我分成了几个部分来说明

第二篇:http://blog.windworkshop.net/?p=437

第三篇:http://blog.windworkshop.net/?p=440

第四篇:http://blog.windworkshop.net/?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

public class CommonProxy implements IGuiHandler { 
	@Override 
	public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
	{ 
		return null;
	} 
	@Override 
	public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
	{ 
		return null;
	}
}

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个参数作用的图解.
E4-1
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

如果教程中的代码有什么问题,以源码中的代码为准。

下一篇:[1.7.10]当风过时的GUI教程(二)

本文链接地址:http://blog.windworkshop.net/?p=36 »文章允许转载 ,转载请注明出处,谢谢。

[1.7.10]当风过时的GUI教程(一)” 的评论

  1. Greyd

    拥有TileEntity是必要条件吗?TAT
    我看到铁砧并没有TileEntity呀。。

    回复
    1. 当风过时 文章作者

      TileEntity不是必要,铁砧是通过Slot来实现逻辑功能,和工作台一样,你可以找找ContainerRepair和ContainerWorkbench,最明显的特征就是不能储存物品。

      回复
  2. KorTornado

    请问MC游戏屏幕中的默认GUI分辨率是多少?一直无法定位x,y这些参数QAQ

    回复
    1. 当风过时 文章作者

      在继承GuiContainer的GUI类中,this.width和this.height分别是屏幕的宽和高,this.xSize和this.ySize分别是默认的GUI宽高(这个可以在构造之初进行修改),this.guiLeft和this.guiTop分别是描绘GUI的左上角坐标,常用定位GUI的就是这些参数

      回复
    1. 当风过时 文章作者

      物品的GUI简单粗暴点和Block一样,引入IInventory,东西存ItemStack的NBT中,复杂但是实用点的是在IGuiHandler只返回Client部分,关于这个的教程以后有空了我再补上……

      回复
    1. 当风过时 文章作者

      底下已经给出地址了啊- -http://pan.baidu.com/s/1mg27nLm

      回复
      1. ...

        这方块可以当垃圾站啊,当里面有东西的时候,打掉方块不掉里面的东西啊

        回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据