Spine iOSランタイム ドキュメント

ライセンスについて

Spineランタイムをアプリケーションに組み込む前に、必ずSpine Runtimes Licenseを確認してください。

はじめに

Spine iOSランタイムはspine-cppの薄いラッパーを使って実装されています。UIKitSwiftUI の両方を使って実行できます。SwiftObjective-C の両方がサポートされています。

レンダリングにはMetalを使用しており、ティントブラックを除くすべてのSpine機能をサポートしています。

インストール方法

Spine iOS は iOS 13 以降でサポートされています。プロジェクトでSpine iOSを使用するには、Swift Package ManagerまたはCocoaPodsを使用してインストールすることができます。

spine-runtimesリポジトリのブランチの major.minor バージョンが、エクスポートする Spineエディターの major.minor バージョンと一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。

Swift Package Manager

プロジェクトにSpine iOS SPMパッケージを追加するには:

  1. Xcodeでご自身のプロジェクトを開いてください。
  2. Project Settingsに移動します: Project Navigatorでプロジェクトをクリックし、ターゲットの右上にあるプロジェクトを選択します。
  3. Swift Packagesパッケージを選択します: Package Dependencies タブをクリックします。
  4. パッケージを追加: + をクリックして検索欄に git@github.com:EsotericSoftware/spine-runtimes.git を入力します。
  5. 依存関係のルール: Commit を選択し、サポートしたい major.minor ブランチの最新の完全なコミットハッシュを入力します。
  6. 終了: Next をクリックし、Xcodeがパッケージを解決した後、Finish をクリックします。

また、C++サポートを有効にする必要があります:

  1. Project Settingsに移動します: アプリのメインターゲットをクリックします。
  2. Built Settings タブを開きます: C++ and Objective-C Interoperability を探します。
  3. C++ / Objective-C++ を選択します。

CocoaPods

バージョン管理方法の性質上、Spine iOSは公開されているCocoaPodsインデックスには含まれていません。ご自身のPodfile に追加するには、以下のように .podspec ファイルへリンクさせる必要があります。その際、正しいmajor.minorを使用してください。

python
# 次の行のコメントを外して、プロジェクトのグローバル・プラットフォームを定義します。
platform :ios, '13.0'

target 'Spine iOS Example' do
# ダイナミック・フレームワークを使いたくない場合は、次の行をコメントします。
use_frameworks!

pod 'Spine', :podspec => 'https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/4.2/Spine.podspec'
pod 'SpineCppLite', :podspec => 'https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/4.2/SpineCppLite.podspec'
pod 'SpineShadersStructs', :podspec => 'https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/4.2/SpineShadersStructs.podspec'
end

使用方法

どちらの場合も、Swiftファイルで Spine をインポートすることができます:

Swift
import Spine

サンプル

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

サンプルプロジェクトを実行するには:

  1. お使いのMacにXcodeをインストールします
  2. spine-runtimesリポジトリをクローンします: git clone https://github.com/esotericsoftware/spine-runtimes
  3. Xcodeで spine-runtimes/spine-ios/Example/Spine iOS Example.xcodeproj を開きます

代わりに、spine-runtimes/spine-ios/Example - Cocoapods/Spine iOS Example.xcodeproj を開いて CocoaPods を使用してSpine iOSがどのようにインポートされるかを確認することもできます。

以下のすべてのサンプルは SwiftUI のプレビューをサポートしており、Xcode上でキャンバスにレンダリングすることができます。

サンプルプロジェクトには以下のサンプルが含まれています:

  • SimpleAnimation.swift: エクスポートしたSpineスケルトンを読み込み、ビューに表示し、特定のアニメーションを再生する SpineViewSpineController の基本的な使い方を実演しています。
  • PlayPauseAnimation.swift: アニメーションを一時停止して再開する方法を実演しています。
  • AnimationStateEvents.swift: スロットの色を設定する方法、複数のアニメーションをキューに入れる方法、AnimationStateイベントをリッスンする方法を実演しています。
  • DebugRendering.swift: SpineControlleronAfterPaint コールバックを介して、レンダリングされたスケルトンの上にカスタム描画を実行する方法を実演しています。
  • DressUp.swift: Spineのスキン機能と、キャラクター作成UIで使用するためにスケルトンをサムネイルにレンダリングする方法を実演しています。
  • IKFollowing.swift: スケルトンのボーンをタッチ入力でドラッグする方法を実演しています。
  • DisableRendering.swift: SpineView が画面外に移動したときにレンダリングを無効にする方法を実演しています。これはCPU/GPUリソースを維持する必要がある場合に重要です。
  • SimpleAnimationViewController.m: UIKitObjective-CSpine iOS を使用する方法を示します。

Spine iOSランタイムのアップデート

プロジェクトのSine iOSランタイムをアップデートする前に、Spineエディターとランタイムのバージョン管理に関するガイドを参照してください。

CocoaPodsについては、正しい major.minor ブランチにリンクしてください。

SPMの場合は、正しい major.minor ブランチから正しいコミットハッシュを選択してください。

注意: Spine iOS パッケージの major.minor バージョンを変更した場合、使用しているSpineスケルトンを同じ major.minor バージョンのSpineエディターを使って再エクスポートする必要があることに注意してください!

Spine iOSを使用する

Spine iOS ランタイムは、Spineで作成されたアニメーションのロード、再生、操作をサポートする汎用spine-cppの慣用的なラッパーです。Spine iOSランタイムは、spine-cpp API のほとんどすべてを慣用的なSwiftとして公開し、Spineスケルトンを簡単に表示、操作するためのSwiftUIとUIKit固有のクラスを提供します。

Spine iOSランタイムは、ティントブラックを除くすべてのSpineの機能をサポートしています。

アセットのマネージメント

Spine iOS用にエクスポートする

以下の実行方法については、Spineユーザーガイド内で紹介されています :

  1. スケルトン&アニメーションデータのエクスポート
  2. スケルトンの画像を含むテクスチャアトラスのエクスポート

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

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

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

これらのファイルを、AtlasSkeletonDataSkeletonDrawableSpineViewなどのSpine iOSクラスを使って読み込むことができます。

注意: 乗算済みアルファではないアセットを使用する場合は、アプリケーションターゲットのビルド設定で、Compress PNG FilesRemove Text Metadata From PNG Files を無効にする必要があるので注意してください。

Spineアセットの更新

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

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

コアクラス

Spine iOSのAPIは汎用spine-cppランタイムの上に構築されており、プラットフォームに依存しないコアクラスとSpineスケルトンのロード、クエリ、変更、アニメーションを行うアルゴリズムを提供します。コアクラスは、Swiftのイディオムクラスとして公開されます。

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

Atlas クラスは、.atlas ファイルとそれに対応する .png 画像ファイルからロードしたデータを保管します。

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

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

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

Spine iOSランタイムはこれらのコアクラスの上に構築されています。

SpineView / SpineUIView

/img/spine-runtimes-guide/spine-ios/simple-animation.png

SpineView struct は SpineUIView の周りの UIViewRepresentable なので、後者はSwiftUI プロジェクトで使用できます。SpineUIViewMTKViewのサブクラスです。

以降、どちらかを参照するときは SpineView を使用します。

SpineView は Spineのスケルトンをロードして表示する役割を果たします。最低限、ビューはスケルトンとアトラスファイルをどこからロードするかを知る必要があり、アニメーションの設定やスケルトンのスキンの変更など、ウィジェットの状態を変更する役割を持つ SpineController インスタンスを受け取ることができます。

SpineControllerObservableObject で、@State 変数内に保持されます。最も単純なケースでは、SpineView は以下のように他のビュー body の中でインスタンス化できます:

Swift
@StateObject
var controller = SpineController(
   onInitialized: { controller in
      controller.animationState.setAnimationByName(
         trackIndex: 0,
         animationName: "walk",
         loop: true
      )
   }
)

var body: some View {
   SpineView(
      from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
      controller: controller,
      mode: .fit,
      alignment: .center
   )
}

インスタンス化すると、SpineView は指定されたファイルを非同期でロードし、そこから基礎となるコアクラスのインスタンス、つまり AtlasSkeletonDataSkeletonAnimationStateDataAnimationState のインスタンスを構築します。

ロードが完了すると、SpineControlleronInitialized が呼び出され、1つ以上のアニメーションの設定、ボーン階層の操作、スケルトンのスキンの変更など、ウィジェットの状態を変更できるようになります。以下の SpineController のセクションを参照してください。

SpineView クラスは、異なるソースからスケルトンとアトラスファイルをロードするために、最初のパラメーターとして SpineViewSource 列挙型(enum)を取ります:

  • SpineViewSource.bundle は、メインバンドルまたは指定されたバンドルからファイルをロードします。
  • SpineViewSource.file は、ファイルシステムからファイルをロードします。
  • SpineViewSource.http は URL からファイルをロードします。
  • SpineViewSource.drawable() は、SkeletonDrawable からビューを構築します。これは、スケルトンデータをプリロード、キャッシュ、および/または SpineView インスタンス間で共有する場合に便利です。以下の「スケルトンデータのプリロードと共有」のセクションを参照してください。

さらに、SpineView にはオプションの引数があり、Spineのスケルトンをビュー内でどのようにフィットさせ、どのように整列させるか、また、ビューのサイズをどのように設定するかを定義することができます。

  • mode : SpineUIView 内でのスケルトンのフィット方法。デフォルトは .fit です。
  • alignment : SpineUIView内でスケルトンをどのように整列させるかを指定します。デフォルトは .center です。
  • boundsProvider : これはフィットと整列を計算するときにスケルトンに使用されるバウンディングボックスのピクセルサイズを計算するために使用されます。デフォルトでは、スケルトンのセットアップポーズのバウンディングボックスが使用されます。詳細は、SetupPoseBoundsRawBoundsSkinAndAnimationBoundsのクラスドキュメントを参照してください。
  • backgroundColor: ビューの背景色。デフォルトでは UIColor.clear が使用されます。

SpineView には、レンダリングを無効にするオプションのバインディングパラメータisRendering があります。詳細については、DisableRendering.swift の例を参照してください。

SpineController

SpineController は、SpineView のスケルトンのアニメーションを制御します。コントローラーは、コンストラクタの引数としてオプションのコールバックセットを提供します。これは SpineView のライフタイム中に特定のタイミングで呼び出されます。

コントローラは、AtlasSkeletonDataSkeletonAnimationState などの Spineランタイム API オブジェクトを返すゲッターを通して、スケルトンの状態を公開します。詳細については、Spineランタイムガイドおよびクラスのドキュメントを参照してください。

SpineView を初期化すると、コントローラーの onInitialized() コールバックメソッドが呼び出されます。このメソッドを使用して、再生するアニメーションを設定したり、スケルトンのスキンを設定したりすることができます。

初期化が完了すると、SpineView は画面のリフレッシュレートで連続的にレンダリングされます。各フレームで AnimationState が現在キューに入っているアニメーションに基づいて更新され、Skeleton に適用されます。

次に、オプションの onBeforeUpdateWorldTransforms() コールバックが呼び出され、Skeleton.updateWorldTransform() を使って現在のポーズが計算される前にスケルトンを変更することができます。

現在のポーズが計算された後、オプションの onAfterUpdateWorldTransforms() コールバックが呼び出され、スケルトンがレンダリングされる前に現在のポーズをさらに修正することができます。手動でボーンの位置を変更したいならばここが適切です。

SpineView によってスケルトンがレンダリングされる前に、オプションの onBeforePaint() コールバックが呼び出されます。これにより、ビュー階層でスケルトンの後ろにあるべき背景やその他のオブジェクトをレンダリングすることができます。

SpineWidget が現在のスケルトンのポーズをレンダリングした後、オプションの onAfterPaint() コールバックが呼び出されます。

デフォルトでは、ビューはスケルトンを毎フレーム更新してレンダリングします。SpineController.pause() メソッドを使用すると、スケルトンの更新とレンダリングを一時停止できます。SpineController.resume() メソッドで、スケルトンの更新とレンダリングを再開できます。SpineController.isPlaying プロパティは、現在の再生状態を報告します。詳しくはサンプル AnimationStateEvents.swift をご覧ください。

SkeletonDrawableWrapper / SkeletonDrawable

SkeletonDrawableWrapperSkeletonDrawable を保持し、 Skeleton とそれに関連する AnimationState の読み込み、保存、更新、レンダリングを一つの使いやすいクラスにまとめます。SpineViewSkeletonDrawableWrapper のインスタンスを介して、表示するスケルトンの状態をカプセル化します。

ファイルアセットから SkeletonDrawableWrapper を作成するには、fromBundle()fromFile()fromHttp() メソッドを使用します。複数の SkeletonDrawableWrapper インスタンス間で AtlasSkeletonData を共有するには、同じアトラスとスケルトンデータを各インスタンスに渡し、コンストラクタでドローアブルをインスタンス化します。

SkeletonDrawableWrapperSkeletonDrawableSkeletonAnimationStateAnimationStateWrapper を公開して、スケルトンのクエリ、変更、アニメーションを行います。また、スケルトンとアニメーション状態を構築するための AtlasSkeletonData も公開しています。

スケルトンをアニメーションさせるには、AnimationState.setAnimation()AnimationState.addAnimation() などの AnimationState API を使って、1つ以上のトラックにアニメーションをキューします。

アニメーションの状態を更新し、スケルトンに適用し、現在のスケルトンのポーズを更新するには、SkeletonDrawableWrapper.update() メソッドを呼び、アニメーションを進めるためのデルタ時間(秒単位)を与えます。

Skeleton の現在のポーズを CGImage としてレンダリングするには、SkeletonDrawableWrapper/renderToImage(size:backgroundColor:scaleFactor:) を使います。

SkeletonDrawable はネイティブヒープに割り当てられたオブジェクトを保存します。そのため、SkeletonDrawable が不要になったら、SkeletonDrawable.dispose() を呼び出して、ネイティブオブジェクトを手動で破棄する必要があります。そうしないと、ネイティブのメモリリークになってしまうので注意してください。

注意: SpineController は初期化解除時に自動的にこの処理を行います。しかし、SkeletonDrawableWrapperSpineController の外部で保持する場合は、上記のようにディスポーズする必要があります。この場合、SpineControllerのコンストラクタのオプションのdisposeDrawableOnDeInit パラメーターを false に設定してください。

アニメーションの適用

SpineView で表示されるスケルトンにアニメーションを適用するには、SpineController の コールバックで AnimationState を使用します。

注意: アニメーショントラックやアニメーションのキューイングなど、より詳しい情報については、Spineランタイムガイドのアニメーションの適用を参照してください。

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

Swift
@StateObject
var controller = SpineController(
   onInitialized: { controller in
       // トラック0にwalkアニメーションをセットし、それをループさせます
      controller.animationState.setAnimationByName(
         trackIndex: 0,
         animationName: "walk",
         loop: true
      )
   }
)

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

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

Swift
controller.animationState.setAnimationByName(trackIndex: 0, animationName: "walk", loop: true)
controller.animationState.addAnimationByName(trackIndex: 0, animationName: "jump", loop: false, delay: 2)
controller.animationState.addAnimationByName(trackIndex: 0, animationName: "run", loop: true, delay: 0)

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

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

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

AnimationStateData インスタンスはコントローラーを介して利用することもできます。デフォルトのミックスタイムや、特定のアニメーションのペアのミックスタイムを設定することができます:

Swift
controller.animationStateData.defaultMix = 0.2
controller.animationStateData.setMixByName(fromName: "walk", toName: "jump", duration: 0.1)

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

Swift
var entry = controller.animationState.setAnimationByName(trackIndex: 0, animationName: "walk", loop: true)
entry.reverse = true

利用できるオプションについて詳しくは TrackEntry クラスのドキュメントをご覧ください。

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

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

Swift
controller.animationState.setEmptyAnimation(trackIndex: 0, mixDuration: 0.5)
controller.animationState.addEmptyAnimation(trackIndex: 0, mixDuration: 0.5, delay: 0.5)

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

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

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

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

Swift
controller.skeleton.setToSetupPose()

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

AnimationStateイベント

AnimationState は、再生中のアニメーションのライフサイクル中に様々なイベントを発行します。必要に応じてこのイベントをリッスンすることで、それらに反応させることができます。SpineランタイムのAPIでは、以下のイベントタイプを定義しています:

  • SPINE_EVENT_TYPE_START: アニメーションが開始された時に発されます。
  • SPINE_EVENT_TYPE_INTERRUPT: アニメーションのトラックがクリアされた、または新しいアニメーションが設定されたなどにより中断された時に発されます。
  • SPINE_EVENT_TYPE_COMPLETE: アニメーションが1ループを完了するごとに発されます。
  • SPINE_EVENT_TYPE_END: アニメーションが二度と適用されない時に発されます。
  • SPINE_EVENT_TYPE_DISPOSE: アニメーションのTrackEntryが破棄された時に発されます。
  • SPINE_EVENT_TYPE_EVENT: ユーザーが定義したイベントが発生した時に発されます。

イベントを受け取るには、 AnimationStateListener コールバックを AnimationStateWrapper に登録してすべてのアニメーションでイベントを受け取るか、または AnimationStateWrapper.setTrackEntryListener() で再生キューに入った特定のアニメーションの TrackEntry に登録します:

Swift
var walkEntry = controller.animationState.setAnimationByName(0, "walk", true);
controller.animationStateWrapper.setTrackEntryListener(entry: walkEntry) { type, entry, event in
   if let eventk, type == SPINE_EVENT_TYPE_EVENT {
      print("User defined event: \(event.data.name ?? "--")")
   }
}

controller.animationStateWrapper.setStateListener { type, entry, event in
   print("Animation state event \(type)")
}

詳しくは AnimationStateEvents.swift をご覧ください。

スキン

/img/spine-runtimes-guide/spine-ios/skins.png

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

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

Swift
var data = controller.skeletonData
var skeleton = controller.skeleton
var customSkin = Skin.create(name: "custom-skin")
customSkin.addSkin(other: data.findSkin(name: "skin-base")!)
customSkin.addSkin(other: data.findSkin(name: "nose/short")!)
customSkin.addSkin(other: data.findSkin(name: "eyelids/girly")!)
customSkin.addSkin(other: data.findSkin(name: "eyes/violet")!)
customSkin.addSkin(other: data.findSkin(name: "hair/brown")!)
customSkin.addSkin(other: data.findSkin(name: "clothes/hoodie-orange")!)
customSkin.addSkin(other: data.findSkin(name: "legs/pants-jeans")!)
customSkin.addSkin(other: data.findSkin(name: "accessories/bag")!)
customSkin.addSkin(other: data.findSkin(name: "accessories/hat-red-yellow")!)
skeleton.skin = customSkin
skeleton.setToSetupPose()

まず、Skin.create() static関数でカスタムスキンを作成します。

次に、コントローラーから SkeletonData を取得します。SkeletonData.findSkin() でスキンの名前を検索します。

Skin.addSkin() で、新しいカスタムスキンに組み合わせたいスキンをすべて追加します。

最後に、Skeleton に新しいスキンを設定し、Skeleton.setSlotsToSetupPose() を呼び出して、以前のスキンやアニメーションのアタッチメントが残らないようにします。

注意: Skin はC++オブジェクトをラップします。不要になったら Skin.dispose() を呼び出して手動で破棄する必要があります。

詳しくは DressUp.swift をご覧ください。これは SkeletonDrawableWrapper を使用してスキンのサムネイルプレビューをレンダリングする方法も実演しています。

ボーンのトランスフォームの設定

/img/spine-runtimes-guide/spine-ios/bone-transform.png

Spineエディターでスケルトンを構築する際、スケルトンは「スケルトン座標系」と呼ばれる座標系で定義されます。この座標系は、スケルトンがレンダリングされる SpineView の座標系と一致しない場合があります。そのため、ユーザーがタッチ操作でボーンを動かせるようにしたい場合などは、SpineView に対するタッチ座標をスケルトン座標系に変換する必要があります。

SpineController は、関連付けられた SpineView からの相対位置 CGPoint を受け取り、スケルトン座標系に変換する toSkeletonCoordinates() メソッドを提供します。

詳しくは IKFollowing.swift の例を参照してください。

また、fromSkeletonCoordinates() を使用して、別の方向に座標を変換することもできます。詳しくは DebugRendering.swift の例を参照してください。

SpineランタイムのAPI呼び出し

Spine iOSは、SpineランタイムAPIのほとんどすべてをSwiftにマッピングしています。例えば SkeletonAnimationState のように、SpineControllerSkeletonDrawableWrapper/SkeletonDrawable が返すオブジェクトはspine-cppのAPIをSwiftに1:1変換したものです。そのため、一般的なSpineランタイムガイドにある資料のほとんどすべてをSwiftコードに適用できます。

しかしながら、spine-cppブリッジの性質上、以下のような制限があります:

  • 返される配列やマップはすべて内部配列のコピーです。それらを変更しても影響はありません。
  • ボーンやスロット、その他のSpineオブジェクトを直接、作成、追加、削除することはできません。
  • タイムラインのC++クラス階層はSwiftでは公開されていません。