unityでボーンコンストレインするC#スプリクトを書いたものの……
閉じる
閉じる

新しい記事を投稿しました。シェアして読者に伝えましょう

×

unityでボーンコンストレインするC#スプリクトを書いたものの……

2019-06-30 20:58
    書いたものの結局必要なかったというか、そもそもの原因を勘違いしていたという結論



    事の始まりとして自作ゆかりさんを一度テスト的にunityに持ち込んでモーション当ててみて大丈夫かを確認するところが始まり

    unity標準のhumanoid という人体構造用のボーンを踏襲することでアセットストアなどで売られているモーションを適用できるようになるのだけども
    これは当然MMDのときとは構造が違う

    それでまぁ多少はすり合わせればいいのだけど、腕のねじりを分散緩和するようなボーンがどうやら組み込まれていないっぽい
    (ただ肩だとかの?さらに親や子のほうにねじれ具合を分散するなどの対策はされているらしい、いま見直してもうちょっと正確な理解になったがこの時点ではあまり把握していなかった)
    参考:UnityでHumanoidリグを利用する際の肘関節の捩れを調べる


    そもそも最新のunityには回転コンストレインのスプリクトがある
    参考:Unityにおけるボーン回転コンストレイント
    じゃぁhumanoidボーンから部分的に並列的な親子関係にして、
    自前のボーンのほうにコンストレインで値を移してあげてねじりだけMMDと同一構造のところに同じように分散したらいいのでは?
    と思いblenderでのボーン構造見直しもそれを見込んで行って
    unity上で設定して動かしてみるとなんかずれる

    ここで勘違いしたのが「unityの回転コンストレインってワールド空間判定でやっているのでは?だからずれるのでは?」
    やったことないスプリクトに挑戦する前に、もうちょっと試して確認するべきだったよね
    まーそのうちやろうとか詳しくなりたいと思ってたことであるので知識、学習的に無駄なことはなかったんだけども

    さんざん勘違いしてたと書いているから今更なのだけども
    unityの回転コンストレインはちゃんとローカル軸を見てやってくれてたっぽい
    ということを自前のスプリクト完成してから
    「あれ?こっちもずれる上にずれ方に見覚えしかないぞ!?」
    という

    自前のスプリクトもunityのコンストレインもunity上でキューブとか出して
    簡易的に対象のボーン回りと同じ親子構造作って試したらどっちも一応正常動作してて
    完全に別の原因ですよ……

    

    そうなると何が原因かが謎で頭抱えるのだけど
    先ほど上に「もうちょっと正確な理解になった」と書いたhumanoidボーンについて
    上腕のねじりを肩とかにも分散してるっぽいというのが
    今ちょうど上腕で試しているのでそれが原因か?とかちょっと思っているところなんだけども
    (でもねじりを分散したとき(根本はxz回転のみコンストレイン、子供のほうでY回転を0.25づつ請け負う)に思いっきりずれるのであって、根元でxyz全部請け負ってコンストレインする分にはずれない(少なくとも見た目上は)なのでやっぱり違うかな……)
    それはそれとしてせっかく参考書とか買って読みふけって書いてみて
    正常に動いてはいるからスプリクト晒しとけ!!
    という奴ですはい(結論)

    ただまぁ返す返すunity標準のコンストレインで用は足りるというかそっちのほうが優秀というかなので
    自分と同じくスプリクトやり始めた人がみたらなんか参考になるところあるかもぐらいの感じ
    (例によって自分がすぐ忘れるからコメント入れまくったつもりなので)


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;


    [ExecuteInEditMode] //これをつけることでゲームすたーとしていないエディター上でも動作するようになる
    public class LocalRotationConstraints : MonoBehaviour //通常はMonoBehaviourを継承することでunity用の各種メソッド?を使用できる
    {
    //public……どこからでもアクセスできる変数ですよという宣言
    //private……このクラス内からしかアクセスできないですよ(このクラスの外では使用しませんよ)という宣言
    //後程スプリクトを書き換える時などに、このクラス自体が不要じゃね?となったとき、publicが使われていたら
    //いやどっかでこの変数使ってるはずだぞアブねー確認!となる為のものらしい

    //各型で変数を宣言(といってもVector3は正確にはクラスだったらしい……)
    //これで登録欄も同時にできるっぽいがボタンなどは無理なので下のカスタムインスペクター用の記述で改めて書く
    //(するとそちらが優先される?)
    public GameObject ConstraintBone;
    public Vector3 ThisBoneRotation;
    public Vector3 ConstraintBoneRotation;
    public float ConstraintWeight;
    public bool ConstraintX;
    public bool ConstraintY;
    public bool ConstraintZ;
    public bool ConstraintActive;




    // 初期化に使用 最初に一回だけ実行される処理ということ、繰り返さなくていいならここに記述してしまう
    private void Start()
    {
    //リアルタイムで動いてほしい処理なので個々の記述はとくになし
    }


    // Update はフレームごとに1 回 毎フレーム繰り返す、常時処理されるということ
    // FixedUpdateは一定間隔(フレーム落ちしても本来のフレームタイミングでということ?)
    // FixedUpdateであるべきな気がするけどExecuteInEditModeで反応しなかったのでいったん通常のUpdate
    private void Update()
    {
    if (ConstraintActive == true)
    {

    //vectore3の入れ物を用意しておく ゼロで初期化済みにしておく
    Vector3 Temporary_Value = Vector3.zero;


    //Mathf.DeltaAngleは角度の差を-180~180の間の最小で返してくれる350と20みたいな時でも330ではなく-30となって実際安心
    //それぞれの軸の有効無効判定もして有効なものだけ処理
    if (ConstraintX == true)
    {
    Temporary_Value.x = Mathf.DeltaAngle(ConstraintBoneRotation.x, ConstraintBone.transform.localEulerAngles.x);
    }
    if (ConstraintY == true)
    {
    Temporary_Value.y = Mathf.DeltaAngle(ConstraintBoneRotation.y, ConstraintBone.transform.localEulerAngles.y);
    }
    if (ConstraintZ == true)
    {
    Temporary_Value.z = Mathf.DeltaAngle(ConstraintBoneRotation.z, ConstraintBone.transform.localEulerAngles.z);
    }

    //その差分をこちらのベースと足してあげてからクォータニオンに変換して入れる
    this.transform.localRotation = Quaternion.Euler(ThisBoneRotation + (Temporary_Value * ConstraintWeight) );

    }
    }




    //以下はボタンが押されたとき用の処理

    //通常時のコンストレインされる側ボーン角度を取得
    //角度はlocalRotationだとQuaternion(xyzw)で取得する必要があることに注意
    //Vector3でほしいときはlocalEulerAngles
    public Vector3 GTBR()
    {
    Vector3 Temporary_Value = this.transform.localEulerAngles;
    return Temporary_Value;
    }

    //通常時のコンストレインする側ボーン角度を取得
    public Vector3 GCBR(GameObject NowBone)
    {
    Vector3 Temporary_Value = NowBone.transform.localEulerAngles;
    return Temporary_Value;
    }

    /*
    //間違えた時のためにボーン角度リセット
    //必要かと思ったがRevert的なリセット方法というかデフォルト時の値取得方法なさそうなのでいったんオミット
    public void ResetBone(GameObject NowBone)

    {
    this.transform.localRotation = Quaternion.identity;
    NowBone.transform.localRotation = Quaternion.identity;
    }
    */

    }







    //ここから下はスプリクトのUI(カスタムインスペクターというらしい)の描画に関する記述-----------------------------------------------------------------------------------------------------------

    [CustomEditor(typeof(LocalRotationConstraints))] //属性を与えて(?)どのスプリクトをカスタマイズするのかを指定
    public class LRCEditor : Editor //カスタムインスペクターの場合はEditorを継承しないとダメらしい(ので別クラスで記述することになる)
    {



    public override void OnInspectorGUI()
    {
    // 「target は処理コードのインスタンスで 処理コードの型でキャストする」
    //クラスを宣言しなおしてそれをインスタンスで取得しているということでよいかな??
    LocalRotationConstraints LRC = target as LocalRotationConstraints;


    //シリアライズオブジェクト?関係あるかもしれないのでコメント扱いで残している
    //serializedObject.Update();
    //ボーンを登録するフィールドを改めて設定と取得
    LRC.ConstraintBone = EditorGUILayout.ObjectField("On the constraining side", LRC.ConstraintBone, typeof(GameObject), true) as GameObject;
    //こちらもシリアライズオブジェクト
    //serializedObject.ApplyModifiedProperties();

    //間にスペース作る
    EditorGUILayout.Space(); EditorGUILayout.Space();

    //このボーンの基本角度入力欄
    LRC.ThisBoneRotation = EditorGUILayout.Vector3Field("ThisBoneRotation", LRC.ThisBoneRotation);

    //ボタンを表示するのと同時に、押されたときにTrueを返すので一緒にif文に含めてしまうことで押された時の処理も書けるようにということらしい
    if (GUILayout.Button("GetThisBoneRotation"))
    {
    // 以下はボタンが押されたときに実行される処理
    //上で記述した角度取得メソッドを呼び出してこのボーンの角度を取得
    LRC.ThisBoneRotation = LRC.GTBR();
    ;

    }


    //上と同じ内容の拘束側ボーン版
    LRC.ConstraintBoneRotation = EditorGUILayout.Vector3Field("ConstraintBoneRotation", LRC.ConstraintBoneRotation);
    if (GUILayout.Button("GetConstraintBoneRotation"))
    {
    //このボーンの角度ではないので対象ボーンをゲームオブジェクトとして渡すところだけ上と違う
    LRC.ConstraintBoneRotation = LRC.GCBR(LRC.ConstraintBone);



    }



    /*
    //スプリクトが変に動作したときに各ボーンの角度おかしくなった時用の角度リセットボタン
    //いったんオミット
    EditorGUILayout.Space(); EditorGUILayout.Space();
    if (GUILayout.Button("RotationRest"))
    {
    LRC.ResetBone(LRC.ConstraintBone);

    }
    */


    EditorGUILayout.Space(); EditorGUILayout.Space();

    //コンストレインの影響度
    LRC.ConstraintWeight = EditorGUILayout.FloatField("Weight", LRC.ConstraintWeight);

    //コンストレインのXYZそれぞれのONOFF用チェックボックス
    LRC.ConstraintX = EditorGUILayout.Toggle("X", LRC.ConstraintX);
    LRC.ConstraintY = EditorGUILayout.Toggle("Y", LRC.ConstraintY);
    LRC.ConstraintZ = EditorGUILayout.Toggle("Z", LRC.ConstraintZ);

    //コンストレインをアクティブ状態にするかどうかのチェックボックス
    LRC.ConstraintActive = EditorGUILayout.Toggle("ConstraintActive", LRC.ConstraintActive);
    }


    }

    数年前のblender用pythonと違ってC#だから
    スペース修正しなおしとかしなくてもコピペで動くかも、動かんかも






    そしてどうでもいいけどマリオメーカー2かってコース作ったのでついでで晒しておく!
    (本当に関係ない)

    QSP-0YY-LNG
    森をコイン集めながら登っていくコース
    現在クリア率30%ぐらいなので平均して三回チャレンジしたらクリアできてる感じの難易度のコース
    普通にマリオにありそうな感じのを作りたいと思ってやったので難易度的にもあっている気はする……?


    RXH-831-L6G
    前作でも作ったパズル系ステージがいま見直すとごちゃごちゃして
    パズル以前のところが把握しずらいのではと思っていろいろ整理しなおしたり簡略化したりしてみたコース
    壁を壊さないと進めないけどどうやって壊せる形にもっていくかみたいな感じ



    前作があっての今作だからかネットで作られているコースも落ち着いた難易度のものが前作より多い印象でヌルゲーマーには大分ありがたい……

    あとコース作ってもどっかで晒してみて、誰かの評価なり足跡なりつけてもらわないと
    野良の人というかマリオメーカーでコースをランダムに遊ぶとかには限りなく引っ掛かりにくいようなので
    コース作った人はどこかでガンガンコースIDさらしたほうがいいと思う
    (自分のコースも某所で晒してみる前は足跡一個もつかなかったし、
     ぎゃくにいいね!してくれた人のまだ遊ばれてないコース遊んだらそのコースのタイムが更新された=ほかの人も遊んだみたいなお知らせが割と早めに来たりとか)





    しばらく時間あったのでのんびりながら期間的には集中していろいろ手を出せていたのだけど
    また普通に戻ってしまうので
    次のゆかりさんをunityに持ち込む進展報告の更新はいつになるやら……
    広告
    コメントを書く
    コメントをするには、
    ログインして下さい。