属性
属性是生物实体的特殊字段,决定最大生命值、速度、护甲等基础特征。属性都以双精度浮点数保存,并自动同步。原版提供多种默认属性,也可以添加自己的属性。
受历史实现影响,并非所有属性都适用于所有实体。例如,恶魂忽略飞行速度属性;跳跃强度原先只影响马,不影响玩家。
内置属性
Minecraft
以下属性位于 minecraft 命名空间,代码中的对应值定义在 Attributes 类中。
| 名称 | 代码常量 | 范围 | 默认值 | 用途 |
|---|---|---|---|---|
armor |
ARMOR |
[0,30] |
0 | 实体护甲值。1 点对应快捷栏上方的半个护甲图标。 |
armor_toughness |
ARMOR_TOUGHNESS |
[0,20] |
0 | 实体护甲韧性。详见 Minecraft Wiki 的护甲韧性。 |
attack_damage |
ATTACK_DAMAGE |
[0,2048] |
2 | 实体不持武器等物品时的基础攻击伤害。 |
attack_knockback |
ATTACK_KNOCKBACK |
[0,5] |
0 | 实体造成的额外击退。击退还具有不由该属性表示的基础强度。 |
attack_speed |
ATTACK_SPEED |
[0,1024] |
4 | 实体攻击冷却。原文称数值越高冷却越长,并称设为 0 可重新启用 1.9 之前的战斗方式。 |
block_break_speed |
BLOCK_BREAK_SPEED |
[0,1024] |
1 | 挖掘方块速度的乘法修正,详见挖掘速度。 |
block_interaction_range |
BLOCK_INTERACTION_RANGE |
[0,64] |
4.5 | 实体与方块交互的距离,单位为方块。 |
burning_time |
BURNING_TIME |
[0,1024] |
1 | 实体被点燃后燃烧持续时间的倍率。 |
camera_distance |
CAMERA_DISTANCE |
[0,32] |
4 | 第三人称相机与实体的距离,也包括旁观或骑乘其他实体的情况。 |
explosion_knockback_resistance |
EXPLOSION_KNOCKBACK_RESISTANCE |
[0,1] |
0 | 实体对爆炸击退的抗性。0 表示无抗性,0.5 表示抵抗一半,1 表示完全抵抗。 |
entity_interaction_range |
ENTITY_INTERACTION_RANGE |
[0,64] |
3 | 实体与其他实体交互的距离,单位为方块。 |
fall_damage_multiplier |
FALL_DAMAGE_MULTIPLIER |
[0,100] |
1 | 实体所受摔落伤害的倍率。 |
flying_speed |
FLYING_SPEED |
[0,1024] |
0.4 | 飞行速度倍率。并非所有飞行实体都使用,例如恶魂会忽略它。 |
follow_range |
FOLLOW_RANGE |
[0,2048] |
32 | 实体锁定或跟随玩家的距离,单位为方块。 |
gravity |
GRAVITY |
[1,1] |
0.08 | 实体受到的重力,单位为方块/游戏刻²。 |
jump_strength |
JUMP_STRENGTH |
[0,32] |
0.42 | 实体跳跃强度,数值越大跳得越高。 |
knockback_resistance |
KNOCKBACK_RESISTANCE |
[0,1] |
0 | 击退抗性。0 表示无抗性,0.5 表示抵抗一半,1 表示完全抵抗。 |
luck |
LUCK |
[-1024,1024] |
0 | 实体幸运值,用于抽取战利品表时增加抽取次数,或以其他方式改变产出物品的品质。 |
max_absorption |
MAX_ABSORPTION |
[0,2048] |
0 | 最大伤害吸收值,即黄色生命值。1 点为半颗心。 |
max_health |
MAX_HEALTH |
[1,1024] |
20 | 实体最大生命值。1 点为半颗心。 |
mining_efficiency |
MINING_EFFICIENCY |
[0,1024] |
0 | 仅在使用正确工具时生效的挖掘速度加法修正,详见挖掘速度。 |
movement_efficiency |
MOVEMENT_EFFICIENCY |
[0,1] |
0 | 在灵魂沙等会减速的方块上行走时,对移动速度应用的线性插值加成。 |
movement_speed |
MOVEMENT_SPEED |
[0,1024] |
0.7 | 实体移动速度,数值越大越快。 |
oxygen_bonus |
OXYGEN_BONUS |
[0,1024] |
0 | 氧气加成,数值越高,开始溺水前可等待的时间越长。 |
safe_fall_distance |
SAFE_FALL_DISTANCE |
[-1024,1024] |
3 | 不会造成摔落伤害的安全下落距离。 |
scale |
SCALE |
[0.0625,16] |
1 | 实体渲染缩放比例。 |
sneaking_speed |
SNEAKING_SPEED |
[0,1] |
0.3 | 潜行时应用的移动速度倍率。 |
spawn_reinforcements |
SPAWN_REINFORCEMENTS_CHANCE |
[0,1] |
0 | 僵尸生成增援僵尸的概率。只与困难难度有关,普通及以下难度不会生成增援。 |
step_height |
STEP_HEIGHT |
[0,10] |
0.6 | 实体自动跨越的高度,单位为方块。设为 1 时,玩家能像走上台阶一样走上一格高的边缘。 |
submerged_mining_speed |
SUBMERGED_MINING_SPEED |
[0,20] |
0.2 | 仅在水下生效的挖掘速度乘法修正,详见挖掘速度。 |
sweeping_damage_ratio |
SWEEPING_DAMAGE_RATIO |
[0,1] |
0 | 横扫伤害相对于主要攻击伤害的比例。0 为无伤害,0.5 为一半,1 为全部。 |
tempt_range |
TEMPT_RANGE |
[0,2048] |
10 | 实体可被物品吸引的范围,主要用于牛、猪等被动动物。 |
water_movement_efficiency |
WATER_MOVEMENT_EFFICIENCY |
[0,1] |
0 | 实体在水下时应用的移动速度倍率。 |
waypoint_transmit_range |
WAYPOINT_TRANSMIT_RANGE |
[0,60000000] |
0 | 实体可以向路径点追踪器发送自身位置的范围。 |
waypoint_receive_range |
WAYPOINT_RECEIVE_RANGE |
[0,60000000] |
0 | 实体可以接收其他位置发送者的范围。 |
警告 Mojang 为一些属性设置的上限较为随意,护甲上限 30 就是明显例子。NeoForge 不修改这些上限,但有 Mod 可以调整。
NeoForge
以下属性位于 neoforge 命名空间,代码中的对应值定义在 NeoForgeMod 类中。
| 名称 | 代码常量 | 范围 | 默认值 | 用途 |
|---|---|---|---|---|
creative_flight |
CREATIVE_FLIGHT |
[0,1] |
0 | 决定实体是否启用创造模式飞行:大于 0 为启用,小于等于 0 为禁用。 |
nametag_distance |
NAMETAG_DISTANCE |
[0,32] |
32 | 实体名称标签的可见距离,单位为方块。 |
swim_speed |
SWIM_SPEED |
[0,1024] |
1 | 实体在水下的移动速度倍率,独立于 minecraft:water_movement_efficiency 应用。 |
默认属性
创建 LivingEntity 时,必须注册一组默认属性。实体生成时会应用它们。默认属性通过 EntityAttributeCreationEvent 注册:
@SubscribeEvent // on the mod event bus
public static void createDefaultAttributes(EntityAttributeCreationEvent event) {
event.put(
// Your entity type.
MY_ENTITY.get(),
// An AttributeSupplier. This is typically created by calling LivingEntity#createLivingAttributes,
// setting your values on it, and calling #build. You can also create the AttributeSupplier from scratch
// if you want, see the source of LivingEntity#createLivingAttributes for an example.
LivingEntity.createLivingAttributes()
// Add an attribute with its default value.
.add(Attributes.MAX_HEALTH)
// Add an attribute with a non-default value.
.add(Attributes.MAX_HEALTH, 50)
// Build the AttributeSupplier.
.build()
);
}
建议 某些类提供了专用的
LivingEntity#createLivingAttributes版本。例如,Monster可改用Monster#createMonsterAttributes。
有时需要向现有实体的 AttributeSupplier 添加属性,例如创建自定义属性时。可以通过 EntityAttributeModificationEvent 完成:
@SubscribeEvent // on the mod event bus
public static void modifyDefaultAttributes(EntityAttributeModificationEvent event) {
event.add(
// The EntityType to add the attribute for.
EntityType.VILLAGER,
// The Holder<Attribute> to add to the EntityType. Can also be a custom attribute.
Attributes.ARMOR,
// The attribute value to add.
// Can be omitted, if so, the attribute's default value will be used instead.
10.0
);
// We can also check if a given EntityType already has a given attribute.
// In this example, if villagers don't have the armor attribute already, we add it.
if (!event.has(EntityType.VILLAGER, Attributes.ARMOR)) {
event.add(...);
}
}
注意,与某些其他注册表不同,自定义属性不会阻止原版客户端连接 NeoForge 服务端。原版客户端连接时,只会接收 minecraft 命名空间中的属性。
查询属性
实体将属性值保存在 AttributeMap 中,基本相当于 Map<Attribute, AttributeInstance>。属性实例与属性的关系,类似物品堆与物品:属性是注册的单例,属性实例则绑定到某个具体实体。
调用 LivingEntity#getAttributes 可以获取实体的属性映射,然后按以下方式查询:
// Get the attribute map.
AttributeMap attributes = livingEntity.getAttributes();
// Get an attribute instance. This may be null if the entity does not have the attribute.
AttributeInstance instance = attributes.getInstance(Attributes.ARMOR);
// Get the value for an attribute. Will fallback to the default for the entity if needed.
double value = attributes.getValue(Attributes.ARMOR);
// Of course, we can also check if an attribute is present to begin with.
if (attributes.hasAttribute(Attributes.ARMOR)) { ... }
// Alternatively, LivingEntity also offers shortcuts:
AttributeInstance instance = livingEntity.getAttribute(Attributes.ARMOR);
double value = livingEntity.getAttributeValue(Attributes.ARMOR);
说明 操作属性时,几乎总是使用
Holder<Attribute>而非Attribute。因此,后文自定义属性的示例也会明确保存Holder<Attribute>。
属性修饰符
与查询相比,修改属性值要复杂一些,主要因为同一个属性可能同时受到多种变化影响。
例如,玩家基础攻击伤害为 1,手持钻石剑增加 6 点,总计为 7。然后喝下力量药水,增加一个伤害倍率;再装备某种饰品,又增加一个倍率。
为避免计算错误,并清楚表达各项修改,Minecraft 引入属性修饰符系统。每个属性都有一个基础值,通常来自前述默认属性。可以添加任意数量的属性修饰符,之后分别移除,而无需自行管理各项运算的正确应用顺序。
首先创建属性修饰符:
// The name of the modifier. This is later used to query the modifier from the attribute map
// and as such must be (semantically) unique.
Identifier id = Identifier.fromNamespaceAndPath("yourmodid", "my_modifier");
// The modifier itself.
AttributeModifier modifier = new AttributeModifier(
// The name we defined earlier.
id,
// The amount by which we modify the attribute value.
2.0,
// The operation used to apply the modifier. Possible values are:
// - AttributeModifier.Operation.ADD_VALUE: Adds the value to the total attribute value.
// - AttributeModifier.Operation.ADD_MULTIPLIED_BASE: Multiplies the value with the attribute base value
// and adds it to the total attribute value.
// - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL: Multiplies the value with the total attribute value,
// i.e. the attribute base value with all previous modifications already performed,
// and adds it to the total attribute value.
AttributeModifier.Operation.ADD_VALUE
);
应用时有两种选择:临时修饰符或永久修饰符。永久修饰符会保存到磁盘,临时修饰符不会。永久修饰符适合永久属性加成,例如护甲或生命值技能;临时修饰符主要用于装备、状态效果以及依赖玩家当前状态的其他修正。
AttributeMap attributes = livingEntity.getAttributes();
// Add a transient modifier. If a modifier with the same id is already present, this will throw an exception.
attributes.getInstance(Attributes.ARMOR).addTransientModifier(modifier);
// Add a transient modifier. If a modifier with the same id is already present, it is removed first.
attributes.getInstance(Attributes.ARMOR).addOrUpdateTransientModifier(modifier);
// Add a permanent modifier. If a modifier with the same id is already present, this will throw an exception.
attributes.getInstance(Attributes.ARMOR).addPermanentModifier(modifier);
// Add a permanent modifier. If a modifier with the same id is already present, it is removed first.
attributes.getInstance(Attributes.ARMOR).addOrReplacePermanentModifier(modifier);
也可以移除这些修饰符:
// Remove by modifier object.
attributes.getInstance(Attributes.ARMOR).removeModifier(modifier);
// Remove by modifier id.
attributes.getInstance(Attributes.ARMOR).removeModifier(id);
// Remove all modifiers for an attribute.
attributes.getInstance(Attributes.ARMOR).removeModifiers();
最后,可以查询属性映射是否含有指定 ID 的修饰符,也可以分别查询基础值和修饰符值:
// Check for the modifier being present.
if (attributes.getInstance(Attributes.ARMOR).hasModifier(id)) { ... }
// Get the base armor attribute value.
double baseValue = attributes.getBaseValue(Attributes.ARMOR);
// Get the value of a certain modifier.
double modifierValue = attributes.getModifierValue(Attributes.ARMOR, id);
自定义属性
需要时也可以添加自己的属性。属性和许多系统一样采用注册表,可以注册自己的对象。首先创建 DeferredRegister<Attribute>:
public static final DeferredRegister<Attribute> ATTRIBUTES = DeferredRegister.create(
BuiltInRegistries.ATTRIBUTE, "yourmodid");
属性本身可以选择以下三个类:
RangedAttribute:大多数属性使用它,定义下限、上限和默认值。PercentageAttribute:类似RangedAttribute,但显示为百分比而非浮点数,由 NeoForge 添加。BooleanAttribute:语义上只有真(大于 0)和假(小于等于 0),内部仍使用双精度浮点数,由 NeoForge 添加。
以 RangedAttribute 为例,另外两种用法相似,注册方式如下:
public static final Holder<Attribute> MY_ATTRIBUTE = ATTRIBUTES.register("my_attribute", () -> new RangedAttribute(
// The translation key to use.
"attributes.yourmodid.my_attribute",
// The default value.
0,
// Min and max values.
-10000,
10000
));
这样就完成了。记得将 DeferredRegister 注册到 Mod 总线。
说明 这里使用
Holder<Attribute>,而不是其他注册对象常用的Supplier<RangedAttribute>,因为实体的大多数方法接受Holder<Attribute>,这样更方便。如果确实需要
Supplier<RangedAttribute>或其他Attribute子类的 supplier,应将类型声明为DeferredHolder<Attribute, RangedAttribute>。相同规则适用于其他
Attribute子类,即通常使用Holder<Attribute>,而非Supplier<PercentageAttribute>或Supplier<BooleanAttribute>。
来源: NeoForged/Documentation(26.1)。中文翻译依据提交
4a680cdd4d15。