2017年3月25日土曜日

【Unity5】キャラモーションをリミテッドアニメにする ~ボーントレース編~

Unityのキャラクターの動きのコマを抜いて、リミテッドアニメにしてみます。

  • 【方法1】 アニメーションクリップの再生フレームを指定する
  • 【方法2】 任意のフレームで、フルアニメーションするキャラのボーントランスフォームを複製する

【方法1】は、アニメーションクリップの再生時間を指定して一時停止、数フレーム待って再び再生位置を指定して一時停止、というイメージです。これは使いたいポーズのフレームを直接指定できますが、物理で動く揺れ物のコマは落とせません。

【方法2】は仕込みに少々手間がかかりますが、揺れ物のボーンまでトレースするのでコマ落としできます。

今回はボーンをトレースしてリミテッドアニメを実現する、【方法2】について書きます。使用したUnityは【5.5.2f1 (64-bit)】です。

※【方法1】については次回記事、再生フレーム指定編へ。


素材のインポート


テストに使うキャラクターモデルはこちらの「プロ生ちゃん」Unityパッケージ。

ダウンロードしたファイルの中から【PronamaChan_Ver3.unitypackage】をプロジェクトにインポートします。

次に、テスト用のアニメーションを用意します。
スタンダードアセットの【Characters】をインポートし、そこに含まれるアニメーションを使います。

Hierarchyに配置


複製元となるプロ生ちゃんを配置します。

【PronamaChan】 > 【Prefabs】 > 【Normal】 > 【Standard】
にある、【PronamaChan_dynamic】を【Hierarchy】に配置します。

このプロ生ちゃんにアニメーションをさせます。
【Standard Assets】 > 【Characters】 > 【ThirdPersonCharacter】 > 【HumanoidRun】にある
【HumanoidRun】を【Hierarchy】の【Pronamachan_Dynamic】にドラッグ&ドロップします。

これでプロ生ちゃんが走るようになりましたが、このままだと画面の外まで走って行ってしまいます。
【PronamaChan_dynamic】の【Inspector】 > 【Animator】の【Apply Root Motion】のチェックを外しましょう。

これでプロ生ちゃんはその場で走るようになります。

リミテッドアニメーション用のキャラクタを配置します。
スクリプトで同名ボーンのトランスフォームを複製するようにしますので、複製元と同じ【PronamaChan_dynamic】を【Hierarchy】に追加し、名前は【PronamaChan_dynamic_clone】とします。

プロ生ちゃんの髪の毛やスカート、リボンは【Spring Manager】と【Spring Bone】というスクリプトで動いていますが、クローン側には必要ないのでオフにします。
【Spring Manager】だけオフにすれば十分ですので、【PronamaChan_Arm】の【lower_body】【Upper_body2】【head】についている【Spring Manager】のチェックを外します。


ライトやカメラの位置も調整します。右が複製元、左がクローンです。

ボーントレースのスクリプト


次にスクリプトを用意します。
下記のサイトを参考にさせていただきました。


【project】に【C# Script】を新規追加し、「LimitedAnimation」という名前に変更、ダブルクリックしてファイルを開き、下記の内容に書き換えて保存。

LimitedAnimation.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;//list使用のために必要
public class LimitedAnimation : MonoBehaviour
{
       public int flameInterval = 3;//抜くフレーム数
       public GameObject masterObject; //ボーンの複製元になるオブジェクト
       private GameObject cloneObject;
       public bool rotationOnly = false;
       private Dictionary<string, Transform> masterAllChildren = new Dictionary<string, Transform>();
       private Dictionary<string, Transform> cloneAllChildren = new Dictionary<string, Transform>();
       void Awake()
       {
              //スクリプトがアタッチされているオブジェクトを取得
              cloneObject = gameObject;
              //すべての子オブジェクトのトランスフォームを取得
              GetChildren(masterObject, ref masterAllChildren, true);
              GetChildren(cloneObject, ref cloneAllChildren, false);
       }

       void LateUpdate()
       {
              if (IsDrawFrame(Time.frameCount))
              {
                     foreach (KeyValuePair<string, Transform> child in cloneAllChildren)
                     {
                           child.Value.transform.rotation = masterAllChildren[child.Key].transform.rotation;
                           if (!rotationOnly)
                           {
                                  child.Value.transform.position = masterAllChildren[child.Key].transform.position;
                           }
                     }
              }
       }
       //すべての子オブジェクトのトランスフォームを取得
       void GetChildren(GameObject obj, ref Dictionary<string, Transform> allChildren, bool springBoneEnabled)//listは参照渡し
       {
              Transform children = obj.GetComponentInChildren<Transform>();
              //子要素がいなければ終了
              if (children.childCount == 0)
              {
                     return;
              }
              foreach (Transform ob in children)
              {
                     allChildren[ob.name] = ob.gameObject.GetComponent<Transform>();
                     GetChildren(ob.gameObject, ref allChildren, springBoneEnabled);
              }
       }
       //ボーンコピーするフレームかどうか判断
       bool IsDrawFrame(float nowframe)
       {
              if (nowframe % flameInterval == 0)
              {
                     return true;
              }
              return false;
       }
}

このスクリプトをクローン側の【PronamaChan_dynamic_clone】にアタッチします。
【Master Object】には複製元を登録します。【Hierarchy】の【Pronamachan_dynamic】をドラッグ&ドロップしてください。
【Flame Interval】に、抜くフレーム数を入力します。ここでは3フレームごとに動くようにします。
今回はわかりやすく複製元と並べて再生したいので、【Rotation Only】にチェックを入れます。
※【Rotation Only】のチェックを外すと複製元と位置が重なるので、レイヤーを分けるなどして複製元がカメラに映らないようにする必要があります。

これでプレビューしてみると、【Rotation Only】にチェックが入っているので上下運動は拾えていないですが、クローン側がコマ落ちアニメーションしていることがわかると思います。

連番画像の出力


連番画像で出力してみます。

こちらのサイトで紹介されているスクリプト【SaveToPng.cs】をカメラにアタッチします。


再生直後は少し挙動が荒れるのでキャプチャ開始する【StartFrame】は「30」にしました。

再生すると【StartFrame】から【EndFrame】の間だけキャプチャされ、画像がプロジェクトフォルダ直下の【ScreenShot】フォルダに保存されます。

左側のプロ生ちゃんのポーズが3フレームごとに更新されているのがお分かりいただけるでしょうか。


今回のスクリプトでは一定の時間間隔でボーントレースしました。
落とすコマ数に変化をつけようとするなら、タイムーシートのような形で配列データを用意したうえでアニメーションクリップの再生フレームも監視する必要がありそうです。

Blender上でMIDIファイルを読み込み3Dモデルの鍵盤を動かすpythonスクリプトに挑戦したこと

昔取り組んだことについての忘備録 作成日 2016/8/7 目的 MIDIファイルの仕様の理解 BlenderとPythonの学習 内容 MIDIファイルのバイナリを読み込んで解析し、ノートオン、ノートオフ情報に従って、対応する音階の...