年前又一次玩缺氧,看到跑动的小人,想了想2D骨骼动画的原理。骨骼彼此相连,每块骨骼的位置状态由前一块骨骼(和自己的)的旋转角度决定,最终由核心的那一块骨骼的位置决定(基本上只是一个点)。然后一块骨骼可以有多个连接点,其中一个连接点会连到上一级骨骼的某一个连接点。举个例子,胸腔可以连脑袋和腰还有胳膊,胳膊的上端会连到胸腔的肩关节。

我们姑且直接称连接点为关节,虽然这好像不符合关节的定义,不过不要在意那些细节。

那么针对每一个「关节」,就需要描述其在骨骼上的位置,比如从左上起始,记录XY坐标。但是随后考虑到要支持给骨骼替换不同的图片,图片尺寸可能并不一致(毕竟最开始的图都是我拿 Procreate 随手画的),统一从左上起始不够灵活。

想想给关节增加了 HAlign 和 VAlign 属性。HAlign 值为 end/right 的关节,X值是从图像右侧往左计算;HAlign值为center的关节,X则从图像中心开始往右计算,默认情况下为 start/left,从最左边往右计算。

02/07 更新:又想了一下,这里不涉及排版所以不需要 start/end,直接 left/right 更清晰。当时写顺手了,不过不是很重要。

在此基础上,假设现在定义了腿的骨骼,那我们可以定义和胯连接的关节的位置,比如顶部中心,HAlign=center VAlign=top,X和Y用于微调,这样不管换什么图片,偏差都不会太大。

然后是位置计算。骨骼是通过一个 BoneMap 的 struct 连接到上一级骨骼的,这个 map 有 From 和 To 两个属性,From 对应是自己的某个关节,To 对应上一级骨骼的某个关节。因此只要知道骨骼 From 相对于画布的绝对位置,再加上自己旋转的角度,就可以完成图像绕点旋转的绘制了。又因为 From 和 To 的绝对位置是完全一样的,于是转换成计算上一级骨骼上关节的位置,于是继续往上直到 root ……

到这里骨骼和关节的关系搞定了,也能渲染出静态图片,考虑到之后要添加的动画定义,用 struct 上的有限的几个方法创建项目得忙到猴年马月去,现在需要一个文件来描述动画项目。因为习惯 HTML,所有选了 XML。

用 Procreate 画了好多动画,我对动画的理解就是一帧帧的图像连在一起,那我就给我的 XML 增加 <project.animations /> 元素,让它可以添加很多不同的动画。然后给动画增加 <animation.frames /> 给它定义帧序列。

这会儿还没考虑关键帧和补间的问题,当时画个行走动画都是先在 Procreate 上试一下看看需要多少帧,旋转角度如何……指导思想是「反正只是一劳永逸的活动,搞完之后就可以换图量产」,文章开头的招财猫(如果我记得放图)就是这种「一劳永逸」的产物。

插一句,整个项目一开始就没有考虑做图形界面,所以包括创建骨骼和动画的一系列流程,都是考虑手写和脑补,就像 Web 开发那样。

Frame 的原理其实就是有一大堆 frame.setters>setter 元素,用于在某一帧内重新设置某个骨骼的旋转角度。借助 imagemagick 和 ffmpeg,现在可以把导出来的图片序列做成 GIF 或者视频了。

然后是换图的功能,这里又给 XML 加了 projects.skins>skin 元素,Skin 也像 frame 那样有 setter,可以覆盖掉 bone 的图像路径。

Skin 和 Frame 的 <setter> 其实是完全一样的,通过 subject/target/name/content 四个属性指定自己要设置什么东西的什么值。所以 Frame 可以设置项目的位置(其实是 root 的位置)实现走路的时候一上一下的效果;Skin 的 setter 除了设置骨骼的图片,还可以设置关节的坐标,针对不同的图片进行微调。

以上就是我手动倒腾出来的东西,画图和导出,玩了半天之后,想着看看专业的工具是啥样的,然后找到了不要钱的 DragonBones。看了教程,做了条能动的触手,行走动画,以及奇奇怪怪的东西,打开了新世界的大门。

DragonBones 的骨骼同样是树形结构,但和骨骼关联的是 slot 而不是图像。多张图像挂在一个 slot 下面,但同一时间只能展示一张。可以在动画时间轴上给某一帧选择不同的图像,关于这个特性我能想到的场景是可以在某几帧用不同的图像展示,比如角色劈砍的时候刀亮起来。

我开始写代码的时候我也有考虑过要不要支持 Sprite 做图像(像 Tiled 那样,它可以用 Sprite 做 tile),然而后来完全忘了还有这个设定。

DragonBones 的骨骼并不是首尾相连的,但骨骼末端和上一级骨骼的尖端的相对位置是固定的,上下两级的骨骼之前只存在旋转这一个关系。

看了导出的 JSON 文件,DragonBones 把动画分为旋转,平移和缩放,各自记录了开始的帧,时长,曲线。运动曲线是贝塞尔曲线,调整过的曲线只需要额外几个参数就能还原,学到了学到了。动画里还有一个 slot 对象,记录哪个插槽在哪一帧开始换了图片。