Live2DをMechanismで制御のやり方は中の人が記事に(1、2)していますが
moc読み込みが基礎のやり方だったのでjsonを読み込むSampleApp1を基礎にする方法を書いていきます
Mechanismとmodel.jsonファイル
MechanismはUnityのアニメーションサポートの機能で
状態遷移とパラメーターの制御を提供してくれます
moc読み込みの方法ではMechanismの機能でパラメーターをひも付けする形で制御しますが
別個でテクスチャーを用意したり、物理演算をつけたりしなくてはいけません
Live2DViewerで作れるmodel.jsonを読み込む方式では
テクスチャーや表示の切り替え、物理演算や音声が指定した状態のモーションの読み込みまで
全部一発でやってくれます
便利ですね
SampleApp1ではモーションの再生をmodel.jsonに登録されたグループ名を使って
StartMotionとStartRandomMotionで再生させるのですが
これのMechanism側との同機をさせます
必要な機能とその手段
状態遷移とモーションを同機させるのですが
まず遷移が移った時にモーションを開始する機能が要ります
そして終了サイドの動きで
・モーションが終わった時に次の状態へ遷移する機能
・モーションが終わった時にもう一度モーションを再生する機能(アイドリング系向け)
このふたつが必要ですね
表記も必要です
また状態と再生するときのグループ名との対応の機能も必要です
切り替え系に関してはUpdateを監視するのも手なのですが便利な機能があります
animatorでノードを指定してAdd behaviourを押すとスクリプトを追加することができます
StateMachineBehaviourから継承されるこのスクリプトでは
状態のEnter,Update,Exitのタイミングでメッセージ関数を実行してくれます
using UnityEngine;
using System.Collections;
public class test : StateMachineBehaviour {
public bool looping;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var proxy = animator.GetComponent<LAppModelProxy>();
if(proxy)
{
proxy.GetModel().StartRandomMotion(getcode(animator,layerIndex), LAppDefine.PRIORITY_FORCE);
animator.SetBool("nomtion",false);
}
}
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
var proxy = animator.GetComponent<LAppModelProxy>();
if (proxy)
{
if (proxy.GetModel().getMainMotionManager().isFinished())
{
if(looping)
{
proxy.GetModel().StartRandomMotion(getcode(animator,layerIndex), LAppDefine.PRIORITY_IDLE);
}
else
{
animator.SetBool("nomtion",true);
}
}
}
}
private string getcode(Animator animator, int layerIndex)
{
var clips = animator.GetNextAnimatorClipInfo(layerIndex);
if (clips.Length == 0)
{
clips = animator.GetCurrentAnimatorClipInfo(layerIndex);
}
return clips[0].clip.name;
}
}
publicのLoopingはUnityのオブジェクトの方から指定できるようになるので
これでアイドリングするかどうか表記します
モーション再生につかうグループ名はMotionのファイル名を呼び出して使います
機能的に重複した状態もあるので
”次ファイル名、参照できなければ現在ファイル名”をgetcodeで参照できるようにしてあります
(右側はLive2DViewer、2つが対応する形)
このスクリプトを全部のノードに貼り付けます
またアイドリング系のノードにはloopingにチェックを入れておきましょう
Mechanism側の設定
スクリプトを眺めるとanimator.SetBool("nomotion",~~);というところがありますね
モーションの終了判定をMechanism側に伝えるトコロです
前後してしまいますがAnimatorの左側、Parametersの欄でboolで追加します
そうしてから終了時遷移ノード間の矢印の条件設定にnomtionがtrueになったとき動作するように設定します
他のノードは他にパラメーターを用意して通常に条件設定を行います
総評として
Mechanismとパラメーターを連動させるのはMechanism側の機能をフルで使えるので
いろんなことができるようになるでしょう
モデルに無いパラメーターを扱えるのもその一つかもしれません
しかしmodel.jsonと共存させた場合はモデルのパラメーター設計が違うものに対しても
コードを打たずに対応できる強みがありますね
また一つのノードで複数のモーションをランダムに動かすという芸当もできます
あと音声がいっしょに登録できるのも強みかな
グループ名の指定にmotionファイル名を使っていましたが
スクリプトのPublicで指定する方法でもよかったかもしれません
最後にUpdateのなかでなんとかやりくりしようとてた残骸をサンプルに残しておきます
string newcode = parent.ani.GetCurrentAnimatorClipInfo(0)[0].clip.name;
if (oldcode != newcode)
{
StartRandomMotion(newcode, LAppDefine.PRIORITY_FORCE);
parent.e_nomotion = false;
oldcode = newcode;
}
else
{
// 待機モーション判定
if (mainMotionManager.isFinished())
{
// モーションの再生がない場合、待機モーションの中からランダムで再生する
if (newcode.IndexOf("idle") != -1)
{
StartRandomMotion(newcode, LAppDefine.PRIORITY_IDLE);
}
parent.e_nomotion = true;
}
else
{
parent.e_nomotion = false;
}
}
動作としては同じ内容です