用户:MashKJo/1.12.2模组开发教程/6.ItemStack和Meta-hack:修订间差异

→‎ItemStack对象的使用:​ // Edit via Wikiplus
→‎ItemStack对象的使用:​ // Edit via Wikiplus
→‎ItemStack对象的使用:​ // Edit via Wikiplus
 
以及你可能已经发现了,ItemStack类中有很多方法是和Item重合的,这些方法一般都通过getItem方法来代理给ItemStack对象对应的Item来做。
 
== Meta-hack ==
那么,笔者终于要给读者解惑:ItemStack的meta字段,到底是什么意思??
 
好,这个字段,它的实际名称,为<code>itemDamage</code>,意思为“物品的损害值”,什么叫损害值?举个例子,一个满耐久的铁镐,其损害值为0;而如果用了它一次,且它挖掉的这个方块具有让它损耗耐久的功能(不能是TNT这种方块),那么它之后的损害值就变为了1——这下明白了吧?一个物品的最大损害值,实际上数值上等于它的最大耐久值。通过Item类的setMaxDamage方法可以控制物品的最大损害值。
 
然而,事情远没有这么简单。itemDamage这一字段,它的含义是会变的——由对应Item实例的<code>boolean hasSubtypes<code>字段控制,该字段默认为false。
 
但如果,我们用setHasSubtypes(true)来改变了这一字段呢?那么itemDamage,也即meta,它就不能用于指代损害值了,而是用于区分共享同一个Item实例的不同种物品了。这个技巧,有什么好处呢?一是为了防止物品占用过多的数字ID——虽然Forge的注册表不要求我们显式指定数字ID,但不代表数字ID就不存在了,它仍然存在于Minecraft底层之中,直到1.13它才彻底消失;而在1.12.2时期,数字ID是会被Forge动态分配的。二是在某些实现中它确实很符合人的惯常思维,也确实很好用,比如MC中有很多与颜色相关的物品,先写一个辅助类EnumDyeColor,再通过meta的值进行统筹分配颜色,确实是个不错的设计。然而为什么这玩意叫“Meta-hack”呢?那是因为Mojang不知道怎么想的,非得把物品的损害值和类型序数这两个概念耦合在一个字段中——这不是吃饱了撑的吗?所以实际上一个Item实例并不能同时拥有类型序数和耐久值,除非其中有一个被存在附加NBT中(实际上最好把itemDamage设定为类型序数,这样在设定物品模型的时候方便——setCustomModelResourceLocation那块正好传入一个meta值,不过把类型序数存进附加NBT也不是不行,写个Property Override就可以,这个内容后面会讲到的)。实际上meta这个概念在1.13直接被废除了,物品的耐久也被并入了附加NBT中。
 
而且,使用Meta-hack时,通常需要覆写Item类的一些getter,通过<code>switch(stack.getMetadata())</code>来控制同一个Item实例不同meta的ItemStack的各自的unlocalizedName、maxStackSize等参数。
 
而且还有一个问题:如何让带合适meta的ItemStack体现在创造模式物品栏中呢?答案是覆写Item类的getSubItems方法,向传入的NonNullList<ItemStack>中添加特定的ItemStack即可。注意这个方法不需要设定hasSubtypes为true才能起效果,因此这个方法也不是专门为meta-hack准备的——因为ItemStack里除了meta还有附加NBT啊,所以理论上你可以让陈列在创造模式物品栏中的ItemStack带上一定的附魔——暮色森林中的迷宫破坏者,在创造模式物品栏中,就是自带附魔的,原理就是这个。
 
另外,由于ItemStack对象的使用方法是随建随用,因此你甚至可以往这个NonNullList<ItemStack>中塞2个各方面都完全相同的2个ItemStack。
 
说教得差不多了,来段示例代码吧(这里笔者设定了:在创造模式物品栏中展示meta为0 - 3的该物品。
 
<code>src/main/java/net/tutorial_mod/item/ItemMultiMetadata.java:</code>
 
package net.tutorial_mod.item;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.tutorial_mod.TutorialMod;
public class ItemMultiMetadata extends Item{
public ItemMultiMetadata(){
this.setRegistryName(TutorialMod.MODID, "multimetadata_item");
this.setCreativeTab(CreativeTabs.MISC);
this.setHasSubtypes(true);
this.setNoRepair();
}
@Override
public String getUnlocalizedName(ItemStack stack){
return "item.multimetadata_item_" + stack.getMetadata();
}
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items)
{
if (this.isInCreativeTab(tab))
{
for(int i = 0; i < 4; i ++)
{
items.add(new ItemStack(this, 1, i));
}
}
}
}
 
最后,再说两点吧:
# 对于物品而言(猜猜看笔者为什么要特意强调“对于物品”),meta值最大为short类型的上限:65535。
# 除非你真的有特殊需求,否则,不要轻易用这个Meta-hack,因为它的实现莫名其妙的,且在1.13就会失效。那么有什么代替的方案吗?也简单,实际上原版就有这样的案例——ItemBucket类,原版的空桶、水桶、岩浆桶都是通过new这个类构造出来的(注意牛奶桶不在此列),因为ItemBucket的构造方法中有个Block参数,借此控制桶中到底装的是啥——空桶在被构造时,传入的是空气方块——合理吧?所以你更应该修改你的Item子类的构造方法,加点形参用于控制不同的变种,再多new几次这个类就行了,犯不着一定要用Meta-hack。
行政员、​优秀编辑者、​界面管理员、​监督员、​管理员、​小部件编辑者
3,334

个编辑