跳到主要内容

Spine

Esoteric Software 的 Spine 是一款 2D 骨骼动画编辑器。playcanvas-spine 插件可以在 PlayCanvas 中播放 Spine 导出的动画,支持 WebGL2 和 WebGPU。

Spine 4.3 示例

Spine 4.3 示例,展示 Esoteric Software 的 Celestial Circus、Diamond 和 Tank 示例项目。

插件为引擎添加 spine 组件,并将 spine-core 运行时暴露为全局 spine,让脚本能够使用完整 Spine API。各个受支持 Spine 版本的用法见示例。

版本​

使用与导出动画所用 Spine 编辑器版本匹配的插件构建。构建位于仓库的 build 目录,每个版本还提供压缩后的 .min.js 文件。

Spine 编辑器插件
4.3playcanvas-spine.4.3.js
4.2playcanvas-spine.4.2.js
4.1playcanvas-spine.4.1.js
4.0playcanvas-spine.4.0.js
3.8playcanvas-spine.3.8.js
3.6playcanvas-spine.3.6.js

从 Spine 导出​

将每个骨架导出为 JSON,并同时导出纹理图集:一个 .atlas 文件及一个或多个 .png 页面。建议使用预乘 alpha,以获得最佳的抗锯齿边缘质量。

Spine 4.3 插件还可以加载二进制 .skel 格式导出的骨架,其体积比 JSON 小数倍,加载也更快。较早版本的插件遇到二进制骨架时会报错。

纹理​

纹理资源必须使用正确的 sRGB 设置,否则骨架颜色会显示错误:

  • Spine 4.3:禁用 sRGB。与 Spine 编辑器和 Spine 运行时一样,4.3 插件在伽马空间渲染;如果图集纹理启用了 sRGB,会记录警告。
  • Spine 3.6 到 4.2:启用 sRGB,这也是导入编辑器的纹理所使用的默认设置。

插件按名称查找图集各页面的纹理,因此纹理资源名称必须与图像文件名一致,例如 spineboy-pma.png。导入编辑器的纹理会按文件名命名。

在编辑器中使用 Spine​

  1. 将对应 Spine 版本的插件构建,以及 spine.js 添加到项目。
  2. 在脚本加载顺序中,确保插件在 spine.js 之前加载。
  3. 导入导出的 .json 或 .skel、.atlas 和 .png 文件。图集会作为文本资源导入,.skel 骨架会作为二进制资源导入。对于 Spine 4.3,禁用导入纹理的 sRGB。
  4. 为实体添加包含 spine 脚本的脚本组件,将图集、骨架和纹理资源赋给对应属性。二进制骨架应赋给 skeletonBinary,而不是 skeleton。priority 控制重叠骨架的绘制顺序,physicsInheritance 让物理响应实体运动。

spine 脚本在初始化时添加 spine 组件,因此应在其他脚本的 postInitialize 方法中控制动画:

import { Script } from 'playcanvas';

export class PlayRun extends Script {
static scriptName = 'playRun';

postInitialize() {
this.entity.spine.state.setAnimation(0, 'run', true);
}
}

Spine 动画教程提供完整编辑器项目,包含二进制和 JSON 骨架,以及控制动画的面板。

在仅使用引擎的项目中使用 Spine​

插件通过全局 pc 使用引擎,因此以模块方式导入引擎时,应在加载插件前设置该全局变量。应先创建应用,再将插件作为脚本资源加载,因为插件会在加载时将 spine 组件系统添加到应用。通过 AppBase 创建的应用需要 ScriptHandler、JsonHandler、TextHandler 和 TextureHandler 资源处理器,以加载这些资源:

import * as pc from 'playcanvas';
import { Asset, AssetListLoader, Entity } from 'playcanvas';

window.pc = pc;

// ... create and start the application ...

const assets = {
plugin: new Asset('playcanvas-spine', 'script', { url: 'playcanvas-spine.4.3.min.js' }),
skeleton: new Asset('spineboy-pro.json', 'json', { url: 'spineboy-pro.json' }),
atlas: new Asset('spineboy-pma.atlas', 'text', { url: 'spineboy-pma.atlas' }),
// named after the atlas page, and without sRGB for Spine 4.3
texture: new Asset('spineboy-pma.png', 'texture', { url: 'spineboy-pma.png' }, { srgb: false })
};

await new Promise((resolve) => {
new AssetListLoader(Object.values(assets), app.assets).load(resolve);
});

const spineboy = new Entity('spineboy');
spineboy.addComponent('spine', {
atlasAsset: assets.atlas.id,
skeletonAsset: assets.skeleton.id,
textureAssets: [assets.texture.id]
});
app.root.addChild(spineboy);

spineboy.spine.state.setAnimation(0, 'run', true);

通过 Spine 4.3 插件加载二进制 .skel 骨架时,使用 binary 资源类型:

const skeleton = new Asset('spineboy-pro.skel', 'binary', { url: 'spineboy-pro.skel' });

Spine 坐标会乘以 0.01,因此 Spine 编辑器中 700 像素高的骨架,在场景中高 7 个单位。

Spineboy 示例是一个使用插件的完整纯引擎项目:

Spineboy

控制动画​

spine 组件提供对骨架 spine-core 对象的访问:

属性说明
entity.spine.stateAnimationState,用于在轨道上播放动画并进行动画混合。
entity.spine.skeletonSkeleton,包含骨骼、槽位、皮肤和颜色。
entity.spine.spine插件对象,包含 priority、layers 和 timeScale 属性。

例如,可以在第二条轨道播放动画以进行混合,并监听事件:

const state = entity.spine.state;

// mix for 0.2 seconds when the animation changes
state.data.defaultMix = 0.2;

// loop the run animation on track 0, and play the shoot animation once on track 1
state.setAnimation(0, 'run', true);
state.setAnimation(1, 'shoot', false);
state.addEmptyAnimation(1, 0.2, 0);

state.addListener({
event: (entry, event) => {
console.log(`event ${event.data.name}`);
}
});

完整 API 请参阅 Spine 运行时指南和 spine-core API 参考。

颜色​

Spine 4.3 插件渲染 spine-core 运行时提供的颜色,因此可通过这些颜色对骨架进行颜色调制:

颜色调制对象
skeleton.color整个骨架。
slot.getPose().color通过 skeleton.findSlot(slotName) 找到的槽位。对槽位颜色设置了关键帧的动画会覆盖此值。
slot.getPose().darkColor槽位的暗色,用于黑色调制。
attachment.color通过 skeleton.getAttachment(slotName, attachmentName) 找到的附件。
const skeleton = entity.spine.skeleton;

// tint the whole skeleton
skeleton.color.set(1, 0.5, 0.5, 1);

// tint the head slot
skeleton.findSlot('head').getPose().color.set(1, 0, 0, 1);

Spine 4.3 插件支持槽位混合模式(普通、叠加、正片叠底和滤色)及黑色调制。

物理​

Spine 4.2 引入的物理约束,让头发、布料等骨架部分能够自行摆动并逐渐稳定。4.2 和 4.3 插件会在骨架动画播放时模拟这些约束。

在 Spine 4.3 插件中,物理约束还可以响应实体的移动和旋转,使物理驱动的部分在实体运动时产生滞后、摆动和恢复。物理继承默认禁用。在编辑器中,启用 spine 脚本的 physicsInheritance 属性;在代码中,使用插件的 skeletonPhysics 对象,即 spine-core 运行时的 SkeletonPhysicsMovement:

const { skeletonPhysics } = entity.spine.spine;

// pass the full movement and rotation of the entity to physics
skeletonPhysics.setPositionInheritance(1, 1);
skeletonPhysics.rotationInheritance = 1;

水平移动、垂直移动和旋转各有独立系数。0 到 1 之间的值只传递部分运动,0 表示不传递。旋转指实体绕局部 Z 轴的旋转,该轴也是骨架平面的法线。

实体瞬间移动时,例如重新生成时,应重置变换,避免将位置跳变传给物理模拟:

entity.setPosition(10, 0, 0);
entity.spine.spine.skeletonPhysics.resetTransform();

Spine 4.3​

Spine 4.3 更改了部分 spine-core API,例如 skeleton.setToSetupPose() 改为 skeleton.setupPose(),因此升级到 4.3 时,使用全局 spine 的脚本需要更新。详情见 Spine 运行时变更记录。

较早插件的 setTint 方法不受 4.3 插件支持,调用时会记录警告。请改用骨架、槽位和附件的颜色。

4.3 插件还可以将实体运动传递到物理模拟。

图层与绘制顺序​

骨架默认在 UI 图层中渲染。要在其他图层渲染,设置对应图层 ID:

const worldLayer = app.scene.layers.getLayerByName('World');
entity.spine.spine.layers = [worldLayer.id];

插件对象的 priority 决定重叠骨架的绘制顺序,数值较小的骨架先渲染。