我的世界Minecraft源码分析(4):Item系统
这个系列通过对我的世界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是多对一的关系,比如有很多种泥块,但是挖完掉落的只有一种。这个成员在被用在函数
- 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类 copy
- 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中的食物也有多种,生鸡肉,熟鸡肉,苹果等等,几个关键的属性 copy
- /** 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对应的方法就可以了。