Minecraft源码分析(4) - Item系统

Item简介

物品(Item)是只会出现在玩家的物品栏和手上的物体,它们不能在游戏的世界中放置。一些物品在使用时会在游戏的世界中放置方块和实体,它们在物品栏是物品,放置时是方块。一些符合以上特性的物体包括在放置时会变成实体的物品展示框,以及在放置时会变成一组方块的床。物品(和方块)会简短地在HUD上面展示它们的名字。

物品使用物品ID,方块使用方块ID。


Minecraft源码分析(4) - Item系统_第1张图片

图1. Item示意图


MC中的Item多种多样,各种块,各种家具,各种武器,各种药水,下面来看看Mojong是怎么来处理这些东西的。


类图



Minecraft源码分析(4) - Item系统_第2张图片

图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);
    }

当一个块被挖的时候就会调用这个函数进行查询。


ItemTool

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;
    }
}



ItemFood

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;

食物都可能带有一些buff,比如腐肉的buff就是 Hunger for 30 seconds (80% chance)。


ItemStack

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对应的方法就可以了。



你可能感兴趣的:(Minecraft源码分析(4) - Item系统)