一个生物很少只播放一种动画。我们的红宝石傀儡应该站着时轻轻摇晃,移动时走路,发现敌人时举起拳头。动画控制器负责决定此刻播放其中的哪一个。
这篇指南建立在实体动画的基础上。如果你还没做过动画,请先读那篇。
文件
RP/
animations/
ruby_golem.animation.json
animation_controllers/
ruby_golem.animation_controllers.json
entity/
ruby_golem.entity.json把它想象成一组房间
控制器是一组状态。可以把每个状态想象成一个房间:
- 生物始终只待在一个房间里。
- 每个房间都有一份动画列表,生物在里面时就播放这些动画。
- 每个房间都有门(状态切换)。每扇门上都有一个 Molang 条件。当条件为真时,生物就会穿过这扇门走进另一个房间。
我们的傀儡一开始有两个房间:idle 和 walking。
第 1 步:待机和行走
控制器文件放在资源包的 animation_controllers 文件夹中:
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.ruby_golem.move": {
"initial_state": "idle",
"states": {
"idle": {
"animations": ["idle"],
"transitions": [
{ "walking": "query.modified_move_speed > 0.1" }
],
"blend_transition": 0.2
},
"walking": {
"animations": ["walk"],
"transitions": [
{ "idle": "query.modified_move_speed < 0.05" }
],
"blend_transition": 0.2
}
}
}
}
}可以这样理解:
- 傀儡从
idle开始,播放idle动画。 - 一旦它的速度超过 0.1,就进入
walking,改为播放walk。 - 当速度降到 0.05 以下时,它回到
idle。 - 每次切换都会用 0.2 秒渐变过渡,这样腿就不会突然跳动。
animations 里的名字是客户端实体中的简称,而不是以 animation. 开头的长名字。第 2 步会详细说明。
故意用两个不同的数字
进入的条件(> 0.1)和离开的条件(< 0.05)用的是不同的数字。如果两者相同,一个恰好以这个速度移动的傀儡就可能每一帧都在两个房间之间来回切换,看起来一抖一抖的。
控制器字段
| 字段 | 作用 |
|---|---|
initial_state | 生物的初始状态。不写的话,游戏会寻找名为 default 的状态。 |
states | 所有状态,按名称列出。 |
控制器名称以 controller.animation. 开头。一个好的命名方式是 controller.animation.<生物>.<用途>,比如 controller.animation.ruby_golem.move。
状态内部
| 字段 | 作用 |
|---|---|
animations | 在这个状态中播放的内容。可以是简称,也可以是带混合值的简称。 |
transitions | 离开这个状态的门,按顺序检查。 |
blend_transition | 从这个状态渐变到下一个状态所用的秒数。 |
blend_via_shortest_path | 设为 true 时,渐变会让骨骼沿最短路径旋转。 |
on_entry | 生物进入该状态时运行一次的内容。 |
on_exit | 生物离开该状态时运行一次的内容。 |
variables | 为这个状态计算的数值,见下文。 |
particle_effects | 处于该状态时显示的粒子。 |
sound_effects | 进入该状态时播放的声音。 |
粒子和声音会在动画事件中讲解。
状态切换
transitions 是一个列表。每一项有一个键(要前往的状态)和一个 Molang 条件:
"transitions": [
{ "alert": "query.has_target" },
{ "swimming": "query.is_in_water" },
{ "idle": "query.modified_move_speed < 0.05" }
]- 条件从上到下依次检查。第一个为真的条件胜出。
- 每一帧只会发生一次切换。
- 如果没有一个条件为真,生物就留在原地。
所以要把最重要的门放在最前面。在这里,发现敌人比其他一切都优先。
状态内的混合
一个状态可以同时播放多个动画。每个动画都可以有一个混合值,和 scripts.animate 里的写法一样:
"walking": {
"animations": [
"idle",
{ "walk": "query.modified_move_speed" }
]
}慢速移动的傀儡会在摇晃的基础上带一点行走动作,快速移动的傀儡则会播放完整的行走动画。
第 2 步:连接到生物
控制器和动画一样,列在客户端实体的 animations 映射中。然后在 scripts.animate 中播放它:
"animations": {
"idle": "animation.ruby_golem.idle",
"walk": "animation.ruby_golem.walk",
"fists_up": "animation.ruby_golem.fists_up",
"look_at_target": "animation.common.look_at_target",
"move_controller": "controller.animation.ruby_golem.move"
},
"scripts": {
"animate": [
"look_at_target",
"move_controller"
]
}注意 idle 和 walk 已经不在 animate 里了。现在由控制器来播放它们。如果你把它们留在里面,它们就会播放两次。
你可以同时运行多个控制器,每个控制器都保持自己当前的状态。一种常见的做法是用一个控制器管腿和身体,另一个管手臂。
第 3 步:警戒状态
让傀儡在有目标时举起拳头。添加第三个状态,并从另外两个状态各开一扇通往它的门:
"states": {
"idle": {
"animations": ["idle"],
"transitions": [
{ "alert": "query.has_target" },
{ "walking": "query.modified_move_speed > 0.1" }
],
"blend_transition": 0.2
},
"walking": {
"animations": ["walk"],
"transitions": [
{ "alert": "query.has_target" },
{ "idle": "query.modified_move_speed < 0.05" }
],
"blend_transition": 0.2
},
"alert": {
"animations": [
"fists_up",
{ "walk": "query.modified_move_speed" }
],
"transitions": [
{ "idle": "!query.has_target" }
],
"blend_transition": 0.3
}
}在 alert 状态中,傀儡会一直举着拳头,追你的时候也照样走路。
等待动画播放完毕
有些动画应该完整地播放一次,比如吼叫。有两个查询可以帮上忙:
| 查询 | 何时为真 |
|---|---|
query.all_animations_finished | 状态中的每个动画都已播放到结尾 |
query.any_animation_finished | 至少有一个动画已播放到结尾 |
"roar": {
"animations": ["roar"],
"transitions": [
{ "alert": "query.all_animations_finished" }
]
}这些查询只对不循环的动画有效。循环动画永远不会结束。
on_entry 和 on_exit
on_entry 在生物进入某个状态时运行一次,on_exit 在离开时运行一次。在资源包中它们运行的是 Molang,很适合用来设置变量:
"alert": {
"on_entry": ["variable.times_alerted = (variable.times_alerted ?? 0) + 1;"],
"on_exit": ["variable.calm_since = query.life_time;"],
"animations": ["fists_up"],
"transitions": [{ "idle": "!query.has_target" }]
}在行为包中,同样的列表还可以运行命令和实体事件。参见动画事件。
状态变量
状态可以用 variables 计算自己的数值。其中最好用的是 remap_curve:它能把一个数值范围映射成另一个范围。
"walking": {
"variables": {
"swing": {
"input": "query.modified_move_speed",
"remap_curve": {
"0.0": 0.3,
"1.0": 1.0
}
}
},
"animations": [{ "walk": "variable.swing" }]
}速度为 0 时,variable.swing 是 0.3;速度为 1 时,它是 1.0。介于两者之间时,游戏会按直线插值。所以即使是慢吞吞的傀儡,也会带一点行走动作。
出问题时
| 你看到的现象 | 最可能的原因 |
|---|---|
| 什么都不播放 | 控制器在 animations 里,但不在 scripts.animate 里 |
| 某个动画的幅度变成了两倍 | 它既在 scripts.animate 里,又在某个控制器状态里 |
| 生物卡在某个状态里出不来 | 离开它的切换条件永远不为真。检查它各扇门上的 Molang |
| 只播一次的动画永远不结束 | 它设置了 "loop": true,所以 all_animations_finished 永远不会为真 |
| 生物在两个状态之间闪来闪去 | 进出的条件重叠了。使用两个不同的数字 |
| 内容日志提示缺少某个状态 | 某个切换指向的状态名有拼写错误 |
更多帮助请看故障排除。