IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    qp120291570发表于 2016-05-09 23:43:50
    love 0

    Item简介

    物品(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);
        }

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


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





沪ICP备19023445号-2号
友情链接