问题
数量级的问题:
昨天我朋友问:要做三百多个人名(文本)从宇宙深处飞出来的效果,怎么处理?
对于这样的需求,做 AE 包装的小伙伴们都不陌生,就是 3D文本层摆位置,打个相机穿梭一下。没错,对于几十个图层文本这么操作没什么太大问题,就是繁琐一点,但是量级在 300 以上,恐怕得累疯了。
这里有两个关键难点:
- 三百多个人名要每一个图层都编辑一遍内容,且不说样式可能需要修改,就是切换图层-打字的操作就能要了多半条命;
- 三百多个文本图层,每一层都要调整 Z轴 位置,估计也活不成。
粒子不靠谱:
朋友说,他想到的第一个解决方案是发射粒子。确实,使用粒子可以解决第二个问题,但同时我们必须知道单个图层粒子的最大尺寸是 500x500 像素,一旦粒子飞到近处,它就虚掉了。

除此之外,粒子发射器的设置是非常难调校的,很容易出现名字重复出现或者丢了名字的情况,因为粒子的核心就是随机,所以要让它乖乖听话就不太容易了。
解决方案
我给他的方案还是传统方案,只是上面提到的两个关键难点用表达式去解决了,下面直接全解析一遍,代码我发上来,也会有解释,但是看不懂代码的话就直接忽略,Copy 来用就行了。
一、表达式引擎
我用的所有表达式都是基于 JavaScript 语言,所以必须先在项目设置中设置表达式引擎为 JavaScript,不然就容易报错。话说回来,我无意中学了 JavaScript,虽然在程序员圈子里,js 总是被吐槽的体无完肤,但是现在很多平台像网页设计、微信小程序、APP开发和 AE表达式等都已经广泛使用,所以学习它还是非常划算的,有兴趣的小伙伴可以去看我 JS 的专栏,内容比较少,但是你们要是有问题给我留言,我一定会去更新的。

二、对三百多个名字合理分组
三百多个人名可以按照每一组 30 至 50 个名字进行分组,分组的目的第一是便于管理,因为数量太多的话,万一要修改,就要牵一发而动全身,指不定哪儿就出错了,另一方面也是为了减少运行的压力,同时渲染 300 多个 3D图层很多电脑都会跑不动。
新建一个合成,像下面这个【人名组-1】这样,然后建一个空文本图层,这里将用于生成 40 个人名。

生成人名当然不能一个图层一个图层地去编辑,而是要靠数据去驱动,关于表达式的数据驱动动画可以参考下面这个连接:
After Effects 中的表达式语言 (adobe.com)你可以在外部写一个 Jason 来保存数据,它的好处是当你要修改数据的时候可以直接使用记事本编辑,而不需要操作 AE,坏处就是你可能不会编辑 Jason,还有你容易不小心删掉了数据文件。所以这部分我就不讲了,会用的人不需要,不会用的人讲了白讲。下面说说比较简单的方法,利用 Marker 保存文本。关于 AE 中 Marker 的用法可以看看 [AE 表达式]引用 Marker 注释 - 知乎 (zhihu.com) 这篇文章,了解一下就好,这里主要是用来保存文本。

看上图中,我在注释中填写了 40 个扯淡的假名,每个名字中间用中文逗号隔开,逗号的作用是将文本分割成数组的分隔符,它可以人任何一个打起来比较方便的不会被当作名字用的字符,比如竖线 | 或者横杠 - 。为啥我说不能用 Jason 呢?因为代码必须用英文,中文+英文标点的混合输入很容易出错,但是像上面这样只是处理字符串就不会出毛病了。
后面内容中所有用引用格式框起来的内容都是给对代码有兴趣的小伙伴看的,拿来党可以忽略。
新建一个空文本图层,然后在源文本中开启表达式控件。
thisComp.marker.key(1).comment; // 引用第一个合成标记的注释文本
可以尝试贴入上面这句代码,标记注释的文本会直接显示为文本,但是我们不是要这么使用的,而是要使用split() 方法将其转换为数组,从而得到这样的数组数据 ["张三", "李四", "王五", "甲乙", "丙丁", ...] ,然后每一个文本都可以利用图层索引数字来访问相应的数组元素。
// 从合成标记中引用文本,并使用中文逗号分隔符将其分割为数组
let _arr = thisComp.marker.key(1).comment.split(”,“);
// 使用图层索引访问数组元素
_arr[thisLayer.index - 1]
由于 JavaScript 数组的索引是从 0 开始,而 AE 图层索引是用 1 开始,所以在使用 thisLayer.index 获取当前图层索引后无必要减 1。上面这个代码搞上去就可以只显示一个名字了,然后 Ctrl+D 再复制 39 个图层,就可以让每一个图层显示一个名字。但是事情还没完,因为三百多个图层要是客户说改一下字体,或者调整一下字号,那又要改死了。所以我们又建立一个合成,名字就叫【设置和渲染】,然后建一个新的文本图层,叫做【字体样式设置】,然后在上面的代码基础上做一些调整来使所有文本都与【字体样式设置】的样式相同。
// 引用字体样式
let _fontStyle = comp('设置和渲染').layer('字体样式设置').text.sourceText.style;
// 从合成标记中引用文本,并使用中文逗号分隔符将其分割为数组
let _arr = thisComp.marker.key(1).comment.split(”,“);
// 使用源文本设置来代替直接替换内容