跳转至

实体属性

在以往的实体开发中,如果需要为实体存储一个状态量,我们往往会借助所谓的"哑组件"——这些组件在语义上原本代表某种游戏行为,但在自定义实体上却几乎没有实际效果,因此被当作存储变量来使用。例如,minecraft:variantminecraft:mark_variant各能存储一个整数,可以用query.variantquery.mark_variantMolang查询读取;minecraft:is_babyminecraft:is_sheared等可以充当布尔量,分别通过query.is_babyquery.is_sheared读取。

然而哑组件存在两个明显缺陷:其一,它们在语义上与用途不符,时间一长很难回忆起"这个minecraft:is_baby到底在我的实体里控制什么功能";其二,可供借用的哑组件数目有限,当需要存储更多状态时便会捉襟见肘。实体属性Entity Property)正是为了解决这两个问题而出现的。

版本要求

实体属性需要将实体服务端定义文件的format_version设为1.16.0或更高版本,且需开启实验性玩法中的Beta API(在部分版本中可能标注为假日创作者功能)。

定义属性

在实体服务端定义文件(行为包entities/目录下的JSON文件)的minecraft:entity.description对象中,添加properties字段来声明该实体拥有的属性:

BP/entities/bee.json(节选)
{
  "format_version": "1.18.20",
  "minecraft:entity": {
    "description": {
      "identifier": "minecraft:bee",
      "is_spawnable": true,
      "is_summonable": true,
      "is_experimental": false,
      "properties": {
        "minecraft:has_nectar": {
          "type": "bool",
          "client_sync": true,
          "default": "query.had_component_group('has_nectar')"
        }
      }
    }
  }
}

properties是一个对象,其中每个键值对定义一个属性,键名即属性名。属性名建议带命名空间(如minecraft:has_nectar),以避免与其他属性冲突。属性对象支持以下字段:

type
属性的类型。支持"bool"(布尔值)、"int"(整数)、"float"(浮点数)、"enum"(字符串枚举)四种。
range
属性的取值范围。intfloat类型填写[最小值, 最大值]的两元素数组;enum类型填写所有合法枚举字符串的数组,最多16个元素。bool类型不需要此字段。
client_sync
是否将该属性同步到客户端,默认为false。开启后,客户端的动画、动画控制器、渲染控制器和粒子均可读取该属性值。
default
实体首次初始化时该属性的默认值。之后从存档加载时,读取上次保存时的实际值,不再使用默认值。可以直接填写与type对应的字面量值,也可以填写一个Molang表达式字符串;表达式仅支持query.had_component_group查询,且无法访问实体自定义变量。

实体首次生成时,引擎按default值初始化各属性,并将它们存入内存和NBT存档,供后续读写。

读取属性

通过Molang读取

游戏提供了两个专用查询函数用于读取实体属性:

  • query.has_property('<属性名>') — 检查该属性是否存在,返回1.0(存在)或0.0(不存在)。
  • query.property('<属性名>') — 返回该属性的当前值。数字类型返回浮点数,枚举类型返回其字符串的哈希值,布尔类型返回1.00.0

这两个函数可以在动画、动画控制器、渲染控制器、粒子等支持Molang的所有地方使用(前提是该属性开启了client_sync,或者在服务端上下文中)。

通过过滤器读取

在实体的组件组条件、AI意向过滤器等需要过滤器的位置,可以使用以下过滤器按类型读取属性:

{
  "test": "int_property",
  "subject": "self",
  "operator": "==",
  "domain": "demo:my_int_prop",
  "value": 3
}

domain字段填写要查询的属性名,value填写要比较的值:

int_property
适用于int类型属性,按整数比较。
float_property
适用于float类型属性,按浮点数比较。
bool_property
适用于bool类型属性,按布尔值比较。
enum_property
适用于enum类型属性,按枚举字符串比较。
has_property
检查属性是否存在,属性名填入value字段而非domain字段。

修改属性

目前可以通过实体事件响应set_property修改属性值:

BP/entities/bee.json(节选)
{
  "events": {
    "collected_nectar": {
      "set_property": {
        "minecraft:has_nectar": true
      }
    },
    "minecraft:exited_hive": {
      "set_property": {
        "minecraft:has_nectar": false
      }
    }
  }
}

set_property是一个对象,每个键值对表示一次赋值,键为属性名,值为要赋予的目标值或一个Molang表达式字符串。

赋值是异步的

set_property中的Molang表达式计算是同步的,但赋值到属性是异步的——真正的更新要等到下一刻才会反映。因此,在同一个事件中如果同时对同一属性的两条set_property表达式调用了query.property,它们看到的仍然是旧值,而不是上一条赋值的结果。

此外,set_property的Molang表达式内只能使用query.propertyquery.has_property,无法访问实体自定义变量。

完整示例

以下是一个简化的蜜蜂实体,演示了实体属性的完整定义-读取-修改流程:

BP/entities/bee.json
{
  "format_version": "1.18.20",
  "minecraft:entity": {
    "description": {
      "identifier": "minecraft:bee",
      "is_spawnable": true,
      "is_summonable": true,
      "is_experimental": false,
      "properties": {
        "minecraft:has_nectar": {
          "type": "bool",
          "client_sync": true,
          "default": "query.had_component_group('has_nectar')"
        }
      }
    },
    "component_groups": {
      "has_nectar": {
        "minecraft:grows_crop": {
          "charges": 10,
          "chance": 0.03
        }
      }
    },
    "components": {},
    "events": {
      "minecraft:exited_hive": {
        "set_property": {
          "minecraft:has_nectar": false
        }
      },
      "collected_nectar": {
        "set_property": {
          "minecraft:has_nectar": true
        }
      },
      "find_hive_timeout": {
        "sequence": [
          {
            "filters": {
              "test": "bool_property",
              "operator": "!=",
              "domain": "minecraft:has_nectar"
            },
            "remove": {
              "component_groups": ["find_hive"]
            },
            "add": {
              "component_groups": ["look_for_food"]
            }
          }
        ]
      }
    }
  }
}

这个示例中,minecraft:has_nectar属性: - 在description.properties中被定义为布尔类型,并通过query.had_component_group从旧组件组状态迁移初始值; - 在events.collected_nectar中通过set_property设为true; - 在events.find_hive_timeout中通过bool_property过滤器读取,用于条件分支。

从哑组件迁移

如果你的旧实体用minecraft:is_baby之类的哑组件存储状态,迁移到实体属性的思路是:

  1. description.properties中新增对应的属性,类型根据原哑组件对应。
  2. query.property替换原有的query.is_baby等Molang查询。
  3. 在事件中用set_property替换原有的add/remove组件组操作。
  4. 如果需要用过滤器判断,改用对应类型的属性过滤器。

迁移时可以利用default字段的query.had_component_group在不破坏存档兼容性的前提下完成初始值对齐,如同上方蜜蜂示例所示。