# spine-canvaskitランタイム　ドキュメント

> **ライセンスについて**
>
> Spineランタイムをアプリケーションに組み込む前に、必ず[Spine Runtimes License](/spine-runtimes-license)を確認してください。

# はじめに
spine-canvaskitはTypeScriptベースのランタイムで、ブラウザやNode.js環境で[CanvasKit](https://skia.org/docs/user/modules/canvaskit/)を使ってSpineのスケルトンをロード、操作、レンダリングします。spine-canvaskitはフロントエンド(UI要素のレンダリング)とバックエンド(ヘッドレスでスケルトンをレンダリング)の両方で使用できます。

spine-canvaskitはCanvasKit +0.39.1 を必要とし、[2色ティント](/spine-slots#ティントブラック)を除くすべてのSpine機能をサポートしています。

spine-canvaskitはSpineランタイム コアAPIのTypeScript実装である spine-core 上に構築されています。コアAPIの詳細については[Spineランタイムガイド](/spine-runtimes-guide)を参照してください。

## インストール方法
> **注意:** spine-canvaskitの `major.minor` バージョンがエクスポートを行ったSpineエディターの `major.minor` バージョンと一致していることを確認してください。詳しくは「[Spineエディターとランタイムのバージョン管理に関するガイド](/spine-runtime-architecture#バージョン)」を参照してください。

### NPM または Yarn の場合
spine-canvaskitはNPMまたはYarn経由でプロジェクトに追加できます:

```
npm install @esotericsoftware/spine-canvaskit@^4.2.0
yarn add @esotericsoftware/spine-canvaskit@^4.2.0
```

spine-canvaskitは[ECMAScriptモジュール](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules)で、Node.jsやすべてのモダンブラウザでネイティブに使用したり、webpack、rollup、esbuild などのツールでバンドルしたりできます。また、デバッグしやすいようにソースマップも含まれています。

> **補足:** spine-canvaskitモジュールからクラス、列挙型(enum)、または関数にアクセスするには、`import { loadTextureAtlas } from "@esotericsoftware/spine-canvaskit"` のように単純にそれらをインポートします。

### バニラJavaScriptの場合
バニラJavaScript(カスタマイズされていないJavaScript)プロジェクトでは、`script`タグを使って[unpkg CDN](https://unpkg.com/)からspine-canvasを追加できます：

```
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
<script src="https://unpkg.com/@esotericsoftware/spine-canvaskit@4.2.*/dist/iife/spine-canvaskit.js"></script>
```

ソースマップを有効にすると、ランタイムのオリジナルのTypeScriptソースコードをデバッグできます。

また、spine-canvaskitのminified(縮小)バージョンも提供しており、unkgのURLで接尾辞 `.js` を `min.js` に置き換えることで使用できます。

```
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
<script src="https://unpkg.com/@esotericsoftware/spine-canvaskit@4.2.*/dist/iife/spine-canvaskit.js"></script>
```

> **注意:** バニラJavaScriptプロジェクトにspine-canvaskitをインクルードする場合、すべてのクラス、列挙型、および関数にグローバルspineオブジェクト (`spine.loadTextureAtlas()` や `spine.SkeletonData` など) を介してアクセスする必要があります。以下のコードサンプルではspineオブジェクトは省略されているので注意してください。

### サンプル
spine-canvaskitランタイムには、利用できる機能セットを実演しているサンプルがいくつか含まれています。

サンプルを実行するには:
1. [Node.js](https://nodejs.org/en)をインストールします
2. [spine-runtimesリポジトリ](https://github.com/esotericsoftware/spine-runtimes)をクローンします
3. ターミナルで以下を実行します:
```
cd path/to/spine-runtimes/spine-ts
npm run dev
```

するとブラウザウィンドウが開き、すべてのspine-tsランタイムの例が表示されます。spine-canvaskit関連のサンプルは `CanvasKit` の見出しの下にあります。

以下のサンプルが含まれています:
* [`spine-canvaskit/example/headless.js`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/headless.js): Node.jsコマンドラインアプリで、[Spineboy](/spine-examples-spineboy)のスケルトンとアトラスをロードし、`portal` アニメーションをアニメーションPNGファイルにレンダリングします。`npm run dev` を起動したら、ターミナルで `node spine-canvaskit/example/headless.js` を実行すると、アニメーションを含む `output.png` というファイルが生成されます。
* [`spine-canvaskit/example/index.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/index.html): ブラウザでCanvasKitを介したSpineスケルトンのロードとレンダリングを実演するウェブアプリです。
* [`spine-canvaskit/example/animation-state-events.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/animation-state-events.html): アニメーションステートイベントのリスナーの設定を実演するウェブアプリです。
* [`spine-canvaskit/example/mix-and-match.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/mix-and-match.html): 複数スキンを組み合わせる方法を実演するウェブアプリです。

## spine-canvaskitランタイムの更新
プロジェクトの spine-canvaskit ランタイムを更新する前に、[Spineエディターとランタイムのバージョン管理に関するガイド](/spine-runtime-architecture#バージョン)を参照してください。

spine-canvaskitランタイムの更新は、`package.json` ファイル内の `spine-canvaskit` パッケージのバージョン文字列を変更して、再度 `npm install` を実行するだけでできます。バニラJavaScriptの場合は、scriptタグ内のunpkg URLを更新してください。

> **注意:** `spine-canvaskit` パッケージの `major.minor` バージョンを変える場合は、必ずSpineスケルトンも同じ`major.minor`バージョンのSpineエディターで再エクスポートする必要はあります！

# spine-canvaskitを使用する

## アセットのマネージメント
### spine-canvaskit用にエクスポートする
![](/img/spine-runtimes-guide/spine-ue4/export.png)
以下の実行方法については、Spineユーザーガイド内で紹介されています :

1. [スケルトン＆アニメーションデータのエクスポート](/spine-export)
2. [スケルトンの画像を含むテクスチャアトラスのエクスポート](/spine-texture-packer)

> **注意:** spine-canvaskitはアトラスイメージに自動的に乗算済みアルファ(premultiplied alpha)を適用します。そのため、アトラスをエクスポートするときは乗算済みアルファを使用しないでください!

スケルトンのデータとテクスチャアトラスをエクスポートすると、以下のファイルが得られます：

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

1. `skeleton-name.json` または `skeleton-name.skel`:　これはスケルトンとアニメーションのデータを含んでいます。
2. `skeleton-name.atlas`:　これはテクスチャアトラスの情報を含んでいます。
3. １つまたは複数の `.png` ファイル:　これはテクスチャアトラスの各ページで、スケルトンが使用するイメージを含んでいます。

> **補足**: JSONエクスポートよりもバイナリ形式でのスケルトンエクスポートの方がサイズが小さく、読み込みが速いので、基本的にはそちらを選択したほうが良いでしょう。

これらはアプリに同梱することになるファイルです。

### Spineアセットの更新
開発中にスケルトンデータやテクスチャアトラスファイルを更新したい時は、単純にSpineエディターから再エクスポートを行なって、これらのソースファイル(`.json`, `.skel`, `.atlas`, `.png`)を置き換えるだけで簡単に更新できます。

その際、spine-canvaskitの `major.minor` バージョンが、エクスポートを行ったSpineエディターの`major.minor`が一致していることを確認してください。詳しくは「[バージョンの同期](/spine-versioning#バージョンの同期)」を参照してください。

## CanvasKitの初期化
spine-canvaskitはSpineスケルトンの読み込みとレンダリングをCanvasKitに依存しています。spine-canvaskitを使用する前に、CanvasKitを初期化する必要があります。

NodeJSまたはES6対応のブラウザプロジェクトする場合：

```
import CanvasKitInit from "canvaskit-wasm";

const ck = await CanvasKitInit();
```

ブラウザのscriptタグでバニラJavaScriptを使用する場合：
```
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
<script type="module">
const ck = await CanvasKitInit();
</script>

```

以降のコード例では、`ck` が初期化された `CanvasKit` オブジェクトへの参照を保持していると仮定します。

## コアクラス
spine-canvaskit APIはTypeScriptベースの汎用[spine-core](/git/spine-runtimes/spine-ts/spine-core)ランタイムの上に構築されており、プラットフォームに依存しないコアクラスとSpineスケルトンのロード、クエリ、変更、アニメーションを行うアルゴリズムを提供します。コアクラスもspine-canvaskitの一部です。

ここでは、pine-canvaskitを使用する際によく見ることになる最も重要なコアクラスについてのみ簡単に説明しています。Spineランタイムのアーキテクチャ、コアクラス、APIの使用法の詳細については、[Spineランタイムガイド](/spine-runtimes-guide)を参照してください。

[`Atlas`](/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` ファイルからロードされたデータを保管します。このスケルトンデータには、ボーン階層、スロット、アタッチメント、コンストレイント、スキン、アニメーションに関する情報が含まれます。 `SkeletonData` インスタンスは、通常、それが表すスケルトンで使用されるイメージをソースとする `TextureAtlas` も一緒に提供することによってロードされます。これは、`Skeleton` インスタンスを作成するための設計図として機能します。複数のスケルトンを同じアトラスとスケルトンデータからインスタンス化し、ロードされたデータを共有することで、ロード時間と実行時のメモリ消費を最小限に抑えることができます。

[`Skeleton`](/git/spine-runtimes/spine-ts/spine-core/src/Skeleton.ts) クラスは、`SkeletonData` インスタンスから作成されたスケルトンのインスタンスを格納します。スケルトンは現在のポーズを保管します。つまり、ボーンの位置、スロット、アタッチメント、アクティブなスキンの現在の構成を保管します。現在のポーズは、手動でボーンのトランスフォームを変更するか、より一般的には、`AnimationState` を介してアニメーションを適用することで計算されます。

[`AnimationState`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts) クラスは、 スケルトンに適用する(単数または複数の)アニメーションを追跡し、最後のレンダリングフレームと現在のレンダリングフレームの間の経過時間に基づいてそれらのアニメーションを進め、ミックスを行い、スケルトンインスタンスにアニメーションを適用して現在のポーズを設定します。`AnimationState` は [`AnimationStateData`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationStateData.ts) インスタンスに問い合わせ(処理要求)をして、アニメーション間のミキシング時間を取得します。特定のアニメーション間に使用するミキシング時間が無ければデフォルトミックスタイムを取得します。

spine-canvaskit は spine-core の上に機能を追加することで、Spineスケルトンの読み込み、変更、レンダリングを簡単にしています。

## アセットのロード
Spineアトラスおよびスケルトンデータファイルは、それぞれ `loadTextureAtlas()` 関数および`loadSkeletonData()` 関数でロードできます。

spine-canvaskitは、NodeJSやブラウザなど、CanvasKitが利用可能なあらゆるJavaScript環境で動作します。そのため、ローダー関数はプラットフォームに依存せず、絶対パスまたは相対パスを受け取り、ファイルの生(raw)のバイナリコンテンツを含む `Buffer`（NodeJS）または`ArrayBuffer`（ブラウザ）を返す関数を提供する必要があります。

この関数の `TypeScript` シグネチャは以下のとおりです：

```
readFile(path: string): Promise<any>
```

NodeJSの場合、この関数は以下のように実装できます：

```
import * as fs from "fs"

async function readFile(path) {
   return fs.readFileSync(path)
}
```

ブラウザ環境では、この関数は次のように実装できます：

```
async function readFile(path) {
   const response = await fetch(path);
   if (!response.ok) throw new Error("Could not load file " + path);
   return await response.arrayBuffer();
}
```

`loadTextureAtlas()` 関数を使用して、`.atlas` ファイルとその `.png` ページ画像ファイルを読み込みます：

```
const atlas = await loadTextureAtlas(ck, "myatlas.atlas", readFile);
```

アトラスの `.png` ファイルは、`.atlas` ファイルが存在するディレクトリに対して相対的に解決されます。

同様に、`loadSkeletonData()` 関数を使用して、スケルトンデータの `.json` または `.skel` ファイルをロードします：

```
const skeletonData = await loadSkeletonData("myskeleton.skel", atlas, readFile);
```

`loadSkeletonData()` は、ファイル名のそれぞれの拡張子に基づいて、`.json` ファイルと `.skel` ファイルの両方をロードできます。`atlas` は、スケルトンデータから派生したスケルトンをレンダリングするために必要な画像を取得するために使用されます。

## SkeletonDrawable
`SkeletonDrawable` は、スケルトンの現在のポーズとスキンを保存する `Skeleton` と、アニメーションの追跡と適用を行う `AnimationState` をカプセル化します。

スケルトンのアトラスとスケルトンデータファイルをロードしたら、そこから1つ以上の`SkeletonDrawable` インスタンスを作成できます。`SkeletonData` は暗黙的に`TextureAtlas` を参照していることに注意してください。

`SkeletonDrawable` コンストラクタを呼び出して、新しいインスタンスを作成します：

```
const drawable = new SkeletonDrawable(skeletonData);
```

`SkeletonDrawable` 内の `Skeleton` と `AnimationState` には、それぞれのフィールドからアクセスできます：

```
// スケルトンの位置とスケール
const skeleton = drawable.skeleton
skeleton.x = 300;
skeleton.y = 380;
skeleton.scaleX = skeleton.scaleY = 0.5;

// AnimationStateにアニメーションをセットする
const animationState = drawable.animationState;
animationState.setAnimation(0, "walk", true);
```

`Skeleton` および `AnimationState` APIの包括的な説明については、[Spineランタイムガイド](/spine-runtimes-guide)を参照してください。以下に、API の最低限の使用例を示します。

## アニメーションの適用
> ** 補足:** より詳しい情報についてはSpineランタイムガイドの [アニメーションの適用](/spine-applying-animations)セクションを参照してください。

`AnimationState` を使用すると、複数のトラックに1つ以上のアニメーションをキューに入れることができます。トラックはインデックス0から始まります。上位トラックのアニメーションは、下位トラックのアニメーションでキーになっているプロパティを上書きします。このトラックの概念により、複数のアニメーションを一度に再生したり、ミックスしたりすることができます。

トラック0に特定のアニメーションを設定するには、`AnimationState.setAnimation()` を呼び出します：

```
drawable.animationState.setAnimation(0, "walk", true);
```

最初のパラメーターはトラック、2番目のパラメータはアニメーションの名前、3番目のパラメータはアニメーションをループさせるかどうかを指定します。

複数のアニメーションをキューに入れることもできます:

```
const animationState = drawable.animationState;
animationState.setAnimation(0, "walk", true);
animationState.addAnimation(0, "jump", false, 2);
animationState.addAnimation(0, "run", true, 0);
```

`addAnimationByName()` の最初のパラメーターはトラックです。2番目のパラメーターはアニメーションの名前です。3番目のパラメーターはアニメーションをループさせるかどうかを指定します。最後のパラメーターは、このアニメーションがトラック上の前のアニメーションと置き換わるまでの時間(ディレイ)を秒単位で指定します。

上の例では、まず `"walk"` アニメーションが再生されます。その2秒後に `"jump"` アニメーションが一度再生され、続いて `"run"` アニメーションに移行し、ループします。

あるアニメーションから別のアニメーションに遷移するとき、`AnimationState` は指定された時間の間、アニメーションをミックスします。これらのミックスタイムは `AnimationStateData` インスタンスで定義され、`AnimationState` はそこからミックスタイムを取得します。

`AnimationStateData` インスタンスは、AnimationStateからも利用できます。デフォルトのミックスタイムや、特定のアニメーションのペアのミックスタイムを設定できます：

```
animationState.data.defaultMix = 0.2;
animationState.data.setMix("walk", "jump", 0.1);
```

アニメーションを設定または追加すると、`TrackEntry` オブジェクトが返されます。これを利用してアニメーションの再生をさらに変更できます。例えば、アニメーションを逆再生するようにTrackEntryを設定することができます:

```
const entry = drawable.animationState.setAnimation(0, "walk", true);
entry.reverse = true;
```

利用できるオプションについて詳しくは [`TrackEntry` クラスのドキュメント](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L798)をご覧ください。

> **注意:** `TrackEntry` インスタンスを使用している関数の外部で保持しないでください。TrackEntryは内部で再利用されるため、それが表すアニメーションが完了すると無効になります。

スケルトンをスムーズにセットアップポーズに戻したい場合は、アニメーショントラックに空のアニメーションをセットまたはキューに追加します:

```dart
controller.animationState.setEmptyAnimation(0, 0.5);
controller.animationState.addEmptyAnimation(0, 0.5, 0.5);
```

`setEmptyAnimation()` の最初のパラメーターはトラックを指定します。2番目のパラメーターは、前のアニメーションをミックスアウトし、"空の"アニメーションをミックスするために使用するミックスタイムを秒単位で指定します。

`addEmptyAnimation()` の最初のパラメーターはトラックを指定します。2番目のパラメーターはミックス時間を指定します。3番目のパラメーターはディレイ(秒単位)で、このディレイの後に空のアニメーションがミキシングされてトラック上の前のアニメーションと置き換わります。

`AnimationState.clearTrack()` を使えばトラック上のすべてのアニメーションを即座にクリアすることができます。すべてのトラックを一度にクリアするには `AnimationState.clearTracks()` を使います。しかしこれはスケルトンを最後に適用されたポーズのままにする点に注意してください。

スケルトンのポーズをセットアップポーズに戻すには、`Skeleton.setupPose()` を使います:

```
drawable.skeleton.setupPose();
```

これはボーンとスロットの両方をセットアップポーズの設定にリセットします。スロットだけをセットアップポーズの設定にリセットしたい場合は `Skeleton.setupPoseSlots()` を使用してください。

## AnimationStateイベント
`AnimationState` は、再生中のアニメーションのライフサイクル中にイベントを発行します。必要に応じてこのイベントをリッスンすることで、それらに反応させることができます。SpineランタイムのAPI では、以下の[イベントタイプ](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L1095)を定義しています：

* `Start`: アニメーションが開始された時に発されます。
* `Interrupted`: アニメーションのトラックがクリアされた、または新しいアニメーションが設定されたなどにより中断された時に発されます。
* `Completed`: アニメーションが1ループを完了するごとに発されます。
* `Ended`: アニメーションが二度と適用されない時に発されます。
* `Disposed`: アニメーションのTrackEntryが破棄された時に発されます。
* `Event`: ユーザーが定義した[イベント](/spine-events)が発生した時に発されます。

イベントを受け取るには、[`AnimationStateListener`](/git/spine-runtimes/spine-ts/spine-core/src/AnimationState.ts#L1161) コールバックを、すべてのアニメーションでイベントを受信する `AnimationState` か、キューされた特定のアニメーションの `TrackEntry` に登録します:

```
const entry = drawable.animationState.setAnimation(0, "walk", true);
entry.listener = {
   event: (entry, event) => console.log(`User defined event: ${event.data.name}`),
   complete: (entry) => console.log(`Animation loop completed.`)
}

drawable.animationState.setListener({
   end: (entry) => console.log(`Animation ${entry.data.name} has ended and will not be applied again.`
});
```

詳しくは [`example/animation-state-events.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/animation-state-events.html) をご覧ください。

## スキン
多くのアプリケーションやゲームでは、髪や目、ズボン、イヤリングやバッグなどのアクセサリーなど、さまざまなアイテムを組み合わせてカスタムアバターを作ることができます。Spineでは、[複数スキンを組み合わせる](/spine-examples-mix-and-match)ことでこれを実現することができます。

以下のようにして、他のスキンからカスタムスキンを作成することができます：

```
// カスタム用の空のスキンを作成
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"));
skeleton.setSkin(skin);
skeleton.setupPoseSlots();
```

まず、コンストラクタ `Skin()` でカスタムスキンを作成します。

次に、スケルトンから `SkeletonData` を取得します。これは `SkeletonData.findSkin()` でスキンを名前から探すのに使用します。

`Skin.addSkin()` で新しいカスタムスキンにまとめたいスキンをすべて追加します。

最後に、`Skeleton.setSkin()`で出来上がった新しいスキンを `Skeleton` にセットし、`Skeleton.setupPoseSlots()` を呼び出して、以前のスキンやアニメーションのアタッチメントが残らないようにします。

詳しくは [`example/mix-and-match.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/mix-and-match.html) をご覧ください。

## ボーンのトランスフォームの設定
Spineエディターでスケルトンを構築する際、スケルトンは「スケルトン座標系」と呼ばれるもので定義されます。そのため、キャンバスに対するタッチ座標やマウス座標をボーンの座標系に変換するには、`Bone.worldToLocal()` メソッドを使用します。

これは、ユーザー入力に基づいてボーンの位置を駆動する場合に便利です。

詳しくは [`example/ik-following.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/ik-following.html) をご覧ください。


## パフォーマンス
spine-canvaskitは `CavansKit.MakeVertices()` と `Canvas.drawVertices()` を使用して、個々のスケルトンアタッチメントのメッシュを描画します。Skiaはこれらのメッシュをバックグラウンドでバッチ処理しているように見えますが、spine-canvaskit自体でアタッチメントのメッシュのバッチ処理を行うことで、さらなるパフォーマンスの向上が期待できます。

詳しくは [`example/micro-benchmark.html`](/git/spine-runtimes/spine-ts/spine-canvaskit/example/micro-benchmark.html) をご覧ください。