# spine-pixi 运行时文档

> **Licensing**
>
> 将官方的Spine运行时整合到你的应用程序之前, 请仔细阅读 [Spine运行时许可页面](/spine-runtimes-license).

# 简介

本页面为官方 [PixiJS](https://pixijs.com/)运行时的文档汇总页, 你可以在此找到官方 PixiJS 运行时列表及其对应的兼容版本:

<table>
  <tbody>
    <tr>
      <th>运行时</th>
      <th>PixiJS 版本</th>
      <th>是否维护</th>
    </tr>
    <tr>
      <td id="spine-pixi-v8">
        <a href="https://github.com/EsotericSoftware/spine-runtimes/tree/4.2/spine-ts/spine-pixi-v8">spine-pixi-v8</a>
      </td>
      <td>
        <p>8 (最低 8.16)</p>
      </td>
      <td>
        <p>Yes</p>
      </td>
    </tr>
    <tr>
      <td id="spine-pixi-v7">
        <a href="https://github.com/EsotericSoftware/spine-runtimes/tree/4.2/spine-ts/spine-pixi-v7">spine-pixi-v7</a>
      </td>
      <td>  
        <p>7 (最低 7.2.0)</p>
      </td>
      <td>  
        <p>Yes</p>
      </td>
    </tr>
    <tr>
      <td id="spine-pixi">
        <a href="https://github.com/EsotericSoftware/spine-runtimes/tree/ca00e561368f2c838026e2d02aed57b709b037ef/spine-ts/spine-pixi">spine-pixi</a>
      </td>
      <td>
        <p>7 (最低 7.2.0)</p>
      </td>
      <td>
        <p>No (切到 spine-pixi-v7)</p>
      </td>
    </tr>
  </tbody>
</table>

除非另有说明, 否则本文中的示例和 GitHub 文件链接均表示 v8 版本. 然而本文中所有内容亦适用于 v7 版本, 只需将路径和链接中的 `-v8` 替换为 `-v7` 即可.

Spine PixiJS 运行时基于 [spine-ts core](https://github.com/EsotericSoftware/spine-runtimes/tree/4.2/spine-ts/spine-core) 实现的, 是 Spine Runtimes 核心 API 的渲染器无关 TypeScript 实现.

`spine-pixi-v8` 既可以使用 [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) 也可以使用 [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)进行渲染, 还可以使用 [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
`spine-pixi-v7` 则只能使用 WebGL 渲染(WebGPU 支持是在 PixiJS 8 中引入的).

Canvas 渲染在 `spine-pixi-v8` 中可用, 但有以下限制:

- 槽位对象 (通过 `addSlotObject` 添加到槽位的 Pixi 容器) 无法渲染
- 不支持 Tint black (双色着色)
- 不支持 Blend modes 模式

Spine PixiJS 运行时支持全部 Spine 功能.

## 安装

要在 Pixi 项目中使用 Spine PixiJS 运行时, 必须首先导入其源码.

### 原生JavaScript

在原生 JavaScript 中安装运行时, 请使用 `script` 标记引入 [unpkg](https://unpkg.com) 中的 spine-pixi-v8 运行时(也可以自建服务):

```
<script src="https://unpkg.com/@esotericsoftware/spine-pixi-v8@4.2.*/dist/iife/spine-pixi-v8.js"></script>
```

> **注意:** 确保 spine-pixi 的 `major.minor` 版本与用来导出资产的Spine编辑器的 `major.minor` 版本一致. 更多详情请见 [同步版本号](/spine-versioning#同步版本) 一节.

这将自动安装 Spine 扩展, 然后便可在 Pixi 项目中使用 `spine-pixi-v8` 运行时. 查看 [`index.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/index.html) 可了解完整示例.

spine-pixi-v8 软件包不仅提供了用于调试的源码映射, 还提供了 spine-pixi-v8 的精简版(minified), 在 unkg URL 中将 `.js` 后缀替换为 `.min.js` 即可使用.

如需自行构建 `spine-pixi-v8.js`, 请遵循 spine-ts 的 [README.md](/git/spine-runtimes/spine-ts/README.md) 文档内容.

### NPM和Yarn

若使用 NPM 或 Yarn 管理软件包依赖关系, 则按常规方式安装 spine-pixi-v8 即可:

```
npm install @esotericsoftware/spine-pixi-v8@~4.2.0
```

> **注意:** 确保 spine-pixi 的 `major.minor` 版本与用来导出资产的Spine编辑器的 `major.minor` 版本一致. 更多详情请见 [同步版本号](/spine-versioning#同步版本) 一节.

完成后请直接导入 Spine 类.

```
import PIXI from "pixi.js"
import { Spine } from '@esotericsoftware/spine-pixi-v8';
```

这也将自动安装 Spine 扩展, 然后便可在项目中使用 Spine PixiJS 运行时. 最小示例请查看 [`esbuild/TypeScript project`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/typescript).

模块包中包含源码映射和 `d.ts` 类型信息, 可用于调试改进和实际开发.

## 示例

spine-pixi 运行时中内置了几个演示用的功能示例.

如需在本地运行这些示例则:

1. 在操作系统上安装 Git 和 [Node.js](https://nodejs.org/en).
2. 克隆 spine-runtimes 代码库: `git clone https://github.com/esotericsoftware/spine-runtimes`
3. 导航至 `spine-runtimes/spine-ts` 目录, 然后运行 `npm install & npm run dev`.

以上步骤将构建 spine-pixi-v8 运行时, 并打开浏览器显示所有基于 spine-ts 运行时的示例目录.

点击便可查看你感兴趣的 spine-pixi-v8 示例, 其对应代码位于 [`spine-runtimes/spine-ts/spine-pixi-v8/example`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example) 文件夹.

## 更新spine-pixi-v8运行时

在更新项目中的 spine-pixi-v8 运行时前, 请先阅读 [Spine 编辑器和运行时版本管理指南](/spine-runtime-architecture#版本控制).

如需更新原生 JavaScript 的 spine-pixi-v8 运行时, 请更改 spine-pixi-v8 的 unkg URL 中 `src` 属性或 `script` 标记中的版本号.

而使用 NPM 或 Yarn 更新 spine-pixi-v8 运行时则请更改 `package.json` 文件中的版本号.

> **注意:** 若更改了 spine-pixi-v8 的 `major.minor` 版本, 则必须使用同版本的Spine编辑器重新导出Spine skeleton！更多详情请见 [同步版本号](/spine-versioning#同步版本) 一节.

# 使用 spine-pixi-v8

Spine PixiJS 运行时支持全部 Spine 功能. `spine-pixi-v8` 可使用 [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API)、[WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API) 或 [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) 进行渲染. `spine-pixi-v7` 只能使用 WebGL 渲染(WebGPU 支持是在 PixiJS 8 中引入的).

## 资产管理

### 导出适用于Spine PixiJS的资产

![](/img/spine-runtimes-guide/spine-ue4/export.png)

请按Spine用户指南中的操作说明来:

1. [导出skeleton和动画数据](/spine-export)
2. [导出包含skeleton图像的texture atlases](/spine-texture-packer)

导出的skeleton数据和texture atlas将包含以下文件:

![](/img/spine-runtimes-guide/spine-ue4/exported-files.png)

1. `skeleton-name.json` 或 `skeleton-name.skel` 文件, 包含了skeleton和动画数据.
2. `skeleton-name.atlas`, 包含了texture atlas的相关信息.
3. 单个或数个 `.png` 文件, texture atlas中的一页就对应着这样一个文件, 而texture atlas则保存着skeleton所需的图片包.

> **请注意:** 建议将skeleton导出为二进制格式而非JSON格式, 因为二进制格式尺寸更小且加载速度更快.

在服务器上提供这些文件时, 应设置正确的 MIME 类型:

- `skel` 文件为 `application/octet-stream`
- `json` 文件为 `application/json`
- `atlas` 文件为 `application/octet-stream`
- `png` 文件为 `image/png`

### 更新Spine资产

在开发过程中, 你可能需要经常更新Spine skeleton数据和texture atlas文件. 只需从Spine编辑器重新导出并替换Pixi项目中的资产文件即可更新这些源文件(`.json`, `.skel`, `.atlas`, `.png`).

确保 spine-pixi-v8 的 `major.minor` 版本与用来导出资产的Spine编辑器的 `major.minor` 版本一致. 更多详情请见 [同步版本号](/spine-versioning#同步版本) 一节.

## 核心类

Spine PixiJS API 构建于通用的 TypeScript [spine-core](/git/spine-runtimes/spine-ts/spine-core) 运行时上, 该运行时提供了平台无关的核心类和算法, 可用于加载、查询、修改 Spine Skeleton和驱动 Skeleton 动画.

本节将简要讨论日常使用 Spine PixiJS 时会遇到的关键核心类. 请同时查阅 [Spine 运行时指南](/spine-runtimes-guide) 详细了解Spine运行时的架构、核心类及API使用方法.

[`TextureAtlas`](/git/spine-runtimes/spine-ts/spine-core/src/TextureAtlas.ts) 类存储了从 `.atlas` 文件和其对应的 `.png` 图像中加载的数据.

[`SkeletonData`](/git/spine-runtimes/spine-ts/spine-core/src/SkeletonData.ts) 类存储了从 `.json` 或 `.skel` Skeleton文件中加载的数据. Skeleton数据包含了骨骼层次结构、槽位、附件、约束、皮肤和动画的信息.通常做法是通过 `Atlas` 来加载 `SkeletonData` 实例, 实例再从 `Atlas` 中获取Skelton所需的图片. 因此 `Atlas` 可视为创建 `Skeleton` 实例的蓝图. 同一套atlas和Skelton数据可以实例化出多个skeleton实例, 这些实例将共享被加载的数据, 由此最大限度地缩短运行时加载时间并减小内存开销.

[`Skeleton`](/git/spine-runtimes/spine-ts/spine-core/src/Skeleton.ts) 类存储的是用 `SkeletonData` 实例创建的Skeleton实例. Skeleton实例会存储其当前pose, 即当前的骨骼位置和槽位、附件及活动皮肤的配置. 虽然手动修改骨骼变换(transform)可以更改当前pose, 但更常规的做法是应用 `AnimationState` 中的动画来更改当前pose.

[`AnimationState`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts) 类负责跟踪需应用于skeleton的动画, 根据上一帧和当前渲染帧之间的时间提前和mix这些动画, 并将动画应用于skeleton实例, 从而设置其当前pose. `AnimationState` 会查询 [`AnimationStateData`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationStateData.ts) 以获取动画间的mix时间, 如果一对动画之间没有设置mix时间, 则会使用默认mix时间.

Spine Pixi 运行时是基于这些核心类构建的.

## Spine Pixi运行时

spine-pixi-v8 运行时会自动在 Pixi 中安装两个 [`Asset`](https://pixijs.download/release/docs/extensions.html#ExtensionType) 类型的扩展: [`skeletonLoader`](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/assets/skeletonLoader.ts) 和 [`atlasLoader`](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/assets/atlasLoader.ts). 它们给 [PIXI.Assets](https://pixijs.download/release/docs/assets.Assets.html)加入了(预)加载 `.json`、`.skel` 和 `.atlas` 文件的功能.

`Spine` 类是 Pixi [`ViewContainer`](https://pixijs.download/release/docs/scene.ViewContainer.html) 类的扩展 (PixiJS 7 中则是 [`Container`](https://pixijs.com/7.x/guides/components/containers)), 并提供了一个工厂函数, 它可以用已加载的 Skeleton 数据和 atals 文件创建 `Spine` 的容器实例.

对于 PixiJS v8, 它额外安装了 SpinePipe, 它是对 WebGPUPipes、WebGLPipes 和 CanvasPipes 的扩展.
最后它还会安装 `DarkTintBatcher`, 这是用于支持 tint black 的合批处理器.
而在 PixiJS v7 中, 它会安装 `RendererPlugin` 类型的扩展, 用于渲染在附件上使用了 tint black 的 Spine 容器.

### 加载Spine资产

Spine 资产(如 Skeleton 数据 `.json`/`.skel` 文件或 `.atlas` 文件)可通过 `PIXI.Assets` 类实例(如  `Assets.load` )中的常用函数加载.

在创建 `Spine` 容器实例前, 必须加载 Skeleton 和对应的 atals 文件. 一种方法是用 `Assets.add` 和 `Assets.load` 函数来加载它们.

- `Assets.add({ alias: string, src: string })`:可以指定如何解析 `url` 中的资产 `alias`. 该函数可用于所有 Spine 资产文件(`.json`、`.skel`和`.atlas`).
- `Assets.load(string[])`: 加载用 `Assets.add` 添加的资产 `alias`.

假设你将 Skeleton 数据导出为二进制Skeleton文件 `skeleton.skel`, 并将 atlas 导为 `skeleton.atlas` 文件, 其对应的图像文件为 `skeleton.png`, 则可以像这样加载资产:

```
PIXI.Assets.add({ alias: "skeleton-data", src: "path/to/skeleton.skel" });
PIXI.Assets.add({ alias: "skeleton-atlas", src: "path/to/skeleton.atlas" });
await PIXI.Assets.load["skeleton-data", "skeleton-atlas"];
```

`Assets.load` 函数从 `skeleton.skel` 文件加载 [SkeletonData](/git/spine-runtimes/spine-ts/spine-core/src/SkeletonData.ts), 并将其缓存于 `skeleton-data` 键中. 它还会从 `skeleton.atlas` 文件加载 [TextureAtlas](/git/spine-runtimes/spine-ts/spine-core/src/TextureAtlas.ts), 并从对应的 `skeleton.png` 文件中加载texture. Atlas缓存在 `skeleton-atlas` 键中, 而每个texture atlas页图像都是透明加载, 无需显式加载它们.

#### 加载自定义 Atlas 页 texture

默认情况下, atlas 加载器会自动加载 `.atlas` 文件中引用的texture图片. 但也可以用 `data.images` 元数据选项来自定义 texture. 这在你已经预加载了texture 或想使用来自其他源的 texture 时会有帮助.

```javascript
// Provide a single texture for the first atlas page
await PIXI.Assets.load({
    src: "path/to/skeleton.atlas",
    data: { images: myTextureSource }
});

// Provide textures for multiple atlas pages by name
await PIXI.Assets.load({
    src: "path/to/skeleton.atlas",
    data: {
        images: {
            "skeleton.png": firstPageTextureSource,
            "skeleton2.png": secondPageTextureSource
        }
    }
});

// You can also provide URLs instead of TextureSource objects
await PIXI.Assets.load({
    src: "path/to/skeleton.atlas",
    data: {
        images: {
            "skeleton.png": "path/to/custom-texture.png"
        }
    }
});
```

`images` 选项接受:

- 一个 `TextureSource` 或 `string` (URL): 用于首个 atlas 页
- 一个 `{ [key: string]: TextureSource | string }`: 将 atlas 页名称映射到texture或URL

你还可以使用 `imageMetadata` 选项向 texture 加载器传递额外的元数据:

```javascript
await PIXI.Assets.load({
    src: "path/to/skeleton.atlas",
    data: {
        imageMetadata: { /* options passed to texture loader */ }
    }
});
```

预加载完成后, 可通过 `Asset.get(atlasKey)` 访问 `TextureAtlas`. 你也同样可以通过 `Asset.get(skeletonKey)` 访问原始的 `.skel` 文件. 不过在此时, `SkeletonData` 实例还没准备好.

原始的Skeleton数据和atlas本身无法进行动画或渲染. 因为你需要用它们构建一个 [`Spine`](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts)容器. 用同一个资产键(key)实例化的 `Spine` 容器会共享同一份Skeleton数据和atlas.

你也可以使用 [Pixi bundles](https://pixijs.com/examples/assets/bundle) 来加载资产. 要使 `Assets.init` 中的 `texturePreference.format` 和 `texturePreference.resolution` 属性生效, 需按 `FILENAME@RESOLUTION.FORMAT.atlas` 格式指定atlas名称.
以下是一个 bundle 中的 `manifest.json` 文件示例, 演示了如何在指定了 `format` 和 `resolution` 的同时加载该文件:

```json
{
    "bundles": [
        {
            "name": "spineboy",
            "assets": [
                {
                    "alias": ["spineboyAtlas"],
                    "src": [
                        "spineboy.png.atlas",
                        "spineboy@2x.png.atlas",
                        "spineboy@3x.png.atlas",
                        "spineboy.webp.atlas",
                        "spineboy@2x.webp.atlas",
                        "spineboy@3x.webp.atlas"
                    ]
                },
                { "alias": ["spineboyData"], "src": ["spineboy-pro.json"] }
            ]
        }
    ]
}
```

```javascript
// Initialize Assets with given manifest and preference
await PIXI.Assets.init({
  basePath: './assets/spineboy-bundle',
  manifest: './manifest.json',
  texturePreference: {
    resolution: Math.min(PIXI.utils.isMobile.any ? window.devicePixelRatio : 3, 3),
    format: ['webp', 'png'],
  },
});

// Load the bundle that includes the skeleton data and atlas
await PIXI.Assets.loadBundle("spineboy");
```

### 创建Spine容器对象

加载了原始Skeleton数据和对应的atlas后, 即可创建 `Spine` 容器.

#### spine-pixi-v8 4.2 版本

在 4.2 版本中, 使用静态 `from()` 方法从已加载的资产创建 Spine 容器:

```javascript
// Pixi app creation
const app = new PIXI.Application({ ... });
...
// Create a Spine container using Spine.from()
const spineboy = Spine.from({ skeleton: "spineboyData", atlas: "spineboyAtlas" });

// Add the Spine container to the stage
app.stage.addChild(spineboy);
```

#### spine-pixi-v8 4.3+ 版本

在 4.3 及更高版本中, 可以直接将选项传递给 [`Spine`](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts) 构造函数:

```javascript
// Pixi app creation
const app = new PIXI.Application({ ... });
...
// Create a Spine container by passing SpineFromOptions directly to the constructor
const spineboy = new Spine({ skeleton: "spineboyData", atlas: "spineboyAtlas" });

// Add the Spine container to the stage
app.stage.addChild(spineboy);
```

`Spine` 构造函数 (4.3+) 和 `Spine.from()` (4.2) 都需要一个 [SpineFromOptions](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts#SpineFromOptions) 对象作为参数. 它需要:

- `skeleton`: 加载到 Assets 中的 Skeleton `.skel`或`.json` 文件的资产名称
- `atlas`: 加载到 Assets 中的 atlas 文件的资产名称
- (可选) `scale`: 传入Skeleton读取器(reader)的值. 若省略则默认为 1.
- (可选) `darkTint`: 若置为 `true`, 则使用 dark tint 渲染器来渲染 Skeleton; 若置为 `false`, 则使用默认的 pixi 渲染器来渲染 Skeleton; 若为 `undefined`, 则在存在 tint black 槽位时使用 dark tint 渲染器
- (可选) `autoUpdate`: 设置 Spine 实例的 `autoUpdate` 值. 若省略则置为 `true`, 动画将会自动播放. 反之则将调用 `update` 方法.
- (可选) `boundsProvider`: 边界提供器. 若未指定该值, 则将使用动态边界, 在请求时根据当前帧计算. 详情请阅读 [边界](#bounds) 一节.
- (可选) `allowMissingRegions`: 若置为 `true`, 允许 atlas 附件加载器在 skeleton 中引用的区域缺失时继续加载. 若省略则默认为 `false`.
- (可选) `ticker`: 当 `autoUpdate` 为 `true` 时使用的 ticker. 若省略则使用 `Ticker.shared`. 详情请阅读 [使用自定义Ticker](#使用自定义ticker) 一节.

#### 高级用法 (4.3+)

如需对初始化过程进行更多控制, 你可以使用静态 `createOptions()` 函数先生成初始化配置, 然后再传递给构造函数:

```javascript
// Create initialization options
const options = Spine.createOptions({ skeleton: "spineboyData", atlas: "spineboyAtlas" });

// Later, create the Spine container using the pre-built options
const spineboy = new Spine(options);

// Add the Spine container to the stage
app.stage.addChild(spineboy);
```

`createOptions()` 函数接受相同的 [SpineFromOptions](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts#SpineFromOptions) 参数, 并返回可传递给 `Spine` 构造函数的 [SpineOptions](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts#SpineOptions). 当你想提前准备配置、为多个实例重用相同配置、或对 `Spine` 类进行子类化以创建自定义 spine 容器时, 这会很有用.

> **注意:** `from()` 静态方法在 4.3 版本中已弃用, 将在 4.4 版本中移除. 请迁移到直接使用 `SpineFromOptions` 的构造函数, 或在需要将选项创建与实例化分离时使用 `createOptions()`.

## Spine容器

`Spine` 容器是 Pixi [`ViewContainer`](https://pixijs.download/release/docs/scene.ViewContainer.html)(PixiJS 7 则是 [Container](https://pixijs.com/7.x/guides/components/containers))的扩展, 用于存储、更新和渲染 `Skeleton` 及其相关的 `AnimationState`. 如前一节所述, `Spine` 容器实例通过 Skeleton 数据和atlas创建. `Skeleton`和 `AnimationState` 可分别通过 `skeleton` 和 `state` 字段访问.

在每一帧中 (当 `autoUpdate` 置为 `true` 时), `Spine` 容器都会:

- 更新 `AnimationState`
- 将 `AnimationState` 应用到 `Skeleton` 上
- 更新 `Skeleton` 的世界变换, 产生一个新的pose
- 以当前pose来渲染 `Skeleton`

### 边界

默认情况下, `Spine` 容器的边界是动态计算的. 在调用 `getBounds` 函数后, 会根据当前帧计算返回的边界.
如果你需要基于网格包面(hulls)的精准交互, 这个功能可能会派上用场.
不过, 在需要设置准确的 skeleton 的尺寸时就不太方便了, 因为宽度改变后会导致动态边界根据当前帧缩放 skeleton.
而且每次请求时都重新计算边界可能会拉低性能.

如果需要边界只计算一次且保持尺寸不变, 那么可以在构造函数中或运行时指定 `SpineBoundsProvider`.
边界提供器(Bounds Provider)可以恒定Spine GameObject的边界框尺寸, 可用的 `SpineBoundsProvider` 包括:

- `SetupPoseBoundsProvider`: 根据skeleton的setup pose计算边界框.
- `SkinsAndAnimationBoundsProvider`: 根据已有皮肤和动画的最大运动范围得出边界框.
- `AABBRectangleBoundsProvider`: 只返回在构造函数中已指定的边界框恒定值.

你还可以通过实现 `SpineBoundsProvider` 接口来创建自定义的 `SpineBoundsProvider`.

请在[此处](/git/spine-runtimes/spine-ts/spine-pixi-v8/src/Spine.ts)参阅其函数签名和 jsdoc.

在 [`bounds.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/bounds.html) 的示例可以让你比较不同的边界提供器及其对应效果.

### 使用自定义Ticker

默认情况下, 当 `autoUpdate` 为 `true` 时, `Spine` 容器使用 `Ticker.shared` 来自动更新每一帧的 skeleton 和动画状态. 你可以提供自定义 ticker, 这在你想一起控制多个 Spine 对象的播放速度, 或通过停止/启动 ticker 来暂停/恢复动画时很有用.

要使用自定义 ticker, 请将其传入构造函数中:

```javascript
// Create a custom ticker
const myTicker = new PIXI.Ticker();
myTicker.start();

// Create a Spine container with the custom ticker
const spineboy = new Spine({
    skeleton: "spineboyData",
    atlas: "spineboyAtlas",
    ticker: myTicker,
});

// Control playback speed for all Spine objects using this ticker
myTicker.speed = 0.5; // Half speed

// Pause all animations
myTicker.stop();

// Resume all animations
myTicker.start();
```

ticker 可以在运行时通过 `ticker` 属性更改:

```javascript
// Switch to a different ticker
spineboy.ticker = anotherTicker;
```

如果在更改 ticker 时 `autoUpdate` 为 `true`, 更新回调将自动从旧 ticker 更新到新 ticker.

### 应用动画

使用 `AnimationState` 可对 `Spine` 容器中显示的Skeleton应用动画.

> **注意:** 关于动画轨道和队列动画的详情, 请参阅Spine运行时指南中的 [应用动画](/spine-applying-animations#AnimationState-API) 一节..

调用动画状态 `setAnimation` 可以在轨道 0 上设置某段动画:

```javascript
spineObject.state.setAnimation(0, "walk", true);
```

第一个参数指定轨道号, 第二个参数表示动画名称, 第三个参数指定是否循环播放动画.

可以使用 `addAnimation` 来队列多段动画:

```javascript
spineObject.state.setAnimation(0, "walk", true);
spineObject.state.addAnimation(0, "jump", 2, false);
spineObject.state.addAnimation(0, "run", 0, true);
```

`addAnimation()` 的第一个参数是轨道号. 第二个参数是动画的名称. 第三个参数指的是延迟时间(以秒为单位), 延迟后该动画就会替换轨道上的前一个动画. 最后一个参数定义是否应循环播放动画.

在上例中, 首先播放的是`"walk"`动画. 2 秒后, 播放一次`"jump"`动画, 然后过渡到`"run"`动画, 最后将循环播放这个`"run"`动画.

从一个动画过渡到另一个动画时, `AnimationState` 会在某段时间内mix(混合)动画, 该时间称为mix时长. 这些mix时长保存在 `AnimationStateData` 实例中, 而 `AnimationState` 会从中获取mix时长.

也可通过 `AnimationState.data` 属性获取 `AnimationStateData` 实例. 你既可以为它设置全局的默认的mix时长, 也可以单独设置某对动画间的mix时长:

```javascript
spineObject.state.data.setDefaultMix = 0.2;
spineObject.state.data.setMix("walk", "jump", 0.1);
```

在设置或添加动画后, 会返回一个 `TrackEntry` 对象, 通过该对象可以进一步定制动画的播放细节. 例如, 你可以用轨道条目来设置mix时长或者反向播放动画:

```javascript
const entry = spineObject.state.setAnimation(0, "walk", true);
entry.mixDuration = 0.4;
entry.reverse = true;
```

更多选项请参阅 [`TrackEntry` 类文档](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L785).

> **注意:** 切勿不要函数作用域外保留 `TrackEntry` 实例. 轨道条目会在内部重复使用, 因此一旦发生轨道条目的 [销毁(dispose)事件](/spine-api-reference#AnimationStateListener-dispose), 轨道条目就会失效.

使用空动画可以将 skeleton 从 setup pose 平滑过渡到动画中的动作, 反之亦可:

```javascript
spineObject.state.setEmptyAnimation(0, 0);
spineObject.state.addAnimation(0, "walk", 0).mixDuration = 0.5;
spineObject.state.addEmptyAnimation(0, 0.5, 6);
```

与 `setAnimation` 类似, `setEmptyAnimation()` 的第一个参数指定轨道号. 第二个参数指定mix持续时间(以秒为单位), 这段时间可以mix掉之前的动画并过渡到空动画.

与 `addAnimation` 类似, `addEmptyAnimation()` 的第一个参数也是指定轨道号. 第二个参数指定mix持续时间(以秒为单位). 第三个参数则是延迟时间(以秒为单位), 轨道上的上一个动画在延迟后将mix到空动画.

调用 `AnimationState.clearTrack()` 可以立即清除轨道上的所有动画. 要清空全部轨道, 可使用 `AnimationState.clearTracks()`. 这将使 skeleton 保持最后的姿势, 而这通常不是需要的效果. 所以此时可以使用空动画自然地过渡到setup pose.

要将skeleton重置为setup pose, 请使用 `Skeleton.setupPose()`:

```javascript
spineObject.skeleton.setupPose();
```

这会把骨骼和槽位重置为setup pose中的配置. 使用 `Skeleton.setupPoseBones()` 或 `Skeleton.setupPoseSlots()` 则只会将骨骼或槽位重置为setup pose设置.

### AnimationState事件

`AnimationState` 将在动画播放的生命周期中触发事件. 你可以监听这些事件来按需响应. Spine Runtimes API 定义了以下几种[事件类型](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L1108):

- `start`: 动画开始时触发.
- `interrupt`: 当清除了某条动画轨道或设置了某个新动画时触发.
- `end`: 当不再应用某个动画时触发.
- `dispose`: 当销毁了某个动画的轨道条目时触发.
- `complete`: 当动画完成循环时触发.
- `event`: 当用户定义的[事件](/spine-events#Events)触发时触发.

要接收事件, 你可以在 `AnimationState` 上注册一个 [`AnimationStateListener`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L1117) 回调函数接收所有动画的事件, 也可以注册到 `TrackEntry` 上来监听队列中某个动画的事件:

```javascript
spineObject.state.addListener({
    start: (entry) => log(`Started animation ${entry.animation.name}`),
    interrupt: (entry) => log(`Interrupted animation ${entry.animation.name}`),
    end: (entry) => log(`Ended animation ${entry.animation.name}`),
    dispose: (entry) => log(`Disposed animation ${entry.animation.name}`),
    complete: (entry) => log(`Completed animation ${entry.animation.name}`),
    event: (entry, event) => log(`Custom event for ${entry.animation.name}: ${event.data.name}`)
})

trackEntry.listener = {
    event: (entry, event) => log(`Custom event for ${entry.animation.name}: ${event.data.name}`)
}
```

请阅读 [`events-example.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/events-example.html) 了解更多详情.

## 皮肤

许多应用程序和游戏都允许用户用头发、眼睛、裤子或耳环、包包等配饰等部件来创建自定义形象. 而用Spine可以通过 [使用皮肤](/spine-examples-mix-and-match) 来实现这一效果.

可以像这样从其他皮肤中组合一套自定义皮肤:

```javascript
const skeletonData = spineObject.skeleton.data;
const skin = new spine.Skin("custom");
skin.addSkin(skeletonData.findSkin("skin-base"));
skin.addSkin(skeletonData.findSkin("nose/short"));
skin.addSkin(skeletonData.findSkin("eyelids/girly"));
skin.addSkin(skeletonData.findSkin("eyes/violet"));
skin.addSkin(skeletonData.findSkin("hair/brown"));
skin.addSkin(skeletonData.findSkin("clothes/hoodie-orange"));
skin.addSkin(skeletonData.findSkin("legs/pants-jeans"));
skin.addSkin(skeletonData.findSkin("accessories/bag"));
skin.addSkin(skeletonData.findSkin("accessories/hat-red-yellow"));
spineObject.skeleton.setSkin(skin);
spineObject.skeleton.setupPoseSlots();
```

使用 `Skin()` 构造函数将创建自定义皮肤.

从skeleton中可获取 `SkeletonData`. 使用 `SkeletonData.findSkin()` 则可以按名称查找皮肤.

通过 `Skin.addSkin()` 能将所有需要组合的皮肤组件添加到新建的自定义皮肤中.

最后, 在 `Skeleton` 上设置新皮肤, 并调用 `Skeleton.setupPoseSlots()` 来确保先前的皮肤和/或动画附件没有残留.

完整示例代码参见 [`mix-and-match-example.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/mix-and-match-example.html).

## 设置骨骼变换

在Spine Editor中创建skeleton时, skeleton定义skeleton世界坐标系下——即所谓的"skeleton坐标系". 该坐标系可能与Pixi的坐标系不一致. 因此 `Spine` 容器的鼠标和触摸操作的坐标值需要转换到skeleton坐标系——比如用户需要通过触控来移动骨骼的时候.

`Spine` 容器提供了 `pixiWorldCoordinatesToBone(point: { x: number, y: number}, bone: Bone)` 方法, 该方法接受一个 `Spine` 容器坐标作为参数, 并将其转换为相对于指定骨骼的skeleton坐标点.

反之, 从 skeleton 坐标系转换到 Pixi 坐标系, 可以使用 `Spine.skeletonToPixiWorldCoordinates(point: { x: number, y: number})` 实现.

完整示例代码请参见 [`control-bones-example.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/control-bones-example.html).

## 在槽位中添加 Pixi 对象

`Spine` 类有三种方便的方法, 可为 pixi `Container` 添加或移除槽位.

```
addSlotObject (slotRef: number | string | Slot, pixiObject: Container, options?: { followAttachmentTimeline?: boolean }): void
```

此方法会将 `pixiObject` 添加到被 `slotRef` 引用的槽位中. 你可以传入名称、槽位索引或槽位对象本身.

每个槽位只能分配一个 Pixi 对象. 一旦添加了 Pixi 对象, Pixi 对象的某些属性(例如变换(transform)和遮罩(mask))将由 `Spine` 对象自动管理.

`followAttachmentTimeline` 选项可以同步当前附件的可见性与槽位的可见性. 默认情况下, 该项的值被置为 `false`.

因此为确保对添加的对象有进一步控制, 强烈建议将对象封装到一个 Pixi `Container` 中, 这样便能在其中添加多个 Pixi 对象, 也能设置它们的位置偏移、旋转角度、尺寸比例等参数, 而不会受到 `Spine` 自动管理机制的限制.

如果要删除容器, 请调用该方法:

```
removeSlotObject (slotRef: number | string | Slot, pixiObject?: Container): void
```

其中 `pixiObject` 是可选参数. 若传入该参数, 仅移除内含了被 `slotRef` 引用的槽位中的 Pixi 对象.

要注意这里的 Pixi 对象只是被移除, 而没有被销毁. 这种手动添加的 Pixi 对象的生命周期有赖于你的主动管理.

如果要移除全部容器, 可以调用:

```
removeSlotObjects (): void
```

若需列出添加到槽位的 Pixi 对象, 请使用该方法:

```
getSlotObject (slotRef: number | string | Slot): Container | undefined
```

如果存在对象, 则该方法会返回添加到被 `slotRef` 引用的槽位上的 `Container`.

完整的示例代码请参见 [`slot-objects.html`](/git/spine-runtimes/spine-ts/spine-pixi-v8/example/slot-objects.html).

## 网格的批处理尺寸

在 v7 中, `Spine` 对象使用 Pixi 网格来渲染附件. 同时 Pixi 会默认将顶点数多于 100 的网格标记为不可合批(batchable). 这将导致合批中断, 使绘制调用增加并降低性能表现. 为绕过这一限制, 建议将全局的 `PIXI.Mesh.BATCHABLE_SIZE` 设置成适配 skeleton 的值.

而 spine-pixi-v8 没有这种限制.

# 访问Spine运行时API

spine-pixi-v8 API 基于 [spine-ts core](https://github.com/EsotericSoftware/spine-runtimes/tree/4.2/spine-ts/spine-core), 因此你可以使用它提供的所有 API. 更多详情请参阅 [Spine 运行时指南](/spine-runtimes-guide).
