1. 生物实体属性(Attribute)

生物实体的属性(Attribute)是指实体身上一系列描述生物“特征”的数值。这些数值包括:

  • 生命值上限
  • 跟随距离
  • 移动速度
  • 飞行速度
  • 攻击力
  • 攻击速度
  • 护甲
  • ……

在中文 Minecraft Wiki 上可以找到关于它的详细说明,本文将重点解释它在 Mod 开发中的意义。
若没有特殊说明,本文出现的“属性”一词均指只适用于生物实体的 Attribute 系统。

1.1. 生物限定

如名字所示,只有生物实体(instanceof EntityLivingBase)才有属性。所以就不用指望矿车之类的东西还能有属性了。

1.2. 享元

属性系统中也可以见到享元模式(Flyweight Pattern)的影子:

  • 每一种属性都由 IAttribute 接口描述。
  • 每一种属性的具体实例则由 IAttributeInstance 接口描述。
  • IAttribute 决定了某一属性的上下限和初始值等。
  • 操作属性的值时,我们修改的是 IAttributeInstance

简单来说,IAttribute 之于 IAttributeInstance 就像是 Item 之于 ItemStack

1.3. 注册:不需要注册

IAttribute 无需注册,创建后即可拿去给生物实体用。 原版使用的实现是 RangedAttribute,我们可以直接拿来用。

// 第一个参数指定一个别的 IAttribute 作为它的 parent。一般都是填 null,
// 但如果你的属性真的是从别的属性衍生而来的话,建议加上。原版会通过
// IAttribute.getParent() 把所有 parent 属性一并加入到目标实体上。
// 第二个参数是 id,兼职 translation key。不要和别的属性重名了。
// 第三个参数是该属性的初始值。
// 第四个参数指定下限:该属性最低必须是这个值。
// 第五个参数指定上限:该属性最高只能是这个值。
//
// setShouldWatch 决定了该属性的值的变化是否需要同步至逻辑客户端。
// setShouldWatch(false) 会令逻辑客户端看不到该属性的变化。
// 如果你不希望客户端看到这个值,或者客户端不需要知道这个值,可以用它
// 来稍微节约一点点带宽。
public static final IAttribute MAGIC_POWER = new RangedAttribute(null, "my_mod.attribute.magic", 0.0, 0.0, 20.0).setShouldWatch(false);

在原版的 SharedMonsterAttributes 类下可以找到原版实体使用的各种 IAttribute, 虽然我们也可以直接拿来用,但是在 EntityLivingBase 中这些属性大都已经在构造阶段添加好了,所以我们直接让我们的新生物继承 EntityLivingBase 即可。

1.4. 初始化

上文提到 EntityLivingBase 在构造阶段就添加了若干属性,具体来说是 applyEntityAttributesfunc_110147_ax)方法:

@Override
protected void applyEntityAttributes() {
    super.applyEntityAttributes();
    // 这个 registerAttribute 的含义更像是 addAttribute。
    // 它只是在声明“这个实体拥有这个属性”。
    this.getAttributeMap().registerAttribute(...);
}

如果你想为你的生物添加全新的自定义属性,在这里添加就没错了。

如果你是想给原版实体添加新属性的话…… 可以考虑使用 EntityEvent.EntityConstructing

@SubscribeEvent
public static void entityInit(EntityEvent.EntityConstructing event) {
    final Entity e = event.getEntity();
    if (e instanceof EntityLivingBase) {
        final EntityLivingBase living = (EntityLivingBase) e;
        living.getAttributeMap().registerAttribute(...);
    }
}

1.5. 获取当前属性数值

EntityLivingBase something = ...;
double attack = something.getAttributeMap().getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue();