跳转到内容

2D 骨骼

2D 骨骼让一张图像身体一样弯折。任何 PNG 都可以在编辑器里直接做,不需要另外的动画软件,也不依赖第三方格式。 Spine 和 DragonBones 照常可用;角色是用哪个做的,就继续用哪个。

  • .esrig 是你编辑的文件:从图上切出的部件、部件下面的骨骼、每个顶点跟随哪些骨骼。
  • 保存时在旁边写出 .esmesh 和 .esprefab。场景只用这两个,所以打包后的游戏不带编辑数据。
  • 骨骼就是实体。 每根骨骼都是带 Transform 的普通实体,片段、Animator、遮罩和代码都能像控制其他实体一样控制它。

在内容浏览器里右键一张图,选 创建 2D 骨骼。然后沿顶部的模式依次操作,顺序就是做角色的顺序:

模式 做什么
部件 按透明区域切分 把一张肢体拼图切成部件,再拖动摆成角色。右侧列表决定谁画在上面。
骨骼 选 新建 后拖出一根骨骼;从另一根骨骼的末端开始,就接在它后面。选 选择 时,拖末端调方向,拖其余位置移动。
网格 每个部件一开始就盖着一层网格。格子越小弯折越顺,代价也越大。拖动顶点可以微调。
权重 按距离 先绑定最近的骨骼,再刷它弄错的地方:在右侧选一根骨骼后拖动。Shift 反向,Ctrl 平滑,[ 和 ] 调半径。整个部件跟选中骨骼 用于不弯的部件。
变形 新建形状,然后拖动把顶点推过去:呼吸、微笑、鼓起。

试摆 可以转动骨骼,看权重是否合适。它不改骨骼文件,回到 绑定姿势 就复原。放入场景 会保存并实例化 prefab。再次保存时,打开的场景里所有实例马上更新,实例上后加的内容会保留。

角色不会锁死在最初刷权重的那套骨骼上。

  • 挪骨骼。 在 绑定姿势 下拖动骨骼或改长度、旋转,改的就是绑定姿势本身。图不会动,所以放错的骨骼直接挪就行。
  • 换父骨骼。 在 骨骼 模式里给选中的骨骼选 父骨骼,或者选 无(根骨骼)。它在图上的位置不变,下面的骨骼也不变。
  • 改名、删除。 删除骨骼时,它的子骨骼和顶点交给父骨骼。
  • 重算权重。 再点一次 按距离,或 整个部件跟选中骨骼,然后再刷。
  • 换成另一个骨骼文件的骨架。 在 骨架 下选另一个 .esrig,点 换绑到这套骨架。骨骼按名字对应;没有同名骨骼的顶点交给最近的同名父骨骼。用同一套骨架的角色骨骼名和路径都一样,可以共用动画。

保存时,依赖旧名字的东西会跟着更新:

  • 用路径引用了改名或换父骨骼的动画(.estimeline)和 Animator 层遮罩(.esanimator)会被改写。一个文件里大部分路径都是这个角色的骨骼,才算这个角色的。指向已删除骨骼的轨道保留原路径,保存时会提示有几条。
  • 每根骨骼有一个固定 id,prefab 里的实体就按它命名。所以场景里加在某根骨骼上的东西(IK2D、挂在手上的剑),在别处重排、换父或删除骨骼之后仍然留在这根骨骼上。

在场景里,选中带 MeshSkin 的实体,在 Details 里用 换绑到:选场景里的另一套骨架,点 按名字换绑关节。每个关节在那套骨架下找同名实体;同一个名字出现两次时(比如两只胳膊下都有 hand),按父级的名字区分。找不到的关节保持原样,并列出名字。3D 蒙皮同样适用,比如把一件外套绑到身体上。

选中角色,在时间线里像其他实体一样给骨骼打关键帧。骨骼按它在根下面的路径命名,比如 hips/spine/arm。变形是 MeshMorph 组件上的权重,打关键帧的方式相同。

混合由 Animator 负责,和其他角色一样:1D、2D 混合、分层、遮罩和过渡都对骨骼有效。

给肢体的最后一根骨骼加 IK2D,把 target 指向一个实体。这根骨骼的末端会在动画播放之后去够它,所以不管播哪个片段,手都能握住要握的东西。

字段 含义
target 要够到的实体。
chainLength 弯折几根骨骼,包括这一根。两根时精确求解并保持弯折方向;更多时迭代求解。
bend 两根骨骼时中间关节的弯折方向:1 逆时针,-1 顺时针。
weight 从动画姿势向解算结果靠拢的比例。可以打关键帧。
matchTargetRotation 让这根骨骼同时转到目标的旋转,比如脚贴合斜坡。

给骨骼加 BoneConstraint2D。约束在 IK 之后运行,从躯干向外:

kind 作用
Limit Rotation 把局部旋转限制在 min 和 max 之间。穿过这根骨骼的 IK 链也会遵守。
Copy Rotation 取 source 的世界旋转,再加 offset。
Copy Position 站到 source 的世界位置上。
Look At 让骨骼的 +x 指向 source,再加 offset。

IK 和约束在播放模式下运行。根节点用负缩放镜像的角色同样适用。

骨骼是普通数据,游戏或工具都可以自己生成:

import {
autoWeightPart, bakeRig, coverRigPart, createRig, rigImageFromRGBA, spawnRig, IK2D,
} from 'esengine/rig2d';
const image = rigImageFromRGBA(width, height, rgba);
const rig = createRig('hero.png', width, height);
rig.bones.push(
{ name: 'upper', parent: -1, x: 0, y: 0, rotation: 0, length: 50 },
{ name: 'lower', parent: 0, x: 50, y: 0, rotation: 0, length: 50 },
);
const rect = { x: 0, y: 0, w: width, h: height };
const part = { name: 'arm', rect, x: 0, y: height / 2, ...coverRigPart(image, rect), weights: [] };
rig.parts.push(part);
autoWeightPart(rig, part);
const mesh = assets.createMesh(bakeRig(rig));
const { bones } = spawnRig(world, rig, { mesh, texture });
world.insert(bones[1], IK2D, { target, chainLength: 2, bend: 1, weight: 1, matchTargetRotation: false, enabled: true });
  • 一个骨骼角色是一个网格、一次绘制,最多 256 根骨骼。一个顶点最多跟随 4 根。
  • 同一角色的部件按你设定的顺序绘制。和场景其他内容之间,按 MeshRenderer.layer 再按 z 排序,和普通网格一样。
  • 选中角色时的外框显示的是绑定姿势,不是当前姿势。