- UID
- 14084
- 阅读权限
- 100
- 注册时间
- 2014-4-6
- 最后登录
- 1970-1-1
- 在线时间
- 小时
- 人气
- 点
- MC币
- 个
- 贡献
- 点
TA的每日心情 | 开心 2016-7-1 20:14 |
---|
签到天数: 282 天 [LV.8]以坛为家I
|
原帖:http://www.hakugyokurou.net/wordpress/?p=163
Hello Guys(Gays)~我又粗线了,这次继续更新MOD开发教程,不过。。貌似Forge API的开发团队的创始人cpw走了,教程有何意义呢?不过会有新的人接手开发的啦,那我们继续,一直没更新是因为电脑重装系统,代码全没了,重新码字ing~
好的
现在我们更新一下,上期我们说到方块,那上次的方块是不能合成什么的,只能在创造模式获得,这次我们来做合成、炼制、烧制的合成表吧,这次比较难,有的不想看的可以放弃(下期更难)。
本章我们要进行:
创建一个新的矿物(砖块):Diracium
创建一个新的矿锭(物品):Diracium Ingot
创建一个新的工具(物品):Dirac Omni Tool
创建一个新的砖块知识点:创建一个新砖块的流程
///////////////////////////////////////////////////////////////////////////////////////////////////////////
在旧教程中,这一部分几乎是一笔带过的,然而现在我却费了很大功夫,反复思考该如何阐述清这个概念,因为Minecraft的砖块机制和很多人想象中都不一样.这里我只能简述一些,更详细的原理会在别处讨论.Minecraft中,一个Block类或其派生类的实例即一种砖块,或许这个用语言描述起来比较难,但请看下列代码:
Block newBlock = new Block(1000,Material.rock);
这个代码是创建一个编号(ID)为100,材质为石材质的砖块.(ID的范围是0~4095,其中前几百个已经被使用了~…在http://www.minecraftforge.net/forum/index.php/topic,638.0.html上能找到哪些编号已经被流行Mod使用了)
很显然,这个结论和大多数教程都是相违的,很多教程,包括我过去写的旧教程,都强调一个派生自Block类的砖块类才代表一个砖块,事实上这并不准确.这个砖块并没有使用一个新的类,但它现在已经可以使用了.那么,为什么我们还要说新建一个砖块类才代表一种新砖块?因为类可以承载各种方法(Method),可以实现各种特殊的功能.一个砖块类的最简形式可以是这样public class MyBlock extends Block{
public MyBlock(int id, Material material) {
super(id, material);
}
而新建一种砖块的代码则是这样,这部分代码添加在你的Mod主类static public Block myBlock;
这个静态公共变量myBlock便代表你的新砖块,但它还没有初始化,初始化要在载入Mod时进行,在你的Mod主类的preLoad方法(即一个拥有@EventHandler,拥有一个FMLPreInitializationEvent参数的方法)中添加[
myBlock = new MyBlock(1000,Material.rock);
myBlock.setUnlocalizedName("MyBlock");
myBlock.func_111022_d("MyBlock");
myBlock.setHardness(1.5f); //可省略
myBlock.setResistance(10.0f); //可省略
myBlock.setLightValue(0.0f); //可省略
myBlock.setStepSound(Block.soundStoneFootstep); //可省略
myBlock.setCreativeTab(CreativeTabs.tabBlock); //可省略
GameRegistry.registerBlock(myBlock, "MyBlock");
LanguageRegistry.addName(myBlock, "我的第一个砖块");
这包含了创建一个砖块的全部过程,new MyBlock(1000,1,Material.rock)是创建一种基于MyBlock类,编号为1000,材质类型为石材质的砖块.
setUnlocalizedName是设置砖块的名称,名称用于游戏内部处理使用,最好使用纯英文.
func_111022_d是Minecraft1.6新增的一个方法,MCP组还尚未给它指定名字.它的作用是用来指定纹理的名字.
(注:在最新版的Forge中,func_111022_d已经被订正为setTextureName)
setHardness是设置砖块的硬度,这个硬度是相对于徒手而言的,泥土是0.5,石头是1.5,大部分矿石是3.0.
setResistance是设置对爆炸的抗性,石头是10.0.
setLightValue是设置发光亮度,范围是0.0~1.0,南瓜灯,萤石和岩浆是1.0,通往下界的传送门是0.75.采集中的红石是0.625.
setStepSound是设置踩在上面的脚步声.
setCreativeTab是设置在创造模式中它在哪个菜单分类里.
GameRegistry.registerBlock是注册一个砖块,只有在被注册后,砖块才能正式使用.自从MC1.46后,Forge建议开发者为registerBlock额外提供一个”专属名称”参数,这个名称主要用于检测Mod冲突.这东西真的有用吗?毕竟普通的砖块名称也能检测出冲突的…
LanguageRegistry.addName是为砖块添加一个显示名称,即游戏里你看到的名字.
事实上,设置砖块属性的方法远不止这些,但常用的只是这几个.顺便一提,让这个砖块可以被特定工具快速开采的方法是:
1
MinecraftForge.setBlockHarvestLevel(myBlock, "shovel", 0);
“shovel”的位置是工具类型,可以是”pickaxe”(镐), “shovel”(铲子)或”axe”(斧头)
最后的那个0是工具材质类型,0是木质,1是石质,2是铁质,3是钻石质最后再顺便一提,利用生成器模式(Builder,程序模式的一种),以上一堆代码可以被简写为.
1
2
3
myBlock = (new MyBlock(1000,Material.rock)).setUnlocalizedName("MyBlock").func_111022_d("MyBlock").setHardness(1.5f).setResistance(10.0f).setLightValue(0.0f).setStepSound(Block.soundStoneFootstep).setCreativeTab(CreativeTabs.tabBlock);
GameRegistry.registerBlock(myBlock,"MyBlock");
LanguageRegistry.addName(myBlock, "我的第一个砖块");
至面向对象编程控们:
也许你会想当然的认为Minecraft中每一个砖块就是它的类的实例,可事实上如果这样做,地图上同时出现的几万个类的实例会把Java虚拟机爆掉的,别忘了Java可没结构体(Struct)啊…所以Minecraft中的砖块类的实质比较倾向于享元模式,一个砖块类的实例仅代表一种砖块,你在游戏中看到的一个个砖块只是在地图数据中批量储存的砖块ID而已…
另外你想知道为什么砖块要被注册后才能使用吗?想知道注册砖块时都做了什么吗?在物品部分我会解答…
///////////////////////////////////////////////////////////////////////////////////////////////////////////在你的package中新建一个类,Minecraft对Block类的命名规范是BlockXXX,所以我取名叫BlockDiracOre.
创建完毕后,添加这些代码.
1
2
3
4
5
public class BlockDiracOre extends Block{
public BlockDiracOre(int id, Material material) {
super(id, material);
}
}
(看上去就像知识点里的一样?你看得太多了…)同样,Eclipse会报错说没有导入相关的Package,让它自动修正.在Mod主类中添加
1
static public Block diracBlock;
在preLoad方法中添加
1
2
3
4
5
6
7
8
9
10
11
diracBlock = new BlockDiracOre(2000, Material.rock);
diracBlock.setUnlocalizedName("diracOre");
diracBlock.func_111022_d("diracon:diracOre");
diracBlock.setHardness(1.5f);
diracBlock.setResistance(10.0f);
diracBlock.setLightValue(0.0f);
diracBlock.setStepSound(Block.soundStoneFootstep);
diracBlock.setCreativeTab(CreativeTabs.tabBlock);
GameRegistry.registerBlock(diracBlock,"diracOre");
LanguageRegistry.addName(diracBlock, "迪拉克矿石");
MinecraftForge.setBlockHarvestLevel(diracBlock, "pickaxe", 0);
现在我们创建了一个砖块,但还有个问题,它没有纹理!我们接下来得为他制作纹理文件.知识点:纹理与资源文件
///////////////////////////////////////////////////////////////////////////////////////////////////////////
老MODer应该会知道Minecraft在1.5.0以前采用纹理集的形式储存纹理,在1.5.0之后,Minecraft改为使用单个文件储存纹理,对开发者而言确实方便了不少.因此这个知识点也缩水了许多.纹理是资源文件的一种,在1.5.0版本以后,Minecraft的纹理文件为一个个.png格式的图片.
过去,这些文件被要求放在textures/items(物品纹理)或textures/blocks(砖块纹理)中.
但是现在,情况有些改变.在Minecraft1.6重构了文件结构后,资源文件的存放位置发生了变化,它的资源文件都被放入了assets目录内,FML也采用了类似的作法,同时它还采用了类似于命名空间的规则,即Mod除了需要指定纹理的名称外,还需要指定它所在的文件夹.
首先,在声明纹理名称(即func_111022_d,或新版Forge的setTextureName方法)时,需要以”文件夹名:纹理名”的格式在纹理名前面加上文件夹名,比如上面的”diracon:diracOre”.
然后,MCP目录下的src/minecraft目录(没错,就是你的源代码目录,我也不知道FML怎么想的…但现实即是如此)中新建一个assets目录,在assets再新建你的文件夹目录(比如diracon),再按照旧规则往里面放文件.
[图:无比丧心病狂的文件架结构]新的资源载入方式十分让人困惑,我为此也尝试寻找其它解决方案,包括省掉声明纹理名时的文件夹名,结果就是当你省掉文件夹名时,FML会默认判定为位于minecraft文件夹下,即”diracOre”等同于”minecraft:diracOre”,丝毫没有省任何力气.如果你发现更好的办法的话,可以在下面留言告诉大家.
(如果你使用Eclipse来调试Mod的话,可以采用这个办法:http://www.hakugyokurou.net/wordpress/?p=609)另外虽然纹理载入策略是可以自定义的,但也并不能重新定义目录结构…只能定义纹理名.最后还有一点,砖块纹理要存在blocks文件夹下,物品纹理则要存放在叫做items的文件夹下,两文件夹都要置于textures文件夹中.
///////////////////////////////////////////////////////////////////////////////////////////////////////////用绘图工具(比如PS)随便画一个纹理…我是在官方的矿石的基础上瞎涂的.(我使用的纹理尺寸是16×16,不过1.5.0以后MC已经支持大尺寸的高清纹理了.)之后将它保存为文件名和砖块的名称相同的png文件(如diracOre.png),并放到src/minecraft/assets/diracon/textures/blocks文件夹中.如果你坚持做到这一步,那么恭喜你,你已经完成了你的第一个砖块!保存,然后运行recompile.bat编译,之后运行startclient.bat测试吧.但是…有一个问题,我们该怎么获取我们的新砖块呢?很简单,用Minecraft自带的控制台指令(别忘了在创建世界时选上Allow Cheats:ON)…按T打开对话框,先随便说一句话获得自己的玩家姓名,然后输入/give [你的名字] [你的物品的id] [数量](PS:其实,如果你是在创造模式下的,可以直接用菜单调出来…)创建一个新的物品接下来我们要为这个矿创建一个冶炼产物:Diracium Ingot.知识点:创建一个新物品的流程
///////////////////////////////////////////////////////////////////////////////////////////////////////////
物品和砖块的原理差不多,同样是一个物品类的实例将会作为一种物品.
一个最简的物品类是这样
1
2
3
4
5
public class MyItem extends Item {
public MyItem(int par1) {
super(par1);
}
}
它的构造函数是它的ID(编号),物品和砖块虽然不是使用同一套ID系统,但两者之间仍有一些关系,游戏中支持的ID范围是0~31743,按最简单的话说,0~3839最好别碰,3840~31743可供物品使用.
但如果你要深究的话,这得从最早的MC聊起,首先你得清楚一件事,你创建一个砖块的同时,游戏还会创造一个与之对应的物品,这个物品派生自ItemBlock类,你注册砖块时其中有一步就是创建一个与被注册砖块相配的物品.这个物品的ID与砖块的ID相同.
最早的Minecraft的砖块只有256个空位,由于”砖块的’影子’物品的ID必须与砖块本体ID相同”和”由于物品ID独立于砖块ID而存在,所以固然要以0起始”这两个限制,你在创建物品时,游戏会偷偷对物品ID进行换算,换算公式就是”实际ID=你设定的物品ID+256″,换句话说一个初始化时物品ID设定为1的物品,它真正的ID是257,在游戏里你要输入/give xxx 257 1才能获得那个物品.
然而在之后发展中,砖块ID渐渐枯竭,最终有一天,Minecraft将砖块ID上限扩增至4096,于是乎0~4095就成了砖块专用ID,然而Notch/Forge没有修改物品ID的换算方式,一个ID为300的砖块会产生一个ID为300的物品,然而如果你创建了一个ID为44的物品,那么它的实际ID(44+256=300)会和砖块的物品冲突.
因此我建议你最好别碰0~3839,3840~31743足够用了…而新建一种砖块的代码则是这样,这部分代码添加在你的Mod主类
1
static public Item myItem;
这个静态公共变量myItem便代表你的新物品,但它还没有初始化,初始化要在载入Mod时进行,在你的Mod主类的load方法(或者是任意一个拥有@init的方法)中添加
1
2
3
4
5
6
myItem = new MyItem(4000);
myItem.setUnlocalizedName("MyItem");
myItem.func_111206_d("MyItem");
myItem.setMaxStackSize(64);
myItem.setCreativeTab(CreativeTabs.tabMaterials);
LanguageRegistry.addName(myItem, "My Item");
new MyItem(4000)是创建一个基于MyItem类,ID为4000的物品.
setUnlocalizedName是设置物品的名称,它的作用和砖块的同名方法一样,用于游戏内部处理.不会直接显示出来.
func_111206_d是设置物品的纹理名,和设置砖块的纹理名类似.
(注:在最新版的Forge中,func_111206_d已经被订正为setTextureName)
setMaxStackSize是设置最大堆叠数量.
setCreativeTab是设置其在创造模式菜单中的分类位置.
LanguageRegistry.addName是为它添加显示名称,即游戏里你看到的名字.
///////////////////////////////////////////////////////////////////////////////////////////////////////////创建一个叫ItemDiracIngot的类,使它继承Item类.构造函数采用默认的即可.之后在Mod主类中添加
1
static public Item diracIngot;
在preLoad方法中添加
1
2
3
4
5
6
diracIngot = new ItemDiracIngot(4000);
diracIngot.setUnlocalizedName("diracIngot");
diracIngot.func_111206_d("diracon:diracingot");
diracIngot.setMaxStackSize(64);
diracIngot.setCreativeTab(CreativeTabs.tabMaterials);
LanguageRegistry.addName(diracIngot, "迪拉克矿锭");
然后为矿物画一个纹理,我同样是把官方的铁锭给涂紫了…然后保存文件到相应的位置(既然我们上头写的是”diracon:diracingot”,它又是个物品,因此纹理要保存到src/minecraft/assets/diracon/textures/items中,纹理名要设为diracingot.png),之后就可以开始测试了,如果你用控制台刷物品的话,别忘了我之前说的物品ID换算原则,即实际ID=物品ID+256,我给物品赋的物品ID为4000,所以我这里输入的ID是4256.顺便轻松一下,我这次Random的世界开局挺好的,Seed:8654197223450583135.开局在草原,正东方有一个村子,旁边还有露天矿洞入口和露天岩浆,正南方(似乎是)还有一个超小空岛.创建一个工具工具是一种特殊的物品,它的定义是能对特定的砖块产生挖掘速度加成,出于这个定义,锄头(Hoe)并不属于工具,因为它不会对挖掘砖块产生任何加成.最初,所有工具的物品类都派生自ItemTool类,但如果基于Forge的话,任何一个物品都能被变成工具.知识点:利用Forge创建工具
///////////////////////////////////////////////////////////////////////////////////////////////////////////
Forge的MinecraftForge类提供了setToolClass方法来使一个物品成为一个工具,它的参数是setToolClass(Item tool, String toolClass, int harvestLevel),tool是要成为工具的物品,toolClass是工具类型,用字符串来表示,默认已有的类型是”pickaxe”(镐),”shovel”(铲子)和”axe”(斧子),可以自定义.harvestLevel是采矿强度,当工具的harvestLevel大于等于砖块的BlockHarvestLevel时,这个砖块就可以被加速开采,具体增加的速度由工具的材质和砖块的硬度决定.例如:
1
MinecraftForge.setToolClass(myTool,"pickaxe",4);
它的效果是将myTool转化为工具,工具类型为pickaxe,采矿强度比钻石还强(但依然采不了基岩).顺便再温习一下如何让一个砖块能够被采集.
MinecraftForge.setBlockHarvestLevel(myBlock, "pickaxe", 3);
这个是让myBlock砖块只能被类型为pickaxe,强度为4的工具采集.由于原版的最强工具钻石工具也只有3强度,所以只有你的新物品能采集它.Forge的这个方法的好处是做到了类型统一,mod作者不用指定自己的砖块能被某几种工具采集,而只需指定自己的砖块能被哪一类工具采集,这样即使是别人开发了新mod,只要双方的工具类型一致,工具就能正常工作.但缺点就是一个工具不能有多种类型,对一个工具多次setToolClass只会让最后一次设置生效.解决的办法也不是没有,就是重写物品类的getStrVsBlock和canHarvestBlock方法.
///////////////////////////////////////////////////////////////////////////////////////////////////////////在这里我胡搞了一个叫Dirac Omni Tool的东西.先要说一下Dirac是什么,Dirac的中文写作迪拉克,读作xi jian(…….),本教程中的Diracium(迪拉克元素)是一种能从无穷无尽的迪拉克之海(反物质世界/纯能量世界)中吸取能量,凭借这些能量来创造各种在我们的宇宙中因为能量守恒定律而无法做到的事情!首先为物品画一个图,我随便瞎图了一个…之后将它存在相应文件夹下,名字叫做diracomnitool.png,然后创建一个物品类.之后我写了一个很简单通用的代码,即使是无法理解它的新人也可以直接拿来用.(不过真的是非常简单…)
public class ItemDiracOmniTool extends ItemTool{
public static final String[] HARVESTABLE = new String[] {"pickaxe","shovel","axe"};
//构造函数
public ItemDiracOmniTool(int id)
{
//调用基类的构造函数,参数分别是物品ID,攻击实体(Entity)造成的伤害,工具材质(ToolMaterial),能被这种工具加速挖掘的砖块.
//其中,第四个参数只对原生MC有用,对安装了Forge的MC是无效的.
super(id, 100, EnumToolMaterial.EMERALD, new Block[] {});
}
//获取对砖块的破坏威力 - 无meta版
@Override
public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block) {
return getStrVsBlock(par1ItemStack, par2Block, 0);
}
//获取对砖块的破坏威力 - 有meta版
@Override
public float getStrVsBlock(ItemStack stack, Block block, int meta) {
for(String toolType : HARVESTABLE)
{
//如果不为-1,那么就说明这种工具能对这个砖块产生加成
if(MinecraftForge.getBlockHarvestLevel(block, meta, toolType) != -1)
{
return this.efficiencyOnProperMaterial;
}
}
return 1.0f;
}
[size=1em]//判断能否挖掘砖块
[size=1em] @Override
[size=1em] public boolean canHarvestBlock(Block par1Block) {
[size=1em] for(String toolType : HARVESTABLE)
[size=1em] {
[size=1em] //如果不为-1,那么就说明这种工具能挖掘这个砖块
[size=1em] if(MinecraftForge.getBlockHarvestLevel(par1Block, 0, toolType) != -1)
[size=1em] {
[size=1em] return true;
[size=1em] }
[size=1em] }
[size=1em] return false;
[size=1em] }
[size=1em]}
之后在Mod主类中添加:[size=1em][size=1em]1
[size=1em][size=1em]static public Item diracOmniTool;
在preLoad方法中添加:[size=1em][size=1em]1
[size=1em]2
[size=1em]3
[size=1em]4
[size=1em]5
[size=1em][size=1em]diracOmniTool = new ItemDiracOmniTool(4001);
[size=1em]diracOmniTool.setUnlocalizedName("diracOmniTool");
[size=1em]diracOmniTool.func_111206_d("diracon:diracomnitool");
[size=1em]diracOmniTool.setCreativeTab(CreativeTabs.tabTools);
[size=1em]LanguageRegistry.addName(diracOmniTool, "迪拉克万能工具");
之后进游戏,开一个生存模式的存档,然后用/give指令获得一个新工具(别忘了换算实际ID,实际ID=物品ID+256),之后用它四处敲敲试试.这玩意敲什么动物都是一击必杀的,敲什么砖块也都敲得动(基岩除外…)接下来我们试试Forge提供的设置工具的功能.在我们刚才的代码下面添加:[size=1em][size=1em]1
[size=1em][size=1em]MinecraftForge.setToolClass(diracOmniTool, "dirac", 4);
然后,找到创建DiracBlock砖块的代码,将[size=1em][size=1em]1
[size=1em][size=1em]MinecraftForge.setBlockHarvestLevel(diracBlock, "pickaxe", 0);
改为[size=1em][size=1em]1
[size=1em][size=1em]MinecraftForge.setBlockHarvestLevel(diracBlock, "dirac", 3);
(如果没有的话就自己写上)然后将它的[size=1em][size=1em]1
[size=1em][size=1em]diracBlock.setHardness(1.5f);
改为[size=1em][size=1em]1
[size=1em][size=1em]diracBlock.setHardness(10.0f);
然后再进游戏测试,用/give刷出迪拉克矿石,然后摆在地上,然后分别拿迪拉克工具和石工具敲一敲,问题来了,为啥石工具拆起来反而更快啊!很遗憾这个是Forge的处理机制造成的…当Forge发现当前砖块不在工具的”加成列表”内时,就会转由它的砖块材质(Material)来判断,由于我们现在的DiracBlock的材质是rock(石头),正好可以被石工具加速采集…所以呢,我们就自己新建一个材质吧.创建一个砖块材质砖块材质(Material)是对砖块的一个分类,对它的具体定义请看Plus篇的Block部分.知识点:创建砖块材质
///////////////////////////////////////////////////////////////////////////////////////////////////////////
一个材质是一个Material类的实例,原版MC已有的材质全部在Material下,可以通过诸如Material.rock之类的来调用.
理论上说,材质可以通过生成器模式来方便地创建,但是由于Minecraft的制作组脑子长炮了,那群傻子将生成器的参数方法全设为了内部保护(protected),迫使我们即使是创建一个最简单的材质也不得不自己动手新建一个材质类,或者使用反射.
Material类的构造函数的参数是一个MapColor,它是指在游戏内置的物品地图中此砖块显示的颜色.
Material在创建时的参数方法包括:
setTranslucent 使其可透过光
setRequiresTool 需要有正确的工具才会有掉落
setBurning 可以被点燃
setReplaceable 让这个砖块可以被其他砖块直接取代,比如雪,你直接往雪上盖个砖块就会自动把雪覆盖消失.
setNoPushMobility 让这个砖块无法被活塞推动,活塞的塞子会直接穿过它.
setImmovableMobility 让这个砖块无法被活塞推动,并且会挡住活塞的塞子.
setAdventureModeExempt 让砖块无节操化,可以被玩家用任意东西破坏,成为可以被任何人用任何东西推倒的街角自行车,妖の惨劇に濡れて…此外,除了Material类以外,还有几个派生自Material的类:
MaterialLogic 没有碰撞体积的砖块,不会影响它的正下方的草,可以被玩家用任意物品破坏.
MaterialLiquid 液体
MaterialPortal 传送门的材质
MaterialWeb 蜘蛛网的材质
MaterialTransparent 火的材质
///////////////////////////////////////////////////////////////////////////////////////////////////////////首先创建一个材质类,我给它起名叫MaterialDirac.之后让它继承Material类.并为它添加上代码.[size=1em][size=1em]1
[size=1em]2
[size=1em]3
[size=1em]4
[size=1em]5
[size=1em]6
[size=1em]7
[size=1em][size=1em]public class MaterialDirac extends Material{
[size=1em]public MaterialDirac(MapColor par1MapColor) {
[size=1em] super(par1MapColor);
[size=1em] setRequiresTool(); //让它只能被特定工具采集
[size=1em] }
[size=1em] }
接下来在mod主类中添加:[size=1em][size=1em]1
[size=1em][size=1em]static public Material diracMaterial;
然后在preLoad方法的开头添加:[size=1em][size=1em]1
[size=1em][size=1em]diracMaterial = new MaterialDirac(MapColor.stoneColor);
然后将[size=1em][size=1em]1
[size=1em][size=1em]diracBlock = new BlockDiracOre(2000, Material.rock);
改为[size=1em][size=1em]1
[size=1em][size=1em]diracBlock = new BlockDiracOre(2000, diracMaterial);
之后进入游戏测试,用迪拉克万能工具敲迪拉克矿石虽然费时间但终归是能敲动(耗时大约16秒,因为我们设置的硬度太高了,你可以降低一些),但如果用石质工具敲嘛…完全敲不动…然而这个东西很无聊,所以我就去掉了,让任何工具都能挖掘它,因为待会我们要制作万能工具的合成,它的合成需要用到迪拉克矿锭,迪拉克矿锭通过烧迪拉克矿石取得,如果矿石需要万能工具才能挖的话….就成一个无解的循环了.物品栈(ItemStack)知识点:物品栈的概念
///////////////////////////////////////////////////////////////////////////////////////////////////////////
在继续下面的内容之前,我们先得说明物品栈的概念.物品栈是游戏中玩家实际拿在手里的物品,例如”10个煤”,”1把铁镐”,”1辆尚未放置的矿车”.一个物品栈主要由3个参数组成(事实上不止3个,但在”速成班”中我们知道这3个就足够了…):此物品栈的物品的实际ID,物品数量,物品损伤度(ItemDamage).
物品栈的物品的实际ID很好理解,比如这是一打煤炭.那么通过查表可知煤炭的物品ID是7,那么这个物品栈储存的就是煤炭的实际ID:263.
物品数量更好理解…
物品损伤度有两种作用,对于工具武器装甲等可以使用的物品来说,它就是代表当前物品的损伤度,当损伤度超过最大耐久后,就会坏掉.而它的另一种作用,就是对于拥有子类型(Subtype)的物品来说,代表当前为哪种类型,比如煤炭就有2个子类型,0为煤矿掉出的煤炭,1为烧木头烧出的木炭.举例,创建一个数量为16的木炭的物品栈的代码是:[size=1em][size=1em]1
[size=1em][size=1em]new ItemStack(Item.coal, 16, 1)
创建一个数量为1的煤炭的物品栈的代码是:[size=1em][size=1em]1
[size=1em][size=1em]new ItemStack(Item.coal, 1, 0)
对于物品损伤度为0的物品栈来说,代码可以简写为:[size=1em][size=1em]1
[size=1em][size=1em]new ItemStack(Item.coal, 1)
对于物品损伤度为0,数量为1的物品栈,还可简写为:[size=1em][size=1em]1
[size=1em][size=1em]new ItemStack(Item.coal)
///////////////////////////////////////////////////////////////////////////////////////////////////////////添加一个冶炼公式知识点:添加一个冶炼公式
///////////////////////////////////////////////////////////////////////////////////////////////////////////
MC专门提供了一个类来管理冶炼公式:FurnaceRecipes,它是个伪静态类,你可以直接跳过Forge,对FurnaceRecipes直接操作,你可以通过调用FurnaceRecipes的smelting方法来获取FurnaceRecipes类的实例.
它的代码是:[size=1em][size=1em]1
[size=1em][size=1em]FurnaceRecipes.smelting().addSmelting(myBlock.blockID, new ItemStack(myItem), 100f);
其中,myBlock是你的矿石砖块,myItem是冶炼产物,100f是冶炼后获得的经验.Minecraft中烧一个金矿才1f,烧一个碎石只有0.1f…)或者,你可以使用Forge的代码,FML的GameRegistry类提供了addSmelting方法来添加冶炼公式,它的参数和FurnaceRecipes的同名方法一样.
图:GameRegistry VS FurnaceRecipes,其实两者本质都一样.那么两者的区别在哪?答案是:没有区别…如果你看一下FML的代码的话,就会发现GameRegistry的addSmelting只是对FurnaceRecipes的addSmelting简单包装了一下,没有进行任何额外的操作…所以说,用哪个都一样- -||.
///////////////////////////////////////////////////////////////////////////////////////////////////////////在mod主类的load方法内添加:[size=1em][size=1em]1
[size=1em][size=1em]GameRegistry.addSmelting(diracBlock.blockID, new ItemStack(diracIngot), 100f);
(其实并非一定要在load中添加合成和冶炼,preLoad,load,postLoad任何一个都可以处理物品砖块的创建和冶炼合成的添加.)之后进游戏试试.当你将产物从炉子里取出来时,能获得不少经验.添加一个合成知识点:添加一个合成公式
///////////////////////////////////////////////////////////////////////////////////////////////////////////
MC有一个专门的类:CraftingManager用于管理物品的合成,CraftingManager类下的getInstance方法可以返回一个可用的CraftingManager.你可以用这个办法来直接获取一个CraftingManager并为它添加合成表(使用addRecipe方法),但为了保险起见,我们习惯使用FML提供的addRecipe方法.FML的addRecipe方法可以直接添加一个合成表.
addRecipe的参数是(ItemStack itemstack, Object aobj[])
ItemStack是物品栈的实例.
Object aobj[]是一个Object数组,这意味着你可以按照一定规范来随意的写.然而想要仅凭文字来解释它的使用规范实在太难了.所以我以牌子的合成为例来解释.[size=1em][size=1em]1
[size=1em][size=1em]addRecipe(new ItemStack(Item.sign, 3), new Object[] {"###", "###", " X ", '#', Block.planks, 'X', Item.stick});
如你所见,首先它创建了一个牌子的物品栈,并将数量设为3,这样每次合成完后能获得3个牌子,之后新建了一个Object数组.这个数组描述了一个合成图,并解释了合成图的内容.
首先,这个数组开头由1~3个字符串组成,分别代表合成表的第1~3行.每个字符串至少有3个字符.分别代表本行的第1,2,3个位置.空位使用空格来填补.
之后,是对合成图的解释,解释的格式是”被解释的字符,含义”.如此重复直到所有的字符都被解释完毕.
‘#’,Block.planks 就是将’#’字符解释为木头砖块.
‘X’, Item.stick 是将’X’字符解释为木条物品.
因此,你在游戏中可以按照
木块 木块 木块
木块 木块 木块
木条
的方式来合成牌子.对于拥有子类型(Subtype)的原料来说,还可以用这种方式来解释:[size=1em][size=1em]1
[size=1em]2
[size=1em][size=1em]addRecipe(new ItemStack(Block.planks, 4, 0), new Object[] {"#", '#', new ItemStack(Block.wood, 1, 0)});
[size=1em] addRecipe(new ItemStack(Block.planks, 4, 1), new Object[] {"#", '#', new ItemStack(Block.wood, 1, 1)});
这样就可以让不同的子类型合成出不同的产物,或者强制要求只有特定的子类型才能参与合成.此外,FML还提供了一种方式来添加合成,它允许你创建一个专门的类来判断合成条件,功能更加强大,但在这里它超出了”速成班”的范围…所以就先不说了
///////////////////////////////////////////////////////////////////////////////////////////////////////////在mod主类的load方法内,我们之前添加冶炼公式的地方的下面,添加:[size=1em][size=1em]1
[size=1em][size=1em]GameRegistry.addRecipe(new ItemStack(diracOmniTool, 1), new Object[] {"###", "#X#", " X ", '#', diracIngot, 'X', Item.stick});
之后进游戏测试.至此,第三期结束
下期预告:
教你怎样做个实体生物
PS:帖子中有什么size我也不知道什么情况,代码从Eclipse复制出来就这样,今天讲的太多了,明天慢慢去除
PS2:其实这个讲完准备去写一个番外篇,在Eclipse里调试Mod
PS3:是不是感觉备注写的很有个性呢,我只是充数的
PS4:同上
PSP:同上
PSV:同上
PS5:我怎么来了,索尼还没有发行呢 |
|