给角色装骨骼,不该需要写脚本
设计札记
一条腿,不过是三根骨骼加一个目标点。说清这件事,用不着五个图层、四段表达式。
一条腿,不该占五个图层
用 Duik (外部网站,在新标签页打开) 绑过角色的人,都见过图层面板渐渐和画没了关系的样子。开始时只有十几张画;等绑定能用了,每条腿、每只手都多出了自己的骨骼、控制器和 Pin,每一层各挂一段表达式,真正要动的那几张画被挤到了最底下。
Duik 是开源的,做得也很出色。可它恰恰说明了 After Effects 缺什么:AE 里没有骨骼,脚本只能拿现成的东西拼一副出来——用图层充当骨骼,用表达式让它们一起动。
我们的看法是,骨架根本就不该是拼出来的。它应该本身就是一种图层,骨骼是图层里的数据,由编辑器像计算变换那样直接求解。Keyframe 就是这样绑定角色的,下面这只狐狸就是这么绑的。往下读之前,先拖拖看。
拖尾巴或爪子:每一处都是一个 IK 目标,带着一串骨骼弯过去。也可以单独拖某根骨骼,或者点一下画面,再用方向键移动 IK 目标。
这只狐狸身上一段表达式也没有。腿和尾巴是同一个骨架图层里的几条链,身体和尾巴是蒙皮到这些骨骼、刷过权重的网格;网页画它,用的就是制作它的那个工程里的网格、权重和骨骼位置。
在 After Effects 里,角色是怎么绑起来的?
拿 Duik Ángela 绑好的一条后腿来看。每根骨骼都是一个普通的形状层,腿的画用父子关系挂在骨骼上跟着走。爪子那里有一个控制器图层。IK,也就是让膝盖弯下去、好让爪子够到控制器的那步计算,由骨骼上的表达式来做。
父子关系只能让画整块地动,腿要在膝盖处弯,就得用上人偶工具 (外部网站,在新标签页打开)。人偶的控制点再经过更多图层、更多表达式跟着骨骼走。人偶工具没有逐顶点的权重:腿弯得软还是硬,只能靠控制点放几个、放在哪儿来调。
这样算下来,一条腿是五个图层、四段表达式,每一段每帧都要算一遍。四条腿再加一条尾巴的角色,就是这个数的好几倍。
After Effects
- 控制器 · 爪子ƒ IK
- 骨骼 · 小腿ƒ 表达式
- 骨骼 · 大腿ƒ 表达式
- Pin · 膝盖ƒ 表达式
- 画面 · 后腿人偶 · 控制点
为什么最后都成了表达式?
并不是做绑定的人偏爱表达式。AE 的核心是闭源的,对外只开了三个口子:效果插件只能画自己那一层的像素;脚本(Duik 也是脚本)只在运行那一刻改动工程,跑完就不在了;只有表达式 (外部网站,在新标签页打开)每帧都运行,能读一个属性去决定另一个属性。
所以,骨骼要逐帧跟着控制器走、父子关系又描述不了这层关系时,表达式就是剩下的唯一一扇门。这扇门的两个特点,决定了从这里做出来的每一套绑定是什么样子。
其一,一段表达式只产出一个值,就是它所在属性的值。一条腿的 IK 本该是对整条链的一次计算,却得分别写到每根骨骼的旋转上,同一个解要算好几遍。
其二,表达式不记事。每一帧都从零算起,看不到自己上一帧算出了什么。做个抖动、连个属性都没问题,一遇到带惯性的运动就麻烦了。下面是大家常写来让尾巴、耳朵产生拖尾的那种弹簧:
// 表达式留不住上一帧的结果,只能从头再算
const stiffness = 0.2, damping = 0.8;
let x = thisComp.layer("爪子").transform.position.valueAtTime(0)[0];
let v = 0;
for (let f = 1; f <= timeToFrames(time); f++) {
const goal = thisComp.layer("爪子").transform.position.valueAtTime(framesToTime(f))[0];
v = (v + (goal - x) * stiffness) * damping;
x += v;
}
[x, value[1]];看那个循环:到第 10 帧,弹簧要步进 10 次;到第 300 帧,要步进 300 次,而且每一步都要去问另一个图层它那一刻在哪。一个 30 fps、10 秒长的镜头从头播到尾,为了 300 帧画面,循环体要跑大约 45,000 次。时间线越往后拖,每一帧就越慢。
这段表达式本身没有写错。在一个帧与帧之间没有记忆的系统里,依赖自身历史的运动,只能这样写才对。
用文本拼出来的绑定,要付出什么?
首先是慢,这一点 Duik 自己说得很坦白:它的性能说明 (外部网站,在新标签页打开)提醒,IK 和跟随这类表达式很复杂,会明显拖慢预览。每多一根骨骼,每帧就多一段表达式要算。
第二个代价不那么显眼。骨骼之间的关系分散写在一段段文本里,AE 本身并不知道是哪五个图层组成了一条腿。不知道这一点,它就给不了任何骨骼层面的功能,比如给骨骼换父级而姿势不跳,或者把一段动作搬到另一个角色身上。这些事要有人帮忙,又只能找脚本。
最新的读者也面临同样的问题。AE 的 AI 助手 (外部网站,在新标签页打开)能读写表达式,这确实有用,可它读到的仍是一段段各自独立的表达式,而不是一副能按结构去理解的骨架。
这些都不是谁的疏忽,而是 AE 很早以前那个选择的结果:带关键帧的图层就是全部模型,表达式就是扩展它的办法。绑定是在这个模型上长出来的,模型允许它走多远,它就走了多远。
骨架成了一种图层,会有什么不同?
Keyframe
- 骨架一个图层
- 大腿
- 小腿IK
- 爪子
- 画面 · 后腿网格 → 骨架,带权重
在 Keyframe 里,骨架是一种图层类型,和图片、文字、形状并列。骨骼是这一个图层里的数据,角色需要几条链就有几条,每条从自己的根骨骼开始。腿、手臂和尾巴可以共用一副骨架,又各动各的。
正向和反向运动学(FK 与 IK)由编辑器自己的代码求解,一次解出整条链。可以一根根地转骨骼,也可以给骨骼加上 IK、拖它的目标点,上面那串骨骼就会弯过去够它。保持角度能让爪子在腿移动时保持水平。没有哪根骨骼上挂着表达式。
部件跟随骨架有两种方式:把图层挂到某根骨骼上,整块跟着走;或者把部件的网格蒙皮到骨骼上,顺滑地弯。画骨骼、换父级、绑定的手法,参照的是 Spine、Moho 这类专做骨骼动画的软件,而不是在合成软件里临时凑出来的办法。

- 骨骼工具:从关节到关节画骨骼。
- 选中的骨骼,小腿,已开 IK。
- 骨骼选项:IK 与保持角度。
- IK 目标:拖动它,腿就跟着弯。
为什么用权重,而不用控制点?
挂在骨骼上的画,会在关节处像纸偶一样折过去。要弯,就得有网格。Keyframe 根据画面自身的透明度生成网格,在图层放置之前先让它的画面变形,这和 AE 里人偶工具起作用的环节相同。
区别在于怎样告诉网格该怎么动。网格蒙皮到它要跟随的骨骼上,这些骨骼可以来自骨架里不同的链;每根骨骼拉动哪一块、拉得多重,用权重画笔刷出来。尾巴能整条顺着弯,而不是在三个关节处折;肩膀可以从脖子借一点、从前腿借一点。骨骼够不到的地方用自由控制柄补上,3D 图层也能带网格,在自身平面内弯曲。
控制点说不出“这根骨骼占六成,那根占四成”。所以 Keyframe 用网格,不设人偶工具。

- 这个网格蒙皮到的骨架。
- 一条链的根骨骼。
- 一条链上的骨骼;勾选带动这个部件的那几根。
- 不带动这个部件的骨骼。
弹簧放到哪儿去了?
表达式没了,拖尾并没有跟着消失,而是成了一种行为。次级运动加在一条骨骼链上,把每根骨骼当作挂在父骨骼上的阻尼弹簧。不管带动它的是关键帧、IK,还是整个骨架图层在甩,这条链都会落在后面、冲过头、再慢慢停稳。你调的是频率、阻尼和强度,不用写循环。
表达式缺的记忆,它有。弹簧从图层入点处静止开始,每帧按固定步数积分;每一帧的状态都会留下,下一帧接着往下算,不必把整个镜头重放一遍。因为步数固定,结果只取决于工程和帧号,跟你先看了哪几帧无关,预览和导出拿到的是同一条尾巴。
编辑器认得骨骼之后,还能做什么?
- 你画骨骼
- 你加 IK
- 你网格蒙皮刷权重
- 编辑器次级运动弹簧是一种行为
- 编辑器存成预设套到另一个角色
骨架一旦成了编辑器读得懂的结构,骨骼上的操作就成了普通命令。给骨骼换父级时,编辑器会改写它的基础值和全部关键帧,画面上的姿势纹丝不动;把一根骨骼拆出来另起一条链,也是这样。
动作也能带走。把一段骨骼动画存成预设,套到另一个角色上:编辑器按链的结构去对应骨骼,两副骨架对不上的地方,它会来问你,而不是去猜。
Agent (在新标签页打开) 面对的也是同一个结构。搭骨架、蒙皮、刷权重、加 IK 都是编辑器里注册过的操作,Agent 用的和你是同一套;它做出来的是一副你能打开、能调整的骨架,而不是一堆生成出来的文本。每一步怎么做,见使用文档的骨骼绑定 (在新标签页打开)。
这套做法还解决不了什么?
弹簧的记忆,只在没有别的东西参与计算姿势时才保得住。只要合成里还有表达式,或者有次级运动以外的行为,编辑器就没法确定留下的状态是否还有效,只能每帧从入点重新积分。这和上面那段表达式的代价一样,合成一长,预览就会慢下来。
重定向没有听上去那么宽。预设只带走骨骼的旋转和根骨骼的位置,骨骼缩放和其他骨骼的位移都留在原处。几根源骨骼对应到同一根目标骨骼时,会逐帧采样成线性关键帧,原来的缓动就没了。
一个网格只能蒙皮到一副骨架,因为两副骨架之间没有可以共同绑定的静止姿势。身体、尾巴和要跟着一起弯的道具,应当作为不同的链放进同一副骨架。习惯把绑定拆成一个个独立部件的人,得换个思路。
习惯了人偶控制点的人,还要学一样新东西。刷权重比放控制点精确,但头一回做,也更费工夫。
绑定是结构,不是文本
表达式是好工具,Keyframe 也保留了表达式 (在新标签页打开),用在它擅长的事上:把一个属性连到另一个属性。骨架是另一类东西。它有静止姿势、有链、有权重,最好就以结构的形式存着:编辑器才能求解它,在你修改时保住它,把它搬到另一个角色身上,再交给下一个接手工程的人或 Agent。
开头你拖过的那只狐狸里,一行脚本也没有。它从来就用不着。