我的世界Minecraft源码分析(4):Item系统

发表于2017-09-07
评论0 5.3k浏览

这个系列通过对我的世界Minecraft源码进行拆分讲解,让大家可以清除的了解一款游戏是怎么一步步被实现出来的,下面就介绍Minecraft源码第四篇关于Item系统的内容。

Item简介

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

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



图1. Item示意图


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


类图



图2. Item系统UML图


从类图中我们可以看出,基本所有的Item都派生自基类Item,Item的几个重要属性

maxStackSize: 最大堆叠数;

maxDamage:最大伤害值;

potionEffect:药水效果;

unlocalizedName:没有本地化的名字,通常是英文。

BlockToItem:这是一个Set对象,由于Block对Item是多对一的关系,比如有很多种泥块,但是挖完掉落的只有一种。这个成员在被用在函数

[java] view plain copy
  1. public static Item getItemFromBlock(Block blockIn)  
  2. {  
  3.     return (Item)BLOCK_TO_ITEM.get(blockIn);  
  4. }  

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


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类 copy

  1. public class ItemAxe extends ItemTool  
  2. {  
  3.     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});  
  4.   
  5.     protected ItemAxe(Item.ToolMaterial material)  
  6.     {  
  7.         super(3.0F, material, EFFECTIVE_ON);  
  8.     }  
  9.   
  10.     public float getStrVsBlock(ItemStack stack, Block block)  
  11.     {  
  12.         return block.getMaterial() != Material.wood && block.getMaterial() != Material.plants && block.getMaterial() != Material.vine ? super.getStrVsBlock(stack, block) : this.efficiencyOnProperMaterial;  
  13.     }  
  14. }  



ItemFood

MC中的食物也有多种,生鸡肉,熟鸡肉,苹果等等,几个关键的属性 copy

  1. /** Number of ticks to run while 'EnumAction'ing until result. */  
  2.    public final int itemUseDuration;  
  3.    /** The amount this food item heals the player. */  
  4.    private final int healAmount;  
  5.    private final float saturationModifier;  
  6.    /** Whether wolves like this food (true for raw and cooked porkchop). */  
  7.    private final boolean isWolfsFavoriteMeat;  
  8.    /** If this field is true, the food can be consumed even if the player don't need to eat. */  
  9.    private boolean alwaysEdible;  
  10.    /** represents the potion effect that will occurr upon eating this food. Set by setPotionEffect */  
  11.    private int potionId;  
  12.    /** set by setPotionEffect */  
  13.    private int potionDuration;  
  14.    /** set by setPotionEffect */  
  15.    private int potionAmplifier;  
  16.    /** probably of the set potion effect occurring */  
  17.    private float potionEffectProbability;  

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


ItemStack

ItemStack可以是快捷栏中的一个格子,也可以是在地上的掉落物

Item所定义的都只是一些属性,数据的Update都放在ItemStack中去做。

ItemStack需要处理的数据只有两个,stackSize和ItemDamage。

类里的一些封装基本都是对Item的封装。


关于Item的损伤的代码如下

  1. /** 
  2.    * Damages the item in the ItemStack 
  3.    */  
  4.   public void damageItem(int amount, EntityLivingBase entityIn)  
  5.   {  
  6.       if (!(entityIn instanceof EntityPlayer) || !((EntityPlayer)entityIn).capabilities.isCreativeMode)  
  7.       {  
  8.           if (this.isItemStackDamageable())  
  9.           {  
  10.               if (this.attemptDamageItem(amount, entityIn.getRNG()))  
  11.               {  
  12.                   entityIn.renderBrokenItemStack(this);  
  13.                   --this.stackSize;  
  14.   
  15.                   if (entityIn instanceof EntityPlayer)  
  16.                   {  
  17.                       EntityPlayer entityplayer = (EntityPlayer)entityIn;  
  18.                       entityplayer.triggerAchievement(StatList.objectBreakStats[Item.getIdFromItem(this.item)]);  
  19.   
  20.                       if (this.stackSize == 0 && this.getItem() instanceof ItemBow)  
  21.                       {  
  22.                           entityplayer.destroyCurrentEquippedItem();  
  23.                       }  
  24.                   }  
  25.   
  26.                   if (this.stackSize < 0)  
  27.                   {  
  28.                       this.stackSize = 0;  
  29.                   }  
  30.   
  31.                   this.itemDamage = 0;  
  32.               }  
  33.           }  
  34.       }  
  35.   }  



小结

整个Item系统完成了一个巨大的工作:解耦。

通过继承来处理Item的多样性,通过科学合理的基类和多态来避免各种的if,else语句,最后再加上各种回调,整个世界都干净了。

要添加新的Item,只要在继承好对应的基类,然后override对应的方法就可以了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: