跳到主要内容

Spine 动画

使用面板选择各骨骼播放的动画、调整速度或暂停。

本教程使用 playcanvas-spine 插件,播放通过 Spine 编辑器制作的两个骨骼动画。坦克从二进制 .skel 导出文件加载,Celestial Circus 从 JSON 导出文件加载。完成后的场景见教程项目。用户手册的 Spine 页面详细介绍了插件。

添加插件​

项目使用 playcanvas-spine 仓库 build 文件夹中的两个脚本:

  • playcanvas-spine.4.3.js:适用于 Spine 4.3 导出骨骼的插件,提供 spine 组件,并将 spine-core 运行时暴露为全局变量 spine。
  • spine.js:名为 spine 的脚本,根据其属性指定的资源,为实体添加 spine 组件。

在项目设置的脚本加载顺序中,插件排在 spine.js 之前。

导入骨骼​

每个骨骼与纹理图集一起从 Spine 导出,包括 .atlas 文件和 .png 图集页面,然后导入项目的 spine43 文件夹:

文件资源类型
tank-pro.skel二进制
celestial-circus-pro.jsonJSON
tank-pma.atlas, celestial-circus-pma.atlas文本
tank-pma.png, celestial-circus-pma.png纹理

二进制 .skel 导出文件比 JSON 小数倍,加载也更快。编辑器将其作为二进制资源导入。

Spine 4.3 在伽马空间渲染,因此纹理资源关闭了 sRGB。纹理资源保留文件名,因为图集会引用这些名称。

向实体添加骨骼​

Tank 和 CelestialCircus 实体都具有脚本组件,挂载 spine 与 spineAnimation 脚本。spine 脚本通过以下属性引用骨骼资源:

  • Atlas:图集文本资源。
  • Skeleton:JSON 骨骼资源,供 CelestialCircus 使用。
  • Skeleton Binary:二进制骨骼资源,Tank 使用它替代 JSON 骨骼。
  • Textures:图集页面的纹理资源。
  • Priority:重叠骨骼的绘制顺序,优先级数值较小的先渲染。

插件将 Spine 坐标缩放为原来的 0.01,因此在 Spine 编辑器中高 700 像素的骨骼,在场景中高 7 个单位。通过实体缩放使骨骼适配视图:女孩位于上半部分,坦克及其长条地面位于下半部分。

播放动画​

spine 脚本在初始化时创建骨骼。实体上的 spineAnimation 排在它后面,因此自身 initialize 执行时骨骼已经存在,可以开始播放属性中指定的动画。省略骨骼或动画缺失检查后的脚本如下:

import { Script } from 'playcanvas';

export class SpineAnimation extends Script {
static scriptName = 'spineAnimation';

/**
* The name of the animation to play.
*
* @attribute
*/
animation = '';

/**
* Whether the animation loops.
*
* @attribute
*/
loop = true;

/**
* The playback speed of the skeleton.
*
* @attribute
* @range [0, 2]
*/
timeScale = 1;

initialize() {
const spine = this.entity.spine;
spine.state.setAnimation(0, this.animation, this.loop);
spine.spine.timeScale = this.timeScale;
}
}

entity.spine.state 是 spine-core 运行时的 AnimationState,负责在轨道上播放并混合动画;entity.spine.spine 是控制播放速度的插件对象。

控制动画​

SpineControls 实体上的 spineControls 脚本创建 HTML 面板。其 postInitialize 在所有实体的脚本初始化后运行,此时骨骼已经存在,可以为场景中的每个 spine 组件添加一张面板卡片:

for (const component of this.app.root.findComponents('spine')) {
panel.append(this.createCard(component));
}

每张卡片通过 spine-core 对象控制对应骨骼:

const { state, skeleton } = component;

// mix between animations when switching, instead of switching instantly
state.data.defaultMix = this.mix;

// list the animations of the skeleton
for (const animation of skeleton.data.animations) {
select.append(new Option(animation.name, animation.name));
}

// play the selected animation
state.setAnimation(0, select.value, true);

// change the speed, or pause with a speed of 0
component.spine.timeScale = paused ? 0 : speed;

各卡片上的标记根据骨骼资源类型显示导出格式。更多 spine-core API 见 Spine 运行时指南。