事件是生物发生变化的方式。幼崽长大、驯服的狼坐下、苦力怕开始引爆:每一种都是一个事件在开启或关闭组件组。本页会用实体文件中的红宝石傀儡来展示事件能做的所有事情。
文件结构
事件与生物的其他行为写在同一个文件里,位于 events 块中。
BP/
entities/
ruby_golem.json <- "events" is inside minecraft:entity事件长什么样
一个事件由一个名字和一个包含动作的对象组成。事件触发时,游戏就会执行这些动作。
"events": {
"wiki:become_angry": {
"add": { "component_groups": ["wiki:angry"] },
"remove": { "component_groups": ["wiki:calm"] }
}
}给你的事件加上自己的命名空间,比如 wiki:become_angry。minecraft: 开头的名字留给游戏自己触发的事件。
所有动作一览
| 动作 | 作用 |
|---|---|
add | 开启一个或多个组件组 |
remove | 关闭一个或多个组件组 |
set_property | 修改在 description.properties 中声明的属性 |
trigger | 触发另一个事件 |
randomize | 按权重从列表中挑选一项 |
sequence | 从上到下执行列表中的每一项 |
first_valid | 执行第一个过滤器通过的项,跳过其余项 |
filters | 只有过滤器通过时才执行动作 |
queue_command | 在下一刻以生物的身份执行一条命令 |
play_sound | 播放生物的某个声音,比如骆驼的 "saddle" |
add 和 remove
大部分工作都由这两个完成。它们都接受一个列表,所以你可以一次切换多个组。
"add": { "component_groups": ["wiki:angry", "wiki:glowing_eyes"] },
"remove": { "component_groups": ["wiki:calm"] }- 这些组必须存在于同一文件的
component_groups中。如果名字拼错了,那个组就不会被添加,所以要仔细检查名字。 - 添加一个已经开启的组会重新启动它。组里的计时器会从零开始重新计数。
- 移除一个没有开启的组不会有任何效果,所以“以防万一”地移除也很安全。
set_property
修改生物身上的一个属性。该属性必须在 description 中声明。
"set_property": {
"wiki:mood": "angry",
"wiki:rubies_eaten": "math.min(q.property('wiki:rubies_eaten') + 1, 5)"
}值可以是普通值,也可以是写在字符串里的 Molang 表达式。上面第二行每次加 1,但永远不会超过 5。可以读取哪些内容请看 Molang 查询。
trigger:串联事件
trigger 会触发另一个事件。这样一段逻辑只需写一次就能反复使用。
"trigger": "wiki:calm_down"你还可以指定谁来接收这个事件:
"trigger": {
"event": "wiki:calm_down",
"target": "self"
}randomize:挑一个
randomize 包含一个条目列表。游戏会从中挑选一个并执行。每个条目可以有一个 weight:权重越大,被选中的次数越多。没有写权重的条目按 1 计算。
"minecraft:entity_spawned": {
"randomize": [
{
"weight": 3,
"trigger": "wiki:spawn_adult"
},
{
"weight": 1,
"trigger": "wiki:spawn_baby"
}
]
}每个条目的概率等于它的权重除以总权重。这里总权重是 4,所以成年个体的概率是 4 分之 3(75%),幼崽是 4 分之 1(25%)。
条目也可以只有 { "weight": 3 } 而没有任何动作。这表示“有 3 份的概率什么都不做”。
sequence:按顺序做几件事
sequence 会从第一项到最后一项执行每一项。当条目带有自己的 filters 时,它就变得非常强大:每一项只有在检查通过时才会执行。
"wiki:fed_ruby": {
"sequence": [
{
"set_property": {
"wiki:rubies_eaten": "q.property('wiki:rubies_eaten') + 1"
}
},
{
"filters": { "test": "is_baby", "value": true },
"trigger": "wiki:grow_up"
},
{
"filters": { "test": "int_property", "domain": "wiki:rubies_eaten", "operator": ">=", "value": 4 },
"add": { "component_groups": ["wiki:shiny"] }
}
]
}改动在最后才生效
事件执行时,游戏会先算出所有动作,等到生物下一次更新时才应用它们。所以 sequence 中靠后条目的过滤器看到的仍然是旧值。在上面的例子中,第三项检查的是第一项加 1 之前的数量。记住这一点,或者把逻辑拆成两个事件。
first_valid:第一个匹配的胜出
first_valid 看起来和 sequence 很像,但它在第一个过滤器通过的条目之后就会停下。它非常适合“如果这样,否则如果那样,否则……”的逻辑。原版的牛就是用它根据生成时所在的生物群系来选择温暖或寒冷外观的。
"wiki:pick_color": {
"first_valid": [
{
"filters": { "test": "has_biome_tag", "value": "desert" },
"add": { "component_groups": ["wiki:sandy"] }
},
{
"filters": { "test": "has_biome_tag", "value": "frozen" },
"add": { "component_groups": ["wiki:icy"] }
},
{
"add": { "component_groups": ["wiki:plain"] }
}
]
}最后一项没有过滤器,所以它总是会通过。它充当“否则”的情况。
作用于整个事件的过滤器
你可以把 filters 直接写在事件上。如果过滤器不通过,事件里的任何内容都不会执行。
"wiki:become_angry": {
"filters": { "test": "is_baby", "value": false },
"add": { "component_groups": ["wiki:angry"] }
}现在幼年傀儡永远不会生气了。这需要 1.19.20 或更新的 format_version。如果版本更旧,游戏会跳过这个过滤器,总是执行事件。
queue_command
像生物自己输入一样执行一条命令。它会在下一刻内执行,除非那时生物已经不在了。
"queue_command": {
"command": "say I am made of rubies!"
}用来做粒子、声音或标签("tag @s add wiki:tamed")这类小事很方便。更复杂的事情,用函数或脚本更容易管理。
游戏为你触发的事件
有些事件有固定的 minecraft: 名字。如果你的生物有一个使用这些名字的事件,游戏会在合适的时刻触发它。
| 事件 | 何时触发 |
|---|---|
minecraft:entity_spawned | 生物第一次出现时:自然生成、刷怪蛋或 /summon |
minecraft:entity_born | 生物由两个亲代繁殖出生时 |
minecraft:entity_transformed | 另一个生物通过 minecraft:transformation 变成这个生物时 |
大多数生物都在 minecraft:entity_spawned 中设置初始状态,比如决定是成年还是幼年。
触发事件的其他方式
- 组件。 许多组件都可以接收事件,比如
timer、damage_sensor、environment_sensor、interact和ageable。 - 命令。
/event entity @e[type=wiki:ruby_golem] wiki:become_angry会在每一只红宝石傀儡上触发它。 - 召唤。
/summon wiki:ruby_golem ~ ~ ~ 0 0 wiki:spawn_baby会生成傀儡并立即触发一个事件。参见 /summon。 - 生成规则。
herd条件可以对群体中的一部分触发事件。参见生成规则。
出问题时
| 你看到的现象 | 最可能的原因 |
|---|---|
| 什么都没发生 | 触发处的事件名拼写不同,或者根本没有东西触发它 |
| 组没有被添加 | add 中的组名不在 component_groups 里 |
sequence 的过滤器好像不对 | 它看到的是事件开始前的值:见上面的警告 |
| 根过滤器被忽略 | 文件的 format_version 早于 1.19.20 |
randomize 总是选中同一个 | 某个权重比其他的大得多 |