Marumittu Engineer Blog

個人でゲーム開発を行っています。ゲーム開発に関する情報を紹介するブログ。

UnityでSpineを使ってみた ②アニメーション切り替え

前回の記事でSpineデータをUnityに取り込む方法を紹介しました。

今回はUnityのスクリプト内でSpineのアニメーションを切り替える方法について紹介していきます。

この記事を書いた時の開発環境は以下の通りです。

 Unity 5.6.0p4
 Spine 3.5.51 Professional

SkeletonAnimation

SkeletonAnimationはSpineコンポーネントの一種です。
ケルトンデータアセット(_SkeletonData)をUnityのGame Objectとしてインスタンス化するときに自動で追加されるコンポーネントとなります。

SkeletonAnimationはSpine.AnimationStateを内包しており、
このAnimationStateを通じて、アニメーションの切り替えが行えます。

アニメーション切り替え方法

まずは単純なアニメーションの切り替えです。

public void PlayAnimation()
{
	SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
	skeletonAnimation.state.SetAnimation(0, "damage", false);
}


モーションをキューイングして再生することも可能です。
下記の指定ですと"idle"アニメーションの再生後に"damage"のアニメーションが再生されます。

public void PlayQueueAnimation()
{
	SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
	skeletonAnimation.state.SetAnimation(0, "idle", false);
	skeletonAnimation.state.AddAnimation(0, "damage", false, 0f);
}


次に各関数の引数について詳しく説明していきます。

TrackEntry SetAnimation(int trackIndex, String animationName, bool loop)
TrackEntry AddAnimation(int trackIndex, String animationName, bool loop, float delay)

Parameters
trackIndex:再生するトラックのインデックス番号
animationName:再生するアニメーションの名前
loop:ループ再生を行うかどうか
delay:再生開始までのディレイ時間

Return
TrackEntry : トラックエントリーオブジェクト

トラックとは

トラックは複数のSpineアニメーションを同時に再生するためのアニメーションレイヤーです。

トラック0に"run"アニメーション
トラック1に"shoot"アニメーションと
トラック毎に別々のアニメーションを同時に再生する事ができます。

SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.state.SetAnimation(0, "run", true);
skeletonAnimation.state.SetAnimation(1, "shoot", false);


このように走りながら撃つといった事が可能です。

f:id:marumittu_tech:20170611204007g:plain

トラックエントリーとは

SetAnimationやAddAnimationの関数を呼び出すと、
トラックエントリー(TrackEntry)のオブジェクトが返されます。
このオブジェクトはアニメーションの情報が格納されたインスタンスです。

・再生しているトラックのインデックス番号
・アニメーションの経過時間
・アニメーションの再生速度
・アニメションの開始位置
・アニメションの終了位置

等々、他にもSetAnimationやAddAnimationの引数に含まれていない再生パラメータを制御する事ができます。

SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
TrackEntry trackEntry = skeletonAnimation.state.SetAnimation(0, "run", false);
trackEntry.Time = 10f/30f;

このように、SetAnimationの直後に.Timeを変更して、開始点ではなく途中でアニメーションが開始されるようにすることができます。

trackEntry.TimeScale = 0.5f;

また、TimeScaleを設定することで再生速度を変更することができます。
SpineではSkeletonAnimation.timeScaleとAnimationState.timeScaleを乗算して最終的なタイムスケールを計算しています。

アニメーションのコールバックについて

これまでの紹介で基本的なアニメーションの切り替えはできるようになったかと思います。

次はちょっと複雑なケースです。

1."idle"アニメーションをループ再生
2.ダメージを受けたタイミングで"damage"アニメーションを単発再生
3."damage"アニメーションの再生終了後に"idle"アニメーションをループ再生


このような場合はアニメーションのコールバック機能を使用しましょう。
Spine.AnimationStateにはアニメーションのコールバックが用意されています。

  • Start:アニメーションが開始された時に呼ばれるコールバック関数
    • SetAnimationを呼び出した時
    • キューに入れられたアニメーションが開始される時
  • End:アニメーションがクリアー、もしくは中断された時に呼ばれるコールバック関数
    • 現在のアニメーションが終了する前にSetAnimationを呼び出した時
    • ClearTrackまたはClearTracksを使用してトラックをクリアする時
    • ClearTrackまたはClearTracksを使用してトラックをクリアする時
  • Dispose:アニメーションが解放された時に呼ばれるコールバック関数
    • TrackEntryを破棄した時
  • Interrupt:アニメーションが中断された時に呼ばれるコールバック関数
    • 現在のアニメーションが再生されている状態で新しいアニメーションが呼び出された時
  • Complete:アニメーションの再生が終了した時に呼ばれるコールバック関数
    • ループしないアニメーションの再生が終了したとき
    • ループアニメーションがループを終了するたびに呼び出される
  • Event:ユーザー定義のイベントが開始された時に呼ばれるコールバック関数


これらを使用してアニメーションの各タイミングで処理を実行する事ができます。
今回のケースですと、下記の記述を行う事で"idle"→"damage"→"idle"とアニメーションを遷移させる事が簡単に実現できます。

public class Enemy : MonoBehaviour 
{
	SkeletonAnimation m_skeletonAnimation = null;

	void Awake()
	{
		m_skeletonAnimation = GetComponent<SkeletonAnimation>();
	}

	void Start()
	{
		m_skeletonAnimation.state.SetAnimation(0, "idle", false);
	}

	//ダメージを受けると呼ばれる関数
	public void OnDamage()
	{
		m_skeletonAnimation.state.SetAnimation(0, "damage", false);
		m_skeletonAnimation.state.Complete += PlayIdleAnimation;
	}

	public void PlayIdleAnimation(TrackEntry trackEntry)
	{
		if (trackEntry.animation.Name == "damage")
		{
			m_skeletonAnimation.state.SetAnimation(0, "idle", true);
			m_skeletonAnimation.state.Complete -= PlayIdleAnimation;
		}
	}
}

このような結果になります。

f:id:marumittu_tech:20170611221159g:plain

上記のような方法で、簡単にアニメーションの切り替えができました。

Spineは日本語のReferenceがあまり充実していないのですが、
プログラムコードを編集する事ができるので
僕は色々と弄ってみてUnity上で結果を見ながら遊んでいます。

こういう手探りで調べていく作業も楽しいわ~。