物品(Item)是只会出现在玩家的物品栏和手上的物体,它们不能在游戏的世界中放置。一些物品在使用时会在游戏的世界中放置方块和实体,它们在物品栏是物品,放置时是方块。一些符合以上特性的物体包括在放置时会变成实体的物品展示框,以及在放置时会变成一组方块的床。物品(和方块)会简短地在HUD上面展示它们的名字。
物品使用物品ID,方块使用方块ID。
图1. Item示意图
MC中的Item多种多样,各种块,各种家具,各种武器,各种药水,下面来看看Mojong是怎么来处理这些东西的。
图2. Item系统UML图
从类图中我们可以看出,基本所有的Item都派生自基类Item,Item的几个重要属性
maxStackSize: 最大堆叠数;
maxDamage:最大伤害值;
potionEffect:药水效果;
unlocalizedName:没有本地化的名字,通常是英文。
BlockToItem:这是一个Set对象,由于Block对Item是多对一的关系,比如有很多种泥块,但是挖完掉落的只有一种。这个成员在被用在函数
public static Item getItemFromBlock(Block blockIn)
{
return (Item)BLOCK_TO_ITEM.get(blockIn);
}
MC中有很多种Tool,斧子,镐子,锄头等等,而且每种Tool都有各种的属性,斧子挖树很快,镐子挖地很快,锄头能够耕地....这些特性都在ItemTool类中实现。
先看一下ItemMaterial这个类的成员
/** The level of material this tool can harvest (3 = DIAMOND, 2 = IRON, 1 = STONE, 0 = WOOD/GOLD) */
private final int harvestLevel;
/** The number of uses this material allows. (wood = 59, stone = 131, iron = 250, diamond = 1561, gold = 32) */
private final int maxUses;
/** The strength of this tool material against blocks which it is effective against. */
private final float efficiencyOnProperMaterial;
/** Damage versus entities. */
private final float damageVsEntity;
/** Defines the natural enchantability factor of the material. */
private final int enchantability;
这几个成员就覆盖了所有的属性。
同时, 在MaterialTool中预制了几种Material,
WOOD,STONE,IRON,EMERALD,GOLD
在配置Item的时候只要引用这几种就可以了。
比如ItemAxe类
public class ItemAxe extends ItemTool
{
private static final Set EFFECTIVE_ON = Sets.newHashSet(new Block[] {Blocks.planks, Blocks.bookshelf, Blocks.log, Blocks.log2, Blocks.chest, Blocks.pumpkin, Blocks.lit_pumpkin, Blocks.melon_block, Blocks.ladder});
protected ItemAxe(Item.ToolMaterial material)
{
super(3.0F, material, EFFECTIVE_ON);
}
public float getStrVsBlock(ItemStack stack, Block block)
{
return block.getMaterial() != Material.wood && block.getMaterial() != Material.plants && block.getMaterial() != Material.vine ? super.getStrVsBlock(stack, block) : this.efficiencyOnProperMaterial;
}
}
MC中的食物也有多种,生鸡肉,熟鸡肉,苹果等等,几个关键的属性
/** Number of ticks to run while 'EnumAction'ing until result. */
public final int itemUseDuration;
/** The amount this food item heals the player. */
private final int healAmount;
private final float saturationModifier;
/** Whether wolves like this food (true for raw and cooked porkchop). */
private final boolean isWolfsFavoriteMeat;
/** If this field is true, the food can be consumed even if the player don't need to eat. */
private boolean alwaysEdible;
/** represents the potion effect that will occurr upon eating this food. Set by setPotionEffect */
private int potionId;
/** set by setPotionEffect */
private int potionDuration;
/** set by setPotionEffect */
private int potionAmplifier;
/** probably of the set potion effect occurring */
private float potionEffectProbability;
ItemStack可以是快捷栏中的一个格子,也可以是在地上的掉落物
Item所定义的都只是一些属性,数据的Update都放在ItemStack中去做。
ItemStack需要处理的数据只有两个,stackSize和ItemDamage。
类里的一些封装基本都是对Item的封装。
关于Item的损伤的代码如下
/**
* Damages the item in the ItemStack
*/
public void damageItem(int amount, EntityLivingBase entityIn)
{
if (!(entityIn instanceof EntityPlayer) || !((EntityPlayer)entityIn).capabilities.isCreativeMode)
{
if (this.isItemStackDamageable())
{
if (this.attemptDamageItem(amount, entityIn.getRNG()))
{
entityIn.renderBrokenItemStack(this);
--this.stackSize;
if (entityIn instanceof EntityPlayer)
{
EntityPlayer entityplayer = (EntityPlayer)entityIn;
entityplayer.triggerAchievement(StatList.objectBreakStats[Item.getIdFromItem(this.item)]);
if (this.stackSize == 0 && this.getItem() instanceof ItemBow)
{
entityplayer.destroyCurrentEquippedItem();
}
}
if (this.stackSize < 0)
{
this.stackSize = 0;
}
this.itemDamage = 0;
}
}
}
}
整个Item系统完成了一个巨大的工作:解耦。
通过继承来处理Item的多样性,通过科学合理的基类和多态来避免各种的if,else语句,最后再加上各种回调,整个世界都干净了。
要添加新的Item,只要在继承好对应的基类,然后override对应的方法就可以了。