gologiusの巣

プログラミング、モデリングなどのメモです。誰かの役に立てるとうれしいです。

【Unity】Playable APIを用いてモーション遷移時に補間に考慮したいこと

※都度更新予定

モーションを補間させながら遷移させる場合、Playable APIには関数は用意されていません(Unity2017.3現在)。 よって、PlayableAPIを使用する場合、自分でコードを書かなければなりません。 まぁ今後関数が用意される可能性もありますが・・・

というわけで、PlayableAPIでモーション遷移させる場合に、 注意したほうがさそうな場合について記載します。

個人的にはモーション切り替えパターンは

  • とにかく指定したモーションにスムーズに移行してほしいパターン(棒立ち→走り始める、など)
  • 上半身が別のモーションによって上書きされるパターン(走る→走りながら撃つ、など)
  • 指定したモーションの最初のフレームから最後のフレームまで再生してほしいパターン(武器をちゃんと振ってほしい場合など)
  • 遷移前の時間を引き継いでほしいパターン(前方向に歩く→右方向に歩く場合など。)

この四つのパターンにに分けられると思っています。今回はこの四つのパターンを実装したサンプルプロジェクトを作成しました。

サンプルプロジェクト

というわけで作成したサンプルプロジェクトを置いておきます。 github.com

実体はMotionPlayerコンポーネントです。Unityちゃんにアタッチされてます。 UIからモーション再生方法をいろいろ弄れます。 ちなみに逆再生も可能。

次からモーションの切り替えパターンの説明となります。

とにかく指定したモーションにスムーズに移行してほしいパターン

切り替え時にPlayableへのinputWeightを変更しながら、モーション切り替えをします(サンプルプロジェクトのMotionMixer. crossFadeCoroutine()が該当)

・・・という基本的な考え方をこちらの記事から学びました。 tsubakit1.hateblo.jp

上半身が別のモーションによって上書きされるパターン

例えば、

layerMixer.SetLayerMaskFromAvatarMask(layerIndex=1, upperMask);

のように、Layer1に上半身のマスクを割り当てた状態で

layerMixer.SetInputWeight(layer=1, weight=1f);

のようにweight=1fにすれば上半身のモーションは上書きされますし、weight=0fにすれば、layer=0に適用されているモーションが上半身にも適用されます。

ただ、このweight切り替えの際にいきなり値を変えると、モーションが補間されすに切り替わってしまいます。 なので

private IEnumerator crossFadeLayerWeight(int layer, float duration, bool enable)
 {
     float waitTime = Time.time + duration;
     yield return new WaitWhile(() =>
     {

         float diff = waitTime - Time.time;
         float rate = Mathf.Clamp01(diff / duration);
         float weight = (enable) ? 1 - rate : rate;
         layerMixer.SetInputWeight(layer, weight);

         if (diff <= 0)
             return false;
         else
             return true;
     });
 }

のようにして値を補間させながら切り替えましょう(詳しくはMotionPlayer.setLayerEnabled( )を参照)。

※サンプルプロジェクトの場合「上半身上書き」にチェックを入れ、遷移時間のスライダを0以上にすると、遷移が見れます。

指定したモーションの最初のフレームから最後のフレームまで再生してほしいパターン

※サンプルプロジェクトの場合「完全遷移まで待機」にチェックを入れる、に該当します。

例えば武器を 1. 振り上げて 2. 振り下ろす

という二つで一つのモーションに遷移したい場合、遷移前のモーションがミックスされてしまうと、 2.振り下ろす のモーションしか再生されてないようにみえる可能性があります。

f:id:gologius:20180331223208p:plain

ですので、遷移後のモーションの一フレーム目の姿勢になるまで、再生を待ってあげる必要があります。

f:id:gologius:20180331223210p:plain

またせる処理はコルーチンを使えば大して難しくないですね。以下のように遷移先のモーション再生を停止させておき、一定時間後にモーション再生を再開します(詳しくはMotionMixer. crossFadeCoroutine()参照)

     if (waitCrossFade)
      nowPlayable.SetSpeed(0f); //遷移先のモーション時間を止めておく

      //inputWeightの変更 略

       if (waitCrossFade)
       {
           float play_speed = playParam.reverse ? -1f : 1f;
           nowPlayable.SetSpeed(play_speed); //遷移先のモーション再生時間を元に戻す
       }

遷移前の時間を引き継いでほしいパターン

※サンプルプロジェクトの場合「アニメーション時間同期」にチェックを入れる、に該当します。

例えば走るモーションが、正面、左、右の3パターンあり、すべて同じ周期で走っているとします。 この場合時間同期させながら遷移させないと、片足で移動しているように見えてしまいます。

f:id:gologius:20180331224118g:plain

なので、再生する時に、遷移前のモーションの再生中時間を

nowPlayable.SetTime(beforePlayable.GetTime());

のようにして引き継いであげる必要があります。 サンプルではMotionMixer.recconect()内に処理があります。

f:id:gologius:20180331224255g:plain

まとめ

  • Playable APIでモーション遷移する際のパターンについて書きました
  • いかんせん動画か資料がないとわかりづらい内容なので、記事を書く準備が面倒でしたね・・・
  • この記事は追記、更新しまくると思います