AIプログラムとかUnityゲーム開発について

探索や学習などを活用したAI系ゲームを作りたいと思います。

SpringBoneを実験中

 髪の毛やスカートなど、3Dモデルにとって揺れものは避けて取れない。
 ということで、次はこれに取り組んでみます。
 ricopinさんが初めに作られたSpringBone.csは、物理エンジンを使わずにコードで物体同士の衝突を計算して揺れものを動かしている。
 これは改良されてUnityChanやプロ生ちゃんにも搭載されている。
 SpringBoneという名称からUnityのSpringJointを使ってるかと思っていたら、そんなものは使ってなくて、positonとrotationを揺れものとコライダーの衝突で計算している仕組みでした。


 髪の毛だと、例えば、Hair1→Hair2→Hair3→Hair4みたいにボーンが階層化されているとします。
 この時に、それぞれにSpringBone.csをAddして、親から子への関係をリストとしてそれぞれ登録します。
 次に、髪の毛と衝突する可能性のある例えば頭にコライダー(SpringCollider)を設定します。
 最終的に、SpringManagerを登録しますが、これがすべてのSpringBoneに指令をだして、個々のSpringBoneがすべてのコライダーとの衝突を考慮しながら、子供のSpringBoneにばねとしての動きを伝えます。
 この遅延した連動によって、髪の毛やスカートのうねうねした動きが実現されている。

 ただし、SpringManagerにすべてのSpringBoneとSpringColliderをちまちま登録しないといけない。
 プロ生ちゃんの頭部のSpringManagerには50個のSpringBoneが登録されています。これはとても人間がやる作業ではないです(´・ω・`)
 しかも、SpringBoneには子供を登録しつつ、対象のSpringColliderも登録しないといけません。髪の毛だけで50個のSpringBoneがあるのです。
 スカートは別ですw

 とてもやってられないので、プログラムでアタッチする自動化を試みます。


 まず、親BONEから自動的に子供にSpringBoneをアタッチして、SpringManagerに登録してくれるコードを書いて自動化してみました。
 まだ設定がおかしいらしく、ポニーテールが直立しています(^◇^)
 プロ生ちゃんを揺すってみると、髪の毛やスカートの動きは完璧ですね!
 あとは登録作業の煩雑さを解消すればいい感じ!

f:id:yasu9780:20161124040059p:plain

ポニーテールの親BONEであるBackHair1を引数に指定すれば、その子供と子供と子供といった感じで末端LEAFまで自動でアタッチしてくれる。コライダーもsc0を設定してくれる。

setupSpringBone("88.joint_BackHair1",sc0);

このBoneのGameObjectは配列に保持しているので、SpringManagerに自動で登録される。

setupSpringManager("3.joint_Head");


 考え方として、スカートにしても髪の毛にしても、SpringBoneのROOTの下の子供は一人しかいません(というのが多いと思う)
 ならば、ROOTだけ指定すれば、あとは自動的に子供を探索して自動アタッチすればいいんです。そして同時にManagerにも自動登録すればいいんです。
 そもそも、boneごとにアタッチしないでも、Managerがboneのtransformをすべて動かせばいい。これならアタッチするのは一つで済むんです。
 UnityStoreで売られているDynamicBoneは解説動画を見る限り、同じような簡単な登録を実現しているようです。

public class SpringSetup : MonoBehaviour {
    GameObject[] gm = new GameObject[10];
    PronamaChan.SpringBone[] sb = new PronamaChan.SpringBone[10];
    int sbIndex;

    void Start () {
        sbIndex = 0;
        PronamaChan.SpringCollider sc0 = setupSpringCollider("3.joint_Head", "HeadCollider", -Vector3.up * 0.1f);
        setupSpringBone("88.joint_BackHair1",sc0);
        setupSpringManager("3.joint_Head");

        sbIndex = 0;
        PronamaChan.SpringCollider sc1 = setupSpringCollider("30.joint_LeftHip", "LegColliderL1" ,- Vector3.up*0.1f);
        PronamaChan.SpringCollider sc2 = setupSpringCollider("55.joint_RightHip", "LegColliderR1", - Vector3.up * 0.1f);
        setupSpringBone("28.joint_LeftSkirtFront",sc1);
        setupSpringBone("29.joint_LeftSkirtBack", sc1);
        setupSpringBone("53.joint_RightSkirtFront", sc2);
        setupSpringBone("54.joint_RightSkirtBack", sc2);
        setupSpringManager("7.joint_HipMaster");
    }
    PronamaChan.SpringCollider setupSpringCollider(string bone,string colName,Vector3 pos)
    {
        GameObject manager = GameObject.Find(bone) as GameObject;
        GameObject col =Instantiate((GameObject)Resources.Load("SpringCollider"), pos, Quaternion.identity) as GameObject;
        col.name = colName;
        col.transform.SetParent(manager.transform);

        PronamaChan.SpringCollider sc = col.AddComponent<PronamaChan.SpringCollider>() as PronamaChan.SpringCollider;
        sc.radius = 0.07f;
        return sc;
    }
    void setupSpringManager(string bone)
    {
        GameObject manager = GameObject.Find(bone) as GameObject;
        PronamaChan.SpringManager sm = manager.AddComponent<PronamaChan.SpringManager>() as PronamaChan.SpringManager;

        /*
        sm.dynamicRatio = 1f;
        sm.stiffnessForce = 0.0001f; // 柔らかさ(小さい程揺れる)
        sm.stiffnessCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0));
        sm.dragForce = 0f;          // 揺れの減衰力
        sm.dragCurve = new AnimationCurve(new Keyframe(0, 0.5f), new Keyframe(1, 0.5f));
        */
        sm.springBones = new PronamaChan.SpringBone[sbIndex];
        for (int i = 0; i < sbIndex; i++) sm.springBones[i] = sb[i];
    }

    void setupSpringBone(string root,PronamaChan.SpringCollider col)
    {
        gm[0] = GameObject.Find(root) as GameObject;
        sb[0] = gm[0].AddComponent<PronamaChan.SpringBone>() as PronamaChan.SpringBone;
        sb[0].radius = 0.05f;
        //sb[0].isUseEachBoneForceSettings = true; // 柔らかさ・揺れ減衰を使用するかどうか
        sb[0].stiffnessForce = 0.01f; //ばねが戻る力(小さい程揺れる)
        sb[0].dragForce = 0.4f;//揺れ減衰力

        for (;;)
        {
            bool flag = false;
            foreach (Transform child in gm[sbIndex].transform)
            {
                gm[sbIndex + 1] = child.gameObject;
                if (gm[sbIndex + 1].transform.gameObject == null) break;

                sb[sbIndex + 1] = gm[sbIndex + 1].AddComponent<PronamaChan.SpringBone>() as PronamaChan.SpringBone;

                sb[sbIndex + 1].radius = 0.05f;
                //sb[i + 1].isUseEachBoneForceSettings = true; // 柔らかさ・揺れ減衰を使用するかどうか
                sb[sbIndex + 1].stiffnessForce = 0.01f; //柔らかさ
                sb[sbIndex+ 1].dragForce = 0.4f;//揺れ減衰力

                sb[sbIndex].child = gm[sbIndex + 1].transform;
                sb[sbIndex].colliders = new PronamaChan.SpringCollider[1];
                sb[sbIndex].colliders[0] = col;

                flag = true;
                break;
            }
            if (!flag) break;
            sbIndex++;
        }
    }


    void Update()
    {
    }
}