MediaPipeとUnityをUDPで繋ぐ -その5-

アバター

はじめに

前回の投稿から約1ヶ月ほど空きました。
何をしていたかと言えば、両手もモデルを使えるように試行錯誤していました。
MediaPipeのそもそもの精度の問題なのか、どうしても納得いく制御ができず🥺
ある程度の形になったところで区切ることにしました。
未だ課題はありますが、こういう方法もある、というところで残そうと思います。

Python

こちらから変更なし

Blender

作成したモデルはこちら。


基本構造は前回同様ですが、両手のボーンを変更しました。
具体的には、手首から各指に繋がる骨を削除しました。


こちらは前回作成したモデルです。手首から指へのボーンがありますね。
なぜ削除したかというと、このボーンと指のボーンに26°ほどの角度が付いているのが不都合だからです。
Unityスクリプトでは、掌と指の角度で回転量を算出するのですが、もともと角度が付いてしまっていると、この計算が難しくなります。
今回は掌の細かな制御はしないので、そもそもボーンを外すことにしました。

別の方法として、掌のボーンをまっすぐにして、角度0として残しておいても良いと思います。

さらに、Unityと軸を合わせるため、以下の操作をしました。
 1. -y方向を正面とする
 2. オブジェクトモードで、x軸を-y方向へ90°回転
 3. 編集モードで、x軸をy方向へ90°回転
見た目は元の状態に戻っていますが、実際には90°下向きになっています。
これで、Unityへインポートしたときに軸がおかしくなる現象が改善します。

Unity

スクリプト

MediaPipe管理

こちらから変更なし

UDP管理

こちらから変更なし

顔パーツ管理

using System.Collections.Generic;
using UnityEngine;

namespace PartsData_face_ns{

[System.Serializable]
public class PartsData_face
{
    public string parts = "";
    public float[] nose = new float[3];
    public float[] top = new float[3];
    public float[] bottom = new float[3];
    public float[] right = new float[3];
    public float[] left = new float[3];
    public float[] blendshape = new float[52];
}

public class FaceManager : MonoBehaviour
{
    public enum FaceBlendshapeName
    {
        Neutral = 0,
        BrowDownLeft,
        BrowDownRight,
        BrowInnerUp,
        BrowOuterUpLeft,
        BrowOuterUpRight,
        CheekPuff,
        CheekSquintLeft,
        CheekSquintRight,
        EyeBlinkLeft,
        EyeBlinkRight,
        EyeLookDownLeft,
        EyeLookDownRight,
        EyeLookInLeft,
        EyeLookInRight,
        EyeLookOutLeft,
        EyeLookOutRight,
        EyeLookUpLeft,
        EyeLookUpRight,
        EyeSquintLeft,
        EyeSquintRight,
        EyeWideLeft,
        EyeWideRight,
        JawForward,
        JawLeft,
        JawOpen,
        JawRight,
        MouthClose,
        MouthDimpleLeft,
        MouthDimpleRight,
        MouthFrownLeft,
        MouthFrownRight,
        MouthFunnel,
        MouthLeft,
        MouthLowerDownLeft,
        MouthLowerDownRight,
        MouthPressLeft,
        MouthPressRight,
        MouthPucker,
        MouthRight,
        MouthRollLower,
        MouthRollUpper,
        MouthShrugLower,
        MouthShrugUpper,
        MouthSmileLeft,
        MouthSmileRight,
        MouthStretchLeft,
        MouthStretchRight,
        MouthUpperUpLeft,
        MouthUpperUpRight,
        NoseSneerLeft,
        NoseSneerRight,
        FaceBlendshapeName_Max
    }

    public static Queue<string> rcvData_face;
    public Queue<PartsData_face> moveData_face;
    [SerializeField] GameObject RightBrow;
    [SerializeField] GameObject LeftBrow;
    [SerializeField] GameObject RightEye;
    [SerializeField] GameObject LeftEye;
    [SerializeField] GameObject RightPupil;
    [SerializeField] GameObject LeftPupil;
    [SerializeField] GameObject Nose;

    private int hidecnt;
    private Vector3 RightEye_angle_base;
    private Vector3 LeftEye_angle_base;
    private Vector3 Nose_pos_base;
    private Vector3 Nose_angle_base;

    private GameObject facedir1; //顔の中央座標用
    private GameObject facedir2; //顔の回転取得用

    private PartsData_face prev_moveData_face;
    private int rcvdata_devide; //受信データを分割して滑らかにする

    void Start()
    {
        hidecnt = 0;
        rcvdata_devide = 2;
        rcvData_face = new Queue<string>();
        moveData_face = new Queue<PartsData_face>();
        prev_moveData_face = null;
        ChangeVisibility(false);
        RightEye_angle_base = RightEye.transform.localEulerAngles;
        LeftEye_angle_base = LeftEye.transform.localEulerAngles;
        Nose_pos_base = Nose.transform.localPosition;
        Nose_angle_base = Nose.transform.localEulerAngles;

        facedir1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        facedir2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        facedir2.transform.parent = facedir1.transform;
        facedir2.transform.localPosition = new Vector3(-1f, 0f, 0f);
        facedir1.GetComponent<Renderer>().enabled = false;
        facedir2.GetComponent<Renderer>().enabled = false;
    }

    void Update()
    {
        hidecnt++;
        if((rcvData_face != null) && (rcvData_face.Count != 0))
        {
            string rcvdata = rcvData_face.Dequeue();
            PartsData_face rcv_face = JsonUtility.FromJson<PartsData_face>(rcvdata);

            //今回値を前回値の差を、今回値(目標)に向かって数回に分けて移動させる
            if(prev_moveData_face != null)
            {
                PartsData_face div_face = new PartsData_face();
                
                //前回値を開始地点とする
                div_face.parts = prev_moveData_face.parts;
                for(int idx=0; idx<3; idx++)
                {
                    div_face.nose[idx] = prev_moveData_face.nose[idx];
                    div_face.top[idx] = prev_moveData_face.top[idx];
                    div_face.bottom[idx] = prev_moveData_face.bottom[idx];
                    div_face.right[idx] = prev_moveData_face.right[idx];
                    div_face.left[idx] = prev_moveData_face.left[idx];
                }
                for(int idx=(int)FaceBlendshapeName.Neutral; idx<(int)FaceBlendshapeName.FaceBlendshapeName_Max; idx++)
                {
                    div_face.blendshape[idx] = prev_moveData_face.blendshape[idx];
                }

                //今回値と前回値の差分を分割し、キューへ格納する
                for(int div=0; div<rcvdata_devide-1; div++)
                {
                    for(int idx=0; idx<3; idx++)
                    {
                        div_face.nose[idx] += (rcv_face.nose[idx] - prev_moveData_face.nose[idx]) / rcvdata_devide;
                        div_face.top[idx] += (rcv_face.top[idx] - prev_moveData_face.top[idx]) / rcvdata_devide;
                        div_face.bottom[idx] += (rcv_face.bottom[idx] - prev_moveData_face.bottom[idx]) / rcvdata_devide;
                        div_face.right[idx] += (rcv_face.right[idx] - prev_moveData_face.right[idx]) / rcvdata_devide;
                        div_face.left[idx] += (rcv_face.left[idx] - prev_moveData_face.left[idx]) / rcvdata_devide;
                    }
                    for(int idx=(int)FaceBlendshapeName.Neutral; idx<(int)FaceBlendshapeName.FaceBlendshapeName_Max; idx++)
                    {
                        div_face.blendshape[idx] += (rcv_face.blendshape[idx] - prev_moveData_face.blendshape[idx]) / rcvdata_devide;
                    }
                    moveData_face.Enqueue(div_face);
                }
            }
            else
            {
                //ここを実行するのは初回のみ
                prev_moveData_face = new PartsData_face();
            }
            //分割数によっては最終値が中途半端になるため、最後は今回値ぴったりで終わらせる
            moveData_face.Enqueue(rcv_face);
            
            //今回値を前回値として保持
            prev_moveData_face.parts = rcv_face.parts;
            for(int idx=0; idx<3; idx++)
            {
                prev_moveData_face.nose[idx] = rcv_face.nose[idx];
                prev_moveData_face.top[idx] = rcv_face.top[idx];
                prev_moveData_face.bottom[idx] = rcv_face.bottom[idx];
                prev_moveData_face.right[idx] = rcv_face.right[idx];
                prev_moveData_face.left[idx] = rcv_face.left[idx];
            }
            for(int idx=(int)FaceBlendshapeName.Neutral; idx<(int)FaceBlendshapeName.FaceBlendshapeName_Max; idx++)
            {
                prev_moveData_face.blendshape[idx] = rcv_face.blendshape[idx];
            }
        }

        if((moveData_face != null) && (moveData_face.Count != 0))
        {
            PartsData_face face = moveData_face.Dequeue();

            hidecnt = 0;
            //処理順は固定
            NoseMove(face);
            BrowMove_blendshape(face);
            EyeBlink_blendshape(face);
            EyeMove(face);
            MouthMove_blendshape(face);
            ChangeVisibility(true);
        }
        if(hidecnt > 100)
        {
            hidecnt = 0;
            for(int i=(int)FaceBlendshapeName.Neutral; i<(int)FaceBlendshapeName.FaceBlendshapeName_Max; i++)
            {
                ChangeVisibility(false);
            }
            rcvData_face.Clear();
            moveData_face.Clear();
        }
    }

    private void OnApplicationQuit()
    {
        rcvData_face.Clear();
        moveData_face.Clear();
    }

    private void OnDestroy()
    {
        rcvData_face.Clear();
        moveData_face.Clear();
    }

    public static void SetRcvData(string rcvdata)
    {
        rcvData_face.Enqueue(rcvdata);
    }

    private void NoseMove(PartsData_face face)
    {
        Transform Nose_Transform = Nose.transform;

        //鼻の位置を計算する
        Vector3 Nose_pos = Nose_Transform.localPosition;
        Nose_pos.x = Nose_pos_base.x + ((face.nose[0]*6f) * (1f));
        Nose_pos.y = Nose_pos_base.y + ((face.nose[1]*3f) * (-1f));
        Nose_pos.z = Nose_pos_base.z + ((face.nose[2]*100f) * (-1f));
        Nose_Transform.localPosition = Nose_pos;

        //顔の上下左右端の座標から、顔全体の傾きを計算する
        Vector3 top = new Vector3(face.top[0], face.top[1]*(-1f), (face.top[2]*(-1f)));
        Vector3 right = new Vector3(face.right[0], face.right[1]*(-1f), face.right[2]*(-1f));
        Vector3 left = new Vector3(face.left[0], face.left[1]*(-1f), face.left[2]*(-1f));
        Vector3 bottom = new Vector3(face.bottom[0], face.bottom[1]*(-1f), (face.bottom[2]*(-1f)));
        Vector3 center_right_left = Vector3.Lerp(right, left, 0.5f);
        facedir1.transform.position = center_right_left;
        facedir1.transform.LookAt(left, top-bottom);
        facedir2.transform.LookAt(facedir1.transform.position, top-bottom);

        Nose_Transform.eulerAngles = facedir2.transform.eulerAngles;
    }

    private void BrowMove_blendshape(PartsData_face face)
    {
        SkinnedMeshRenderer rightbrow_smr = RightBrow.GetComponent<SkinnedMeshRenderer>();
        SkinnedMeshRenderer leftbrow_smr = LeftBrow.GetComponent<SkinnedMeshRenderer>();
        
        float rightbrow = (face.blendshape[(int)FaceBlendshapeName.BrowInnerUp]) * (120f);
        float leftbrow = (face.blendshape[(int)FaceBlendshapeName.BrowInnerUp]) * (120f);
        if(rightbrow > 100.0f) rightbrow = 100.0f;
        if(leftbrow > 100.0f) leftbrow = 100.0f;

        rightbrow_smr.SetBlendShapeWeight(0, rightbrow);
        leftbrow_smr.SetBlendShapeWeight(0, leftbrow);
    }

    private void EyeBlink_blendshape(PartsData_face face)
    {
        SkinnedMeshRenderer Nose_smr;
        Nose_smr = Nose.GetComponent<SkinnedMeshRenderer>();

        float rightblink = (face.blendshape[(int)FaceBlendshapeName.EyeBlinkRight])*120;
        float leftblink = (face.blendshape[(int)FaceBlendshapeName.EyeBlinkLeft])*120;
        if(rightblink > 100.0f) rightblink = 100.0f;
        if(leftblink > 100.0f) leftblink = 100.0f;

        Nose_smr.SetBlendShapeWeight(2, rightblink);
        Nose_smr.SetBlendShapeWeight(3, leftblink);
    }

    private void EyeMove(PartsData_face face)
    {
        EyeMove_Right(face);
        EyeMove_Left(face);
    }

    private void EyeMove_Right(PartsData_face face)
    {
        float eyemove_data_updown;
        float eyemove_data_inout;

        //上下方向に大きい値の方向へ動かす
        if(face.blendshape[(int)FaceBlendshapeName.EyeLookDownRight] > face.blendshape[(int)FaceBlendshapeName.EyeLookUpRight])
        {
            eyemove_data_updown = face.blendshape[(int)FaceBlendshapeName.EyeLookDownRight];
        }
        else
        {
            eyemove_data_updown = face.blendshape[(int)FaceBlendshapeName.EyeLookUpRight] * (-1f);
        }

        //左右方向に大きい値の方向へ動かす
        if(face.blendshape[(int)FaceBlendshapeName.EyeLookInRight] > face.blendshape[(int)FaceBlendshapeName.EyeLookOutRight])
        {
            eyemove_data_inout = face.blendshape[(int)FaceBlendshapeName.EyeLookInRight] * (-1f);
        }
        else
        {
            eyemove_data_inout = face.blendshape[(int)FaceBlendshapeName.EyeLookOutRight];
        }

        Transform RightEye_Transform = RightEye.transform;
        Vector3 RightEye_angle = RightEye_Transform.localEulerAngles;
        RightEye_angle.x = RightEye_angle_base.x + (eyemove_data_updown*20) + (5f); //上下方向の回転はx軸を中心とする
        RightEye_angle.y = RightEye_angle_base.y + ((eyemove_data_inout*25) * (-1)); //左右方向の回転はy軸を中心とする
        RightEye_Transform.localEulerAngles = RightEye_angle;
    }

    private void EyeMove_Left(PartsData_face face)
    {
        float eyemove_data_updown;
        float eyemove_data_inout;

        //上下方向に大きい値の方向へ動かす
        if(face.blendshape[(int)FaceBlendshapeName.EyeLookDownLeft] > face.blendshape[(int)FaceBlendshapeName.EyeLookUpLeft])
        {
            eyemove_data_updown = face.blendshape[(int)FaceBlendshapeName.EyeLookDownLeft];
        }
        else
        {
            eyemove_data_updown = face.blendshape[(int)FaceBlendshapeName.EyeLookUpLeft] * (-1f);
        }

        //左右方向に大きい値の方向へ動かす
        if(face.blendshape[(int)FaceBlendshapeName.EyeLookInLeft] > face.blendshape[(int)FaceBlendshapeName.EyeLookOutLeft])
        {
            eyemove_data_inout = face.blendshape[(int)FaceBlendshapeName.EyeLookInLeft];
        }
        else
        {
            eyemove_data_inout = face.blendshape[(int)FaceBlendshapeName.EyeLookOutLeft] * (-1f);
        }

        Transform LeftEye_Transform = LeftEye.transform;
        Vector3 LeftEye_angle = LeftEye_Transform.localEulerAngles;
        LeftEye_angle.x = LeftEye_angle_base.x + (eyemove_data_updown*25) + (5f); //上下方向の回転はx軸を中心とする
        LeftEye_angle.y = LeftEye_angle_base.y + ((eyemove_data_inout*25) * (-1)); //左右方向の回転はy軸を中心とする
        LeftEye_Transform.localEulerAngles = LeftEye_angle;
    }

    private void MouthMove_blendshape(PartsData_face face)
    {
        SkinnedMeshRenderer Nose_smr;
        Nose_smr = Nose.GetComponent<SkinnedMeshRenderer>();

        float jowopen = (face.blendshape[(int)FaceBlendshapeName.JawOpen])*120f;
        float mouthstretch = (face.blendshape[(int)FaceBlendshapeName.MouthStretchRight])*10000f;
        if(jowopen > 100.0f) jowopen = 100.0f;
        if(mouthstretch > 100.0f) mouthstretch = 100.0f;

        Nose_smr.SetBlendShapeWeight(0, jowopen);
        Nose_smr.SetBlendShapeWeight(1, mouthstretch);
    }

    private void ChangeVisibility(bool vis)
    {
        RightBrow.GetComponent<Renderer>().enabled = vis;
        LeftBrow.GetComponent<Renderer>().enabled = vis;
        RightEye.GetComponent<Renderer>().enabled = vis;
        LeftEye.GetComponent<Renderer>().enabled = vis;
        RightPupil.GetComponent<Renderer>().enabled = vis;
        LeftPupil.GetComponent<Renderer>().enabled = vis;
        Nose.GetComponent<Renderer>().enabled = vis;
    }
}
}


今回はNoseMove()での、顔の回転制御を変更しました。

    private void NoseMove(PartsData_face face)
    {
        Transform Nose_Transform = Nose.transform;

        //鼻の位置を計算する
        Vector3 Nose_pos = Nose_Transform.localPosition;
        Nose_pos.x = Nose_pos_base.x + ((face.nose[0]*6f) * (1f));
        Nose_pos.y = Nose_pos_base.y + ((face.nose[1]*3f) * (-1f));
        Nose_pos.z = Nose_pos_base.z + ((face.nose[2]*100f) * (-1f));
        Nose_Transform.localPosition = Nose_pos;

        //顔の上下左右端の座標から、顔全体の傾きを計算する
        Vector3 top = new Vector3(face.top[0], face.top[1]*(-1f), (face.top[2]*(-1f)));
        Vector3 right = new Vector3(face.right[0], face.right[1]*(-1f), face.right[2]*(-1f));
        Vector3 left = new Vector3(face.left[0], face.left[1]*(-1f), face.left[2]*(-1f));
        Vector3 bottom = new Vector3(face.bottom[0], face.bottom[1]*(-1f), (face.bottom[2]*(-1f)));
        Vector3 center_right_left = Vector3.Lerp(right, left, 0.5f);
        facedir1.transform.position = center_right_left;
        facedir1.transform.LookAt(left, top-bottom);
        facedir2.transform.LookAt(facedir1.transform.position, top-bottom);

        Nose_Transform.eulerAngles = facedir2.transform.eulerAngles;
    }

顔の回転制御を正確に計算することは難しいので、Vector3.LookAt()を使用します。
Vector3.LookAt()はz軸を指定方向に向かせるものですが、顔の上下左右の座標はほぼ平面のため、z方向を向かせることができません。
そこで、顔の回転制御用に2つのオブジェクトを用意しました。
facedir1
facedir2
facedir2はfacedir1の子オブジェクトとして設定します。facedir2は、facedir1のx軸-1の位置に配置します。
facedir1は顔(の左右座標)の中心に置き、LookAtでz軸を左に向かせます。すると、facedir2は顔の後ろ(後頭部)側に位置することになります。この状態でfacedir2をLookAtでfacedir1に向かせると、facedir2のz軸は鼻先、つまり顔の正面を向くようになります。
こうすることで、facedir2は顔全体の回転を表すことができるようになります。
あとは、facedir2の回転(eulerAngles)をそのまま顔オブジェクトの回転に適用すれば、顔オブジェクトを回転させることができます。

右手パーツ管理

using System.Collections.Generic;
using UnityEngine;

namespace PartsData_righthand_ns{

[System.Serializable]
public class PartsData_righthand
{
    public string parts = "";
    public float[] x = new float[21];
    public float[] y = new float[21];
    public float[] z = new float[21];
}

public class RightHandManager : MonoBehaviour
{
    public enum RightHandLandmarkName
    {
        Wrist = 0,
        Thumb_Cmc,
        Thumb_Mcp,
        Thumb_Ip,
        Thumb_Tip,
        Index_Finger_Mcp,
        Index_Finger_Pip,
        Index_Finger_Dip,
        Index_Finger_Tip,
        Middle_Finger_Mcp,
        Middle_Finger_Pip,
        Middle_Finger_Dip,
        Middle_Finger_Tip,
        Ring_Finger_Mcp,
        Ring_Finger_Pip,
        Ring_Finger_Dip,
        Ring_Finger_Tip,
        Pinky_Finger_Mcp,
        Pinky_Finger_Pip,
        Pinky_Finger_Dip,
        Pinky_Finger_Tip,
        HandLandmarkName_Max
    }

    private int[,] righthaand_CylinderIndexList = new int[21,2]
    {
        {(int)RightHandLandmarkName.Thumb_Mcp, (int)RightHandLandmarkName.Thumb_Cmc},
        {(int)RightHandLandmarkName.Thumb_Ip, (int)RightHandLandmarkName.Thumb_Mcp},
        {(int)RightHandLandmarkName.Thumb_Tip, (int)RightHandLandmarkName.Thumb_Ip},
        {(int)RightHandLandmarkName.Index_Finger_Pip, (int)RightHandLandmarkName.Index_Finger_Mcp},
        {(int)RightHandLandmarkName.Index_Finger_Dip, (int)RightHandLandmarkName.Index_Finger_Pip},
        {(int)RightHandLandmarkName.Index_Finger_Tip, (int)RightHandLandmarkName.Index_Finger_Dip},
        {(int)RightHandLandmarkName.Middle_Finger_Pip, (int)RightHandLandmarkName.Middle_Finger_Mcp},
        {(int)RightHandLandmarkName.Middle_Finger_Dip, (int)RightHandLandmarkName.Middle_Finger_Pip},
        {(int)RightHandLandmarkName.Middle_Finger_Tip, (int)RightHandLandmarkName.Middle_Finger_Dip},
        {(int)RightHandLandmarkName.Ring_Finger_Pip, (int)RightHandLandmarkName.Ring_Finger_Mcp},
        {(int)RightHandLandmarkName.Ring_Finger_Dip, (int)RightHandLandmarkName.Ring_Finger_Pip},
        {(int)RightHandLandmarkName.Ring_Finger_Tip, (int)RightHandLandmarkName.Ring_Finger_Dip},
        {(int)RightHandLandmarkName.Pinky_Finger_Pip, (int)RightHandLandmarkName.Pinky_Finger_Mcp},
        {(int)RightHandLandmarkName.Pinky_Finger_Dip, (int)RightHandLandmarkName.Pinky_Finger_Pip},
        {(int)RightHandLandmarkName.Pinky_Finger_Tip, (int)RightHandLandmarkName.Pinky_Finger_Dip},
        {(int)RightHandLandmarkName.Middle_Finger_Mcp, (int)RightHandLandmarkName.Index_Finger_Mcp},
        {(int)RightHandLandmarkName.Ring_Finger_Mcp, (int)RightHandLandmarkName.Middle_Finger_Mcp},
        {(int)RightHandLandmarkName.Pinky_Finger_Mcp, (int)RightHandLandmarkName.Ring_Finger_Mcp},
        {(int)RightHandLandmarkName.Thumb_Cmc, (int)RightHandLandmarkName.Wrist},
        {(int)RightHandLandmarkName.Index_Finger_Mcp, (int)RightHandLandmarkName.Wrist},
        {(int)RightHandLandmarkName.Pinky_Finger_Mcp, (int)RightHandLandmarkName.Wrist}
    };

    public static Queue<string> rcvData_righthand;
    public Queue<PartsData_righthand> moveData_righthand;
    [SerializeField] GameObject righthand_Wrist;
    [SerializeField] GameObject righthand_Thumb_Cmc;
    [SerializeField] GameObject righthand_Thumb_Mcp;
    [SerializeField] GameObject righthand_Thumb_Ip;
    [SerializeField] GameObject righthand_Thumb_Tip;
    [SerializeField] GameObject righthand_Index_Finger_Mcp;
    [SerializeField] GameObject righthand_Index_Finger_Pip;
    [SerializeField] GameObject righthand_Index_Finger_Dip;
    [SerializeField] GameObject righthand_Index_Finger_Tip;
    [SerializeField] GameObject righthand_Middle_Finger_Mcp;
    [SerializeField] GameObject righthand_Middle_Finger_Pip;
    [SerializeField] GameObject righthand_Middle_Finger_Dip;
    [SerializeField] GameObject righthand_Middle_Finger_Tip;
    [SerializeField] GameObject righthand_Ring_Finger_Mcp;
    [SerializeField] GameObject righthand_Ring_Finger_Pip;
    [SerializeField] GameObject righthand_Ring_Finger_Dip;
    [SerializeField] GameObject righthand_Ring_Finger_Tip;
    [SerializeField] GameObject righthand_Pinky_Finger_Mcp;
    [SerializeField] GameObject righthand_Pinky_Finger_Pip;
    [SerializeField] GameObject righthand_Pinky_Finger_Dip;
    [SerializeField] GameObject righthand_Pinky_Finger_Tip;

    private GameObject[] righthand_objs;

    private GameObject righthand_Cylinder_00;
    private GameObject righthand_Cylinder_01;
    private GameObject righthand_Cylinder_02;
    private GameObject righthand_Cylinder_03;
    private GameObject righthand_Cylinder_04;
    private GameObject righthand_Cylinder_05;
    private GameObject righthand_Cylinder_06;
    private GameObject righthand_Cylinder_07;
    private GameObject righthand_Cylinder_08;
    private GameObject righthand_Cylinder_09;
    private GameObject righthand_Cylinder_10;
    private GameObject righthand_Cylinder_11;
    private GameObject righthand_Cylinder_12;
    private GameObject righthand_Cylinder_13;
    private GameObject righthand_Cylinder_14;
    private GameObject righthand_Cylinder_15;
    private GameObject righthand_Cylinder_16;
    private GameObject righthand_Cylinder_17;
    private GameObject righthand_Cylinder_18;
    private GameObject righthand_Cylinder_19;
    private GameObject righthand_Cylinder_20;

    private GameObject[] righthand_Cylinder_objs;

    private GameObject handdir1; //掌の中央座標用
    private GameObject handdir2; //掌の回転取得用

    private int hidecnt;

    PartsData_righthand prev_moveData_righthand;
    private int rcvdata_devide; //受信データを分割して滑らかにする

    private Vector3[] position_list;

    void Start()
    {
        position_list = new Vector3[(int)RightHandLandmarkName.HandLandmarkName_Max];
        hidecnt = 0;
        rcvdata_devide = 2;
        rcvData_righthand = new Queue<string>();
        moveData_righthand = new Queue<PartsData_righthand>();
        prev_moveData_righthand = null;

        righthand_objs = new GameObject[(int)RightHandLandmarkName.HandLandmarkName_Max];
        righthand_objs[(int)RightHandLandmarkName.Wrist] = righthand_Wrist;
        righthand_objs[(int)RightHandLandmarkName.Thumb_Cmc] = righthand_Thumb_Cmc;
        righthand_objs[(int)RightHandLandmarkName.Thumb_Mcp] = righthand_Thumb_Mcp;
        righthand_objs[(int)RightHandLandmarkName.Thumb_Ip] = righthand_Thumb_Ip;
        righthand_objs[(int)RightHandLandmarkName.Thumb_Tip] = righthand_Thumb_Tip;
        righthand_objs[(int)RightHandLandmarkName.Index_Finger_Mcp] = righthand_Index_Finger_Mcp;
        righthand_objs[(int)RightHandLandmarkName.Index_Finger_Pip] = righthand_Index_Finger_Pip;
        righthand_objs[(int)RightHandLandmarkName.Index_Finger_Dip] = righthand_Index_Finger_Dip;
        righthand_objs[(int)RightHandLandmarkName.Index_Finger_Tip] = righthand_Index_Finger_Tip;
        righthand_objs[(int)RightHandLandmarkName.Middle_Finger_Mcp] = righthand_Middle_Finger_Mcp;
        righthand_objs[(int)RightHandLandmarkName.Middle_Finger_Pip] = righthand_Middle_Finger_Pip;
        righthand_objs[(int)RightHandLandmarkName.Middle_Finger_Dip] = righthand_Middle_Finger_Dip;
        righthand_objs[(int)RightHandLandmarkName.Middle_Finger_Tip] = righthand_Middle_Finger_Tip;
        righthand_objs[(int)RightHandLandmarkName.Ring_Finger_Mcp] = righthand_Ring_Finger_Mcp;
        righthand_objs[(int)RightHandLandmarkName.Ring_Finger_Pip] = righthand_Ring_Finger_Pip;
        righthand_objs[(int)RightHandLandmarkName.Ring_Finger_Dip] = righthand_Ring_Finger_Dip;
        righthand_objs[(int)RightHandLandmarkName.Ring_Finger_Tip] = righthand_Ring_Finger_Tip;
        righthand_objs[(int)RightHandLandmarkName.Pinky_Finger_Mcp] = righthand_Pinky_Finger_Mcp;
        righthand_objs[(int)RightHandLandmarkName.Pinky_Finger_Pip] = righthand_Pinky_Finger_Pip;
        righthand_objs[(int)RightHandLandmarkName.Pinky_Finger_Dip] = righthand_Pinky_Finger_Dip;
        righthand_objs[(int)RightHandLandmarkName.Pinky_Finger_Tip] = righthand_Pinky_Finger_Tip;

        righthand_Cylinder_objs = new GameObject[righthaand_CylinderIndexList.GetLength(0)];
        righthand_Cylinder_objs[0] = righthand_Cylinder_00;
        righthand_Cylinder_objs[1] = righthand_Cylinder_01;
        righthand_Cylinder_objs[2] = righthand_Cylinder_02;
        righthand_Cylinder_objs[3] = righthand_Cylinder_03;
        righthand_Cylinder_objs[4] = righthand_Cylinder_04;
        righthand_Cylinder_objs[5] = righthand_Cylinder_05;
        righthand_Cylinder_objs[6] = righthand_Cylinder_06;
        righthand_Cylinder_objs[7] = righthand_Cylinder_07;
        righthand_Cylinder_objs[8] = righthand_Cylinder_08;
        righthand_Cylinder_objs[9] = righthand_Cylinder_09;
        righthand_Cylinder_objs[10] = righthand_Cylinder_10;
        righthand_Cylinder_objs[11] = righthand_Cylinder_11;
        righthand_Cylinder_objs[12] = righthand_Cylinder_12;
        righthand_Cylinder_objs[13] = righthand_Cylinder_13;
        righthand_Cylinder_objs[14] = righthand_Cylinder_14;
        righthand_Cylinder_objs[15] = righthand_Cylinder_15;
        righthand_Cylinder_objs[16] = righthand_Cylinder_16;
        righthand_Cylinder_objs[17] = righthand_Cylinder_17;
        righthand_Cylinder_objs[18] = righthand_Cylinder_18;
        righthand_Cylinder_objs[19] = righthand_Cylinder_19;
        righthand_Cylinder_objs[20] = righthand_Cylinder_20;

        for(int idx=0; idx<righthaand_CylinderIndexList.GetLength(0); idx++)
        {
            righthand_Cylinder_objs[idx] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            righthand_Cylinder_objs[idx].transform.localScale = new Vector3(0.2f, 0.1f, 0.1f);
        }
        ChangeVisibility(false);

        handdir1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        handdir2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        handdir2.transform.parent = handdir1.transform;
        handdir2.transform.localPosition = new Vector3(1f, 0f, 0f);
        handdir1.GetComponent<Renderer>().enabled = false;
        handdir2.GetComponent<Renderer>().enabled = false;
    }

    void Update()
    {
        hidecnt++;
        if((rcvData_righthand != null) && (rcvData_righthand.Count != 0))
        {
            string rcvdata = rcvData_righthand.Dequeue();
            PartsData_righthand rcv_righthand = JsonUtility.FromJson<PartsData_righthand>(rcvdata);

            //今回値を前回値の差を、今回値(目標)に向かって数回に分けて移動させる
            if(prev_moveData_righthand != null)
            {
                PartsData_righthand div_righthand = new PartsData_righthand();

                //前回値を開始地点とする
                div_righthand.parts = prev_moveData_righthand.parts;
                for(int idx=(int)RightHandLandmarkName.Wrist; idx<(int)RightHandLandmarkName.HandLandmarkName_Max; idx++)
                {
                    div_righthand.x[idx] = prev_moveData_righthand.x[idx];
                    div_righthand.y[idx] = prev_moveData_righthand.y[idx];
                    div_righthand.z[idx] = prev_moveData_righthand.z[idx];
                }

                //今回値と前回値の差分を分割し、キューへ格納する
                for(int div=0; div<rcvdata_devide-1; div++)
                {
                    for(int idx=(int)RightHandLandmarkName.Wrist; idx<(int)RightHandLandmarkName.HandLandmarkName_Max; idx++)
                    {
                        div_righthand.x[idx] += (rcv_righthand.x[idx] - prev_moveData_righthand.x[idx]) / rcvdata_devide;
                        div_righthand.y[idx] += (rcv_righthand.y[idx] - prev_moveData_righthand.y[idx]) / rcvdata_devide;
                        div_righthand.z[idx] += (rcv_righthand.z[idx] - prev_moveData_righthand.z[idx]) / rcvdata_devide;
                    }
                    moveData_righthand.Enqueue(div_righthand);
                }
            }
            else
            {
                //ここを実行するのは初回のみ
                prev_moveData_righthand = new PartsData_righthand();
            }
            //分割数によっては最終値が中途半端になるため、最後は今回値ぴったりで終わらせる
            moveData_righthand.Enqueue(rcv_righthand);

            //今回値を前回値として保持
            prev_moveData_righthand.parts = rcv_righthand.parts;
            for(int idx=(int)RightHandLandmarkName.Wrist; idx<(int)RightHandLandmarkName.HandLandmarkName_Max; idx++)
            {
                prev_moveData_righthand.x[idx] = rcv_righthand.x[idx];
                prev_moveData_righthand.y[idx] = rcv_righthand.y[idx];
                prev_moveData_righthand.z[idx] = rcv_righthand.z[idx];
            }
        }

        if((moveData_righthand != null) && (moveData_righthand.Count != 0))
        {
            PartsData_righthand right_hand = moveData_righthand.Dequeue();
            hidecnt = 0;

            //右手に連動する
            for(int idx=(int)RightHandLandmarkName.Wrist; idx<(int)RightHandLandmarkName.HandLandmarkName_Max; idx++)
            {
                Transform myTransform;
                myTransform = righthand_objs[idx].transform;
                Vector3 pos = myTransform.position;
                pos.x = right_hand.x[idx]*(1f);
                pos.y = right_hand.y[idx]*(-1f);
                pos.z = right_hand.z[idx]*(-1f);
                //pos.x = right_hand.x[idx]*(1f)*(10f)+(-5f);
                //pos.y = right_hand.y[idx]*(-1f)*(6f)+(2f);
                //pos.z = right_hand.z[idx]*(-1f)*(10f)+(5f);
                //myTransform.position = pos;
                position_list[idx] = pos;
            }
            //LandmarkJoint(righthand_objs, righthand_Cylinder_objs);
            HandPosition(righthand_objs, position_list);
            HandAngle(righthand_objs, position_list);
            FingerAngle(righthand_objs, position_list);
            ChangeVisibility(true);
        }
        if(hidecnt > 100)
        {
            hidecnt = 0;
            ChangeVisibility(false);
            rcvData_righthand.Clear();
            moveData_righthand.Clear();
        }
    }

    private void OnApplicationQuit()
    {
        rcvData_righthand.Clear();
        moveData_righthand.Clear();
    }

    private void OnDestroy()
    {
        rcvData_righthand.Clear();
        moveData_righthand.Clear();
    }

    public static void SetRcvData(string rcvdata)
    {
        rcvData_righthand.Enqueue(rcvdata);
    }

    private void LandmarkJoint(GameObject[] hand_objs, GameObject[] cylinder_objs)
    {
        for(int idx=0; idx<righthaand_CylinderIndexList.GetLength(0); idx++)
        {
            cylinder_objs[idx].transform.position = Vector3.Lerp(hand_objs[righthaand_CylinderIndexList[idx,0]].transform.position, hand_objs[righthaand_CylinderIndexList[idx,1]].transform.position, 0.5f);
            cylinder_objs[idx].transform.LookAt(hand_objs[righthaand_CylinderIndexList[idx,1]].transform.position);
            cylinder_objs[idx].transform.localScale = new Vector3(0.1f, 0.1f, (hand_objs[righthaand_CylinderIndexList[idx,0]].transform.position - hand_objs[righthaand_CylinderIndexList[idx,1]].transform.position).magnitude);
        }
    }

    private void HandPosition(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Transform transform = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        Vector3 pos = pos_list[(int)RightHandLandmarkName.Wrist];
        pos.x = (pos_list[(int)RightHandLandmarkName.Wrist].x * 12f) + (-3f);
        pos.y = (pos_list[(int)RightHandLandmarkName.Wrist].y * 12f) + (8f);
        pos.z = (pos_list[(int)RightHandLandmarkName.Thumb_Cmc].z * 20f);
        transform.position = pos;
    }

    private void HandAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Vector3 center_index_pinky = Vector3.Lerp(pos_list[(int)RightHandLandmarkName.Index_Finger_Mcp], pos_list[(int)RightHandLandmarkName.Pinky_Finger_Mcp], 0.5f);
        Vector3 center_center_wrist = Vector3.Lerp(center_index_pinky, pos_list[(int)RightHandLandmarkName.Wrist], 0.5f);
        Vector3 center_index_wrist = Vector3.Lerp(pos_list[(int)RightHandLandmarkName.Index_Finger_Mcp], pos_list[(int)RightHandLandmarkName.Wrist], 0.5f);
        handdir1.transform.position = center_center_wrist;
        handdir1.transform.LookAt(center_index_wrist, center_index_pinky-handdir1.transform.position);
        handdir2.transform.LookAt(handdir1.transform.position, center_index_pinky-handdir2.transform.position);

        hand_objs[(int)RightHandLandmarkName.Wrist].transform.eulerAngles = handdir2.transform.eulerAngles;
    }

    private void FingerAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Transform transform_base;
        Transform transform;
        Vector3 angle;
        Vector3 diff_base;
        Vector3 diff;
        float angle_x;
        float angle_y;
        float angle_z;

        //Thumb Cmc
        transform_base = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Thumb_Mcp].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Thumb_Mcp] - pos_list[(int)RightHandLandmarkName.Thumb_Cmc];
        angle_x = 0f;
        angle_y = ((Vector3.Angle(transform_base.forward, diff)*(1f))*(2f))+(-180f);
        angle_y = Mathf.Clamp(angle_y, -90f, 0f);
        angle_z = ((Vector3.Angle(transform_base.up, diff)*(-1f))*(2f))+(40f);
        angle_z = Mathf.Clamp(angle_z, -90f, 0f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Thumb Mcp
        transform_base = hand_objs[(int)RightHandLandmarkName.Thumb_Mcp].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Thumb_Ip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Thumb_Ip] - pos_list[(int)RightHandLandmarkName.Thumb_Mcp];
        angle_x = Vector3.Angle(transform_base.up, diff)*(1f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Thumb Ip
        transform = hand_objs[(int)RightHandLandmarkName.Thumb_Tip].transform;
        diff_base = pos_list[(int)RightHandLandmarkName.Thumb_Ip] - pos_list[(int)RightHandLandmarkName.Thumb_Mcp];
        diff = pos_list[(int)RightHandLandmarkName.Thumb_Tip] - pos_list[(int)RightHandLandmarkName.Thumb_Ip];
        angle_x = Vector3.Angle(diff_base, diff)*(1f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Mcp
        transform_base = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Index_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Index_Finger_Pip] - pos_list[(int)RightHandLandmarkName.Index_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Pip
        transform_base = hand_objs[(int)RightHandLandmarkName.Index_Finger_Pip].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Index_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Index_Finger_Dip] - pos_list[(int)RightHandLandmarkName.Index_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Dip
        transform = hand_objs[(int)RightHandLandmarkName.Index_Finger_Tip].transform;
        angle = hand_objs[(int)RightHandLandmarkName.Index_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Mcp
        transform_base = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Middle_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Middle_Finger_Pip] - pos_list[(int)RightHandLandmarkName.Middle_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Pip
        transform_base = hand_objs[(int)RightHandLandmarkName.Middle_Finger_Pip].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Middle_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Middle_Finger_Dip] - pos_list[(int)RightHandLandmarkName.Middle_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Dip
        transform = hand_objs[(int)RightHandLandmarkName.Middle_Finger_Tip].transform;
        angle = hand_objs[(int)RightHandLandmarkName.Middle_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Mcp
        transform_base = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Ring_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Ring_Finger_Pip] - pos_list[(int)RightHandLandmarkName.Ring_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Pip
        transform_base = hand_objs[(int)RightHandLandmarkName.Ring_Finger_Pip].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Ring_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Ring_Finger_Dip] - pos_list[(int)RightHandLandmarkName.Ring_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Dip
        transform = hand_objs[(int)RightHandLandmarkName.Ring_Finger_Tip].transform;
        angle = hand_objs[(int)RightHandLandmarkName.Ring_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Mcp
        transform_base = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Pinky_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Pinky_Finger_Pip] - pos_list[(int)RightHandLandmarkName.Pinky_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Pip
        transform_base = hand_objs[(int)RightHandLandmarkName.Pinky_Finger_Pip].transform;
        transform = hand_objs[(int)RightHandLandmarkName.Pinky_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)RightHandLandmarkName.Pinky_Finger_Dip] - pos_list[(int)RightHandLandmarkName.Pinky_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Dip
        transform = hand_objs[(int)RightHandLandmarkName.Pinky_Finger_Tip].transform;
        angle = hand_objs[(int)RightHandLandmarkName.Pinky_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle.x = angle_x;
        angle.y = 0f;
        angle.z = 0f;
        transform.localEulerAngles = angle;
    }

    private void ChangeVisibility(bool vis)
    {
        for(int idx=(int)RightHandLandmarkName.Wrist; idx<(int)RightHandLandmarkName.HandLandmarkName_Max; idx++)
        {
            righthand_objs[idx].GetComponent<Renderer>().enabled = vis;
        }
        for(int idx=0; idx<righthaand_CylinderIndexList.GetLength(0); idx++)
        {
            righthand_Cylinder_objs[idx].GetComponent<Renderer>().enabled = false;
        }
    }
}
}

ランドマーク同士を接続するオブジェクトを生成する処理が、コメントアウトで残っています。
実際には
 HandPosition()
 HandAngle()
 FingerAngle()
を使用し、手のモデルの制御を行います。

    private void HandPosition(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Transform transform = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
        Vector3 pos = pos_list[(int)RightHandLandmarkName.Wrist];
        pos.x = (pos_list[(int)RightHandLandmarkName.Wrist].x * 12f) + (-3f);
        pos.y = (pos_list[(int)RightHandLandmarkName.Wrist].y * 12f) + (8f);
        pos.z = (pos_list[(int)RightHandLandmarkName.Thumb_Cmc].z * 20f);
        transform.position = pos;
    }

手全体の移動を制御します。
今回はMediaPipe側でhand_landmarks(hand_world_landmarksではない)を使用しています。
この場合手の原点はWristで、xとyは移動可能ですが、z軸は変化しません。
でもzも変化してほしいので、zのみThumb_Cmcを使用します。

    private void HandAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Vector3 center_index_pinky = Vector3.Lerp(pos_list[(int)RightHandLandmarkName.Index_Finger_Mcp], pos_list[(int)RightHandLandmarkName.Pinky_Finger_Mcp], 0.5f);
        Vector3 center_center_wrist = Vector3.Lerp(center_index_pinky, pos_list[(int)RightHandLandmarkName.Wrist], 0.5f);
        Vector3 center_index_wrist = Vector3.Lerp(pos_list[(int)RightHandLandmarkName.Index_Finger_Mcp], pos_list[(int)RightHandLandmarkName.Wrist], 0.5f);
        handdir1.transform.position = center_center_wrist;
        handdir1.transform.LookAt(center_index_wrist, center_index_pinky-handdir1.transform.position);
        handdir2.transform.LookAt(handdir1.transform.position, center_index_pinky-handdir2.transform.position);

        hand_objs[(int)RightHandLandmarkName.Wrist].transform.eulerAngles = handdir2.transform.eulerAngles;
    }

手の回転制御です。
顔と同じく、2つのオブジェクトを使用して手全体の回転を行います。

    private void FingerAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
・・・
    }

指の回転制御です。
前回まではランドマークに球体オブジェクトを付けて動かしていましたが、動くのはあくまでも座標だけで、球体自体は回転していません。この制御をそのままアバターに適用してしまうと、手のオブジェクトが不自然に引き伸ばされてしまいます。
そのため、ランドマークから指の角度を算出し、アバターに回転角度を適用する流れとしました。
関節の角度はVector3.Angle()で求めます。
 角度 = Vector3.Angle(基準とするベクトル, 回転対象のベクトル)
関節によって動かし方が異なるので、それぞれ細かく調整しています。
式の後ろに+ーしている調整値がありますが、カメラやアバターの位置、大きさによって変わる可能性があります。

左手パーツ管理

using System.Collections.Generic;
using Mono.Cecil.Cil;
using UnityEngine;

namespace PartsData_lefthand_ns{

[System.Serializable]
public class PartsData_lefthand
{
    public string parts = "";
    public float[] x= new float[21];
    public float[] y= new float[21];
    public float[] z= new float[21];
}

public class LeftHandManager : MonoBehaviour
{
    public enum LeftHandLandmarkName
    {
        Wrist = 0,
        Thumb_Cmc,
        Thumb_Mcp,
        Thumb_Ip,
        Thumb_Tip,
        Index_Finger_Mcp,
        Index_Finger_Pip,
        Index_Finger_Dip,
        Index_Finger_Tip,
        Middle_Finger_Mcp,
        Middle_Finger_Pip,
        Middle_Finger_Dip,
        Middle_Finger_Tip,
        Ring_Finger_Mcp,
        Ring_Finger_Pip,
        Ring_Finger_Dip,
        Ring_Finger_Tip,
        Pinky_Finger_Mcp,
        Pinky_Finger_Pip,
        Pinky_Finger_Dip,
        Pinky_Finger_Tip,
        HandLandmarkName_Max
    }

    private int[,] lefthaand_CylinderIndexList = new int[21,2]
    {
        {(int)LeftHandLandmarkName.Thumb_Mcp, (int)LeftHandLandmarkName.Thumb_Cmc},
        {(int)LeftHandLandmarkName.Thumb_Ip, (int)LeftHandLandmarkName.Thumb_Mcp},
        {(int)LeftHandLandmarkName.Thumb_Tip, (int)LeftHandLandmarkName.Thumb_Ip},
        {(int)LeftHandLandmarkName.Index_Finger_Pip, (int)LeftHandLandmarkName.Index_Finger_Mcp},
        {(int)LeftHandLandmarkName.Index_Finger_Dip, (int)LeftHandLandmarkName.Index_Finger_Pip},
        {(int)LeftHandLandmarkName.Index_Finger_Tip, (int)LeftHandLandmarkName.Index_Finger_Dip},
        {(int)LeftHandLandmarkName.Middle_Finger_Pip, (int)LeftHandLandmarkName.Middle_Finger_Mcp},
        {(int)LeftHandLandmarkName.Middle_Finger_Dip, (int)LeftHandLandmarkName.Middle_Finger_Pip},
        {(int)LeftHandLandmarkName.Middle_Finger_Tip, (int)LeftHandLandmarkName.Middle_Finger_Dip},
        {(int)LeftHandLandmarkName.Ring_Finger_Pip, (int)LeftHandLandmarkName.Ring_Finger_Mcp},
        {(int)LeftHandLandmarkName.Ring_Finger_Dip, (int)LeftHandLandmarkName.Ring_Finger_Pip},
        {(int)LeftHandLandmarkName.Ring_Finger_Tip, (int)LeftHandLandmarkName.Ring_Finger_Dip},
        {(int)LeftHandLandmarkName.Pinky_Finger_Pip, (int)LeftHandLandmarkName.Pinky_Finger_Mcp},
        {(int)LeftHandLandmarkName.Pinky_Finger_Dip, (int)LeftHandLandmarkName.Pinky_Finger_Pip},
        {(int)LeftHandLandmarkName.Pinky_Finger_Tip, (int)LeftHandLandmarkName.Pinky_Finger_Dip},
        {(int)LeftHandLandmarkName.Middle_Finger_Mcp, (int)LeftHandLandmarkName.Index_Finger_Mcp},
        {(int)LeftHandLandmarkName.Ring_Finger_Mcp, (int)LeftHandLandmarkName.Middle_Finger_Mcp},
        {(int)LeftHandLandmarkName.Pinky_Finger_Mcp, (int)LeftHandLandmarkName.Ring_Finger_Mcp},
        {(int)LeftHandLandmarkName.Thumb_Cmc, (int)LeftHandLandmarkName.Wrist},
        {(int)LeftHandLandmarkName.Index_Finger_Mcp, (int)LeftHandLandmarkName.Wrist},
        {(int)LeftHandLandmarkName.Pinky_Finger_Mcp, (int)LeftHandLandmarkName.Wrist}
    };

    public static Queue<string> rcvData_lefthand;
    public Queue<PartsData_lefthand> moveData_lefthand;
    [SerializeField] GameObject lefthand_Wrist;
    [SerializeField] GameObject lefthand_Thumb_Cmc;
    [SerializeField] GameObject lefthand_Thumb_Mcp;
    [SerializeField] GameObject lefthand_Thumb_Ip;
    [SerializeField] GameObject lefthand_Thumb_Tip;
    [SerializeField] GameObject lefthand_Index_Finger_Mcp;
    [SerializeField] GameObject lefthand_Index_Finger_Pip;
    [SerializeField] GameObject lefthand_Index_Finger_Dip;
    [SerializeField] GameObject lefthand_Index_Finger_Tip;
    [SerializeField] GameObject lefthand_Middle_Finger_Mcp;
    [SerializeField] GameObject lefthand_Middle_Finger_Pip;
    [SerializeField] GameObject lefthand_Middle_Finger_Dip;
    [SerializeField] GameObject lefthand_Middle_Finger_Tip;
    [SerializeField] GameObject lefthand_Ring_Finger_Mcp;
    [SerializeField] GameObject lefthand_Ring_Finger_Pip;
    [SerializeField] GameObject lefthand_Ring_Finger_Dip;
    [SerializeField] GameObject lefthand_Ring_Finger_Tip;
    [SerializeField] GameObject lefthand_Pinky_Finger_Mcp;
    [SerializeField] GameObject lefthand_Pinky_Finger_Pip;
    [SerializeField] GameObject lefthand_Pinky_Finger_Dip;
    [SerializeField] GameObject lefthand_Pinky_Finger_Tip;

    GameObject[] lefthand_objs;

    private GameObject lefthand_Cylinder_00;
    private GameObject lefthand_Cylinder_01;
    private GameObject lefthand_Cylinder_02;
    private GameObject lefthand_Cylinder_03;
    private GameObject lefthand_Cylinder_04;
    private GameObject lefthand_Cylinder_05;
    private GameObject lefthand_Cylinder_06;
    private GameObject lefthand_Cylinder_07;
    private GameObject lefthand_Cylinder_08;
    private GameObject lefthand_Cylinder_09;
    private GameObject lefthand_Cylinder_10;
    private GameObject lefthand_Cylinder_11;
    private GameObject lefthand_Cylinder_12;
    private GameObject lefthand_Cylinder_13;
    private GameObject lefthand_Cylinder_14;
    private GameObject lefthand_Cylinder_15;
    private GameObject lefthand_Cylinder_16;
    private GameObject lefthand_Cylinder_17;
    private GameObject lefthand_Cylinder_18;
    private GameObject lefthand_Cylinder_19;
    private GameObject lefthand_Cylinder_20;

    private GameObject[] lefthand_Cylinder_objs;

    private GameObject handdir1; //掌の中央座標用
    private GameObject handdir2; //掌の回転取得用

    private int hidecnt;

    PartsData_lefthand prev_moveData_lefthand;
    private int rcvdata_devide; //受信データを分割して滑らかにする

    private Vector3[] position_list;

    void Start()
    {
        position_list = new Vector3[(int)LeftHandLandmarkName.HandLandmarkName_Max];
        hidecnt = 0;
        rcvdata_devide = 2;
        rcvData_lefthand = new Queue<string>();
        moveData_lefthand = new Queue<PartsData_lefthand>();
        prev_moveData_lefthand = null;

        lefthand_objs = new GameObject[(int)LeftHandLandmarkName.HandLandmarkName_Max];
        lefthand_objs[(int)LeftHandLandmarkName.Wrist] = lefthand_Wrist;
        lefthand_objs[(int)LeftHandLandmarkName.Thumb_Cmc] = lefthand_Thumb_Cmc;
        lefthand_objs[(int)LeftHandLandmarkName.Thumb_Mcp] = lefthand_Thumb_Mcp;
        lefthand_objs[(int)LeftHandLandmarkName.Thumb_Ip] = lefthand_Thumb_Ip;
        lefthand_objs[(int)LeftHandLandmarkName.Thumb_Tip] = lefthand_Thumb_Tip;
        lefthand_objs[(int)LeftHandLandmarkName.Index_Finger_Mcp] = lefthand_Index_Finger_Mcp;
        lefthand_objs[(int)LeftHandLandmarkName.Index_Finger_Pip] = lefthand_Index_Finger_Pip;
        lefthand_objs[(int)LeftHandLandmarkName.Index_Finger_Dip] = lefthand_Index_Finger_Dip;
        lefthand_objs[(int)LeftHandLandmarkName.Index_Finger_Tip] = lefthand_Index_Finger_Tip;
        lefthand_objs[(int)LeftHandLandmarkName.Middle_Finger_Mcp] = lefthand_Middle_Finger_Mcp;
        lefthand_objs[(int)LeftHandLandmarkName.Middle_Finger_Pip] = lefthand_Middle_Finger_Pip;
        lefthand_objs[(int)LeftHandLandmarkName.Middle_Finger_Dip] = lefthand_Middle_Finger_Dip;
        lefthand_objs[(int)LeftHandLandmarkName.Middle_Finger_Tip] = lefthand_Middle_Finger_Tip;
        lefthand_objs[(int)LeftHandLandmarkName.Ring_Finger_Mcp] = lefthand_Ring_Finger_Mcp;
        lefthand_objs[(int)LeftHandLandmarkName.Ring_Finger_Pip] = lefthand_Ring_Finger_Pip;
        lefthand_objs[(int)LeftHandLandmarkName.Ring_Finger_Dip] = lefthand_Ring_Finger_Dip;
        lefthand_objs[(int)LeftHandLandmarkName.Ring_Finger_Tip] = lefthand_Ring_Finger_Tip;
        lefthand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Mcp] = lefthand_Pinky_Finger_Mcp;
        lefthand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Pip] = lefthand_Pinky_Finger_Pip;
        lefthand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Dip] = lefthand_Pinky_Finger_Dip;
        lefthand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Tip] = lefthand_Pinky_Finger_Tip;

        lefthand_Cylinder_objs = new GameObject[lefthaand_CylinderIndexList.GetLength(0)];
        lefthand_Cylinder_objs[0] = lefthand_Cylinder_00;
        lefthand_Cylinder_objs[1] = lefthand_Cylinder_01;
        lefthand_Cylinder_objs[2] = lefthand_Cylinder_02;
        lefthand_Cylinder_objs[3] = lefthand_Cylinder_03;
        lefthand_Cylinder_objs[4] = lefthand_Cylinder_04;
        lefthand_Cylinder_objs[5] = lefthand_Cylinder_05;
        lefthand_Cylinder_objs[6] = lefthand_Cylinder_06;
        lefthand_Cylinder_objs[7] = lefthand_Cylinder_07;
        lefthand_Cylinder_objs[8] = lefthand_Cylinder_08;
        lefthand_Cylinder_objs[9] = lefthand_Cylinder_09;
        lefthand_Cylinder_objs[10] = lefthand_Cylinder_10;
        lefthand_Cylinder_objs[11] = lefthand_Cylinder_11;
        lefthand_Cylinder_objs[12] = lefthand_Cylinder_12;
        lefthand_Cylinder_objs[13] = lefthand_Cylinder_13;
        lefthand_Cylinder_objs[14] = lefthand_Cylinder_14;
        lefthand_Cylinder_objs[15] = lefthand_Cylinder_15;
        lefthand_Cylinder_objs[16] = lefthand_Cylinder_16;
        lefthand_Cylinder_objs[17] = lefthand_Cylinder_17;
        lefthand_Cylinder_objs[18] = lefthand_Cylinder_18;
        lefthand_Cylinder_objs[19] = lefthand_Cylinder_19;
        lefthand_Cylinder_objs[20] = lefthand_Cylinder_20;

        for(int idx=0; idx<lefthaand_CylinderIndexList.GetLength(0); idx++)
        {
            lefthand_Cylinder_objs[idx] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            lefthand_Cylinder_objs[idx].transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
        }
        ChangeVisibility(false);

        handdir1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        handdir2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        handdir2.transform.parent = handdir1.transform;
        handdir2.transform.localPosition = new Vector3(-1f, 0f, 0f);
        handdir1.GetComponent<Renderer>().enabled = false;
        handdir2.GetComponent<Renderer>().enabled = false;
    }

    void Update()
    {
        hidecnt++;
        if((rcvData_lefthand != null) && (rcvData_lefthand.Count != 0))
        {
            string rcvdata = rcvData_lefthand.Dequeue();
            PartsData_lefthand rcv_lefthand = JsonUtility.FromJson<PartsData_lefthand>(rcvdata);

            //今回値を前回値の差を、今回値(目標)に向かって数回に分けて移動させる
            if(prev_moveData_lefthand != null)
            {
                PartsData_lefthand div_lefthand = new PartsData_lefthand();

                //前回値を開始地点とする
                div_lefthand.parts = prev_moveData_lefthand.parts;
                for(int idx=(int)LeftHandLandmarkName.Wrist; idx<(int)LeftHandLandmarkName.HandLandmarkName_Max; idx++)
                {
                    div_lefthand.x[idx] = prev_moveData_lefthand.x[idx];
                    div_lefthand.y[idx] = prev_moveData_lefthand.y[idx];
                    div_lefthand.z[idx] = prev_moveData_lefthand.z[idx];
                }

                //今回値と前回値の差分を分割し、キューへ格納する
                for(int div=0; div<rcvdata_devide-1; div++)
                {
                    for(int idx=(int)LeftHandLandmarkName.Wrist; idx<(int)LeftHandLandmarkName.HandLandmarkName_Max; idx++)
                    {
                        div_lefthand.x[idx] += (rcv_lefthand.x[idx] - prev_moveData_lefthand.x[idx]) / rcvdata_devide;
                        div_lefthand.y[idx] += (rcv_lefthand.y[idx] - prev_moveData_lefthand.y[idx]) / rcvdata_devide;
                        div_lefthand.z[idx] += (rcv_lefthand.z[idx] - prev_moveData_lefthand.z[idx]) / rcvdata_devide;
                    }
                    moveData_lefthand.Enqueue(div_lefthand);
                }
            }
            else
            {
                //ここを実行するのは初回のみ
                prev_moveData_lefthand = new PartsData_lefthand();
            }
            //分割数によっては最終値が中途半端になるため、最後は今回値ぴったりで終わらせる
            moveData_lefthand.Enqueue(rcv_lefthand);
            
            //今回値を前回値として保持
            prev_moveData_lefthand.parts = rcv_lefthand.parts;
            for(int idx=(int)LeftHandLandmarkName.Wrist; idx<(int)LeftHandLandmarkName.HandLandmarkName_Max; idx++)
            {
                prev_moveData_lefthand.x[idx] = rcv_lefthand.x[idx];
                prev_moveData_lefthand.y[idx] = rcv_lefthand.y[idx];
                prev_moveData_lefthand.z[idx] = rcv_lefthand.z[idx];
            }
        }

        if((moveData_lefthand != null) && (moveData_lefthand.Count != 0))
        {
            PartsData_lefthand left_hand = moveData_lefthand.Dequeue();
            hidecnt = 0;

            //左手に連動する
            for(int idx=(int)LeftHandLandmarkName.Wrist; idx<(int)LeftHandLandmarkName.HandLandmarkName_Max; idx++)
            {
                Transform myTransform;
                myTransform = lefthand_objs[idx].transform;
                Vector3 pos = myTransform.position;
                pos.x = left_hand.x[idx]*(1f);
                pos.y = left_hand.y[idx]*(-1f);
                pos.z = left_hand.z[idx]*(-1f);
                //pos.x = left_hand.x[idx]*(1f)*(10f)-(5f);
                //pos.y = left_hand.y[idx]*(-1f)*(6f)+(2f);
                //pos.z = left_hand.z[idx]*(-1f)*(10f)+(5f);
                //myTransform.position = pos;
                position_list[idx] = pos;
            }
            //LandmarkJoint(lefthand_objs, lefthand_Cylinder_objs);
            HandPosition(lefthand_objs, position_list);
            HandAngle(lefthand_objs, position_list);
            FingerAngle(lefthand_objs, position_list);
            ChangeVisibility(true);
        }
        if(hidecnt > 100)
        {
            hidecnt = 0;
            ChangeVisibility(false);
            rcvData_lefthand.Clear();
            moveData_lefthand.Clear();
        }
    }

    private void OnApplicationQuit()
    {
        rcvData_lefthand.Clear();
        moveData_lefthand.Clear();
    }

    private void OnDestroy()
    {
        rcvData_lefthand.Clear();
        moveData_lefthand.Clear();
    }

    public static void SetRcvData(string rcvdata)
    {
        rcvData_lefthand.Enqueue(rcvdata);
    }

    private void LandmarkJoint(GameObject[] hand_objs, GameObject[] cylinder_objs)
    {
        for(int idx=0; idx<lefthaand_CylinderIndexList.GetLength(0); idx++)
        {
            cylinder_objs[idx].transform.position = Vector3.Lerp(hand_objs[lefthaand_CylinderIndexList[idx,0]].transform.position, hand_objs[lefthaand_CylinderIndexList[idx,1]].transform.position, 0.5f);
            cylinder_objs[idx].transform.LookAt(hand_objs[lefthaand_CylinderIndexList[idx,1]].transform.position);
            cylinder_objs[idx].transform.localScale = new Vector3(0.2f, 0.2f, (hand_objs[lefthaand_CylinderIndexList[idx,0]].transform.position - hand_objs[lefthaand_CylinderIndexList[idx,1]].transform.position).magnitude);
        }
    }

    private void HandPosition(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Transform transform = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        Vector3 pos = pos_list[(int)LeftHandLandmarkName.Wrist];
        pos.x = (pos_list[(int)LeftHandLandmarkName.Wrist].x * 12f) + (3f);
        pos.y = (pos_list[(int)LeftHandLandmarkName.Wrist].y * 12f) + (8f);
        pos.z = (pos_list[(int)LeftHandLandmarkName.Thumb_Cmc].z * 20f);
        transform.position = pos;
    }

    private void HandAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Vector3 center_index_pinky = Vector3.Lerp(pos_list[(int)LeftHandLandmarkName.Index_Finger_Mcp], pos_list[(int)LeftHandLandmarkName.Pinky_Finger_Mcp], 0.5f);
        Vector3 center_center_wrist = Vector3.Lerp(center_index_pinky, pos_list[(int)LeftHandLandmarkName.Wrist], 0.5f);
        Vector3 center_index_wrist = Vector3.Lerp(pos_list[(int)LeftHandLandmarkName.Index_Finger_Mcp], pos_list[(int)LeftHandLandmarkName.Wrist], 0.5f);
        handdir1.transform.position = center_center_wrist;
        handdir1.transform.LookAt(center_index_wrist, center_index_pinky-handdir1.transform.position);
        handdir2.transform.LookAt(handdir1.transform.position, center_index_pinky-handdir2.transform.position);

        hand_objs[(int)LeftHandLandmarkName.Wrist].transform.eulerAngles = handdir2.transform.eulerAngles;
    }

    private void FingerAngle(GameObject[] hand_objs, Vector3[] pos_list)
    {
        Transform transform_base;
        Transform transform;
        Vector3 angle;
        Vector3 diff_base;
        Vector3 diff;
        float angle_x;
        float angle_y;
        float angle_z;

        //Thumb Cmc
        transform_base = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Thumb_Mcp].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Thumb_Mcp] - pos_list[(int)LeftHandLandmarkName.Thumb_Cmc];
        angle_x = 0f;
        angle_y = (Vector3.Angle(transform_base.forward, diff)*(-1f)*(2f))+(180f);
        angle_y = Mathf.Clamp(angle_y, 0f, 90f);
        angle_z = ((Vector3.Angle(transform_base.up, diff)*(1f))*(2f))+(-40f);
        angle_z = Mathf.Clamp(angle_z, 0f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Thumb Mcp
        transform_base = hand_objs[(int)LeftHandLandmarkName.Thumb_Mcp].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Thumb_Ip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Thumb_Ip] - pos_list[(int)LeftHandLandmarkName.Thumb_Mcp];
        angle_x = Vector3.Angle(transform_base.up, diff)*(1f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Thumb Ip
        transform = hand_objs[(int)LeftHandLandmarkName.Thumb_Tip].transform;
        diff_base = pos_list[(int)LeftHandLandmarkName.Thumb_Ip] - pos_list[(int)LeftHandLandmarkName.Thumb_Mcp];
        diff = pos_list[(int)LeftHandLandmarkName.Thumb_Tip] - pos_list[(int)LeftHandLandmarkName.Thumb_Ip];
        angle_x = Vector3.Angle(diff_base, diff)*(1f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Mcp
        transform_base = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Index_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Index_Finger_Pip] - pos_list[(int)LeftHandLandmarkName.Index_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Pip
        transform_base = hand_objs[(int)LeftHandLandmarkName.Index_Finger_Pip].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Index_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Index_Finger_Dip] - pos_list[(int)LeftHandLandmarkName.Index_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Index Finger Dip
        transform = hand_objs[(int)LeftHandLandmarkName.Index_Finger_Tip].transform;
        angle = hand_objs[(int)LeftHandLandmarkName.Index_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Mcp
        transform_base = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Middle_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Middle_Finger_Pip] - pos_list[(int)LeftHandLandmarkName.Middle_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Pip
        transform_base = hand_objs[(int)LeftHandLandmarkName.Middle_Finger_Pip].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Middle_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Middle_Finger_Dip] - pos_list[(int)LeftHandLandmarkName.Middle_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Middle Finger Dip
        transform = hand_objs[(int)LeftHandLandmarkName.Middle_Finger_Tip].transform;
        angle = hand_objs[(int)LeftHandLandmarkName.Middle_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Mcp
        transform_base = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Ring_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Ring_Finger_Pip] - pos_list[(int)LeftHandLandmarkName.Ring_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Pip
        transform_base = hand_objs[(int)LeftHandLandmarkName.Ring_Finger_Pip].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Ring_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Ring_Finger_Dip] - pos_list[(int)LeftHandLandmarkName.Ring_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Ring Finger Dip
        transform = hand_objs[(int)LeftHandLandmarkName.Ring_Finger_Tip].transform;
        angle = hand_objs[(int)LeftHandLandmarkName.Ring_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Mcp
        transform_base = hand_objs[(int)LeftHandLandmarkName.Wrist].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Pip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Pinky_Finger_Pip] - pos_list[(int)LeftHandLandmarkName.Pinky_Finger_Mcp];
        angle_x = ((Vector3.Angle(transform_base.forward, diff)*(-1f))*(2f))+(180f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = (Vector3.Angle(transform_base.right, diff)*(1f))+(-90f);
        angle_z = Mathf.Clamp(angle_z, -90f, 90f);
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Pip
        transform_base = hand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Pip].transform;
        transform = hand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Dip].transform;
        angle = transform.localEulerAngles;
        diff = pos_list[(int)LeftHandLandmarkName.Pinky_Finger_Dip] - pos_list[(int)LeftHandLandmarkName.Pinky_Finger_Pip];
        angle_x = Vector3.Angle(transform_base.up, diff)*(0.8f);
        angle_x = Mathf.Clamp(angle_x, 0f, 90f);
        angle_y = 0f;
        angle_z = 0f;
        angle.x = angle_x;
        angle.y = angle_y;
        angle.z = angle_z;
        transform.localEulerAngles = angle;

        //Pinky Finger Dip
        transform = hand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Tip].transform;
        angle = hand_objs[(int)LeftHandLandmarkName.Pinky_Finger_Dip].transform.localEulerAngles;
        angle_x = angle.x * 0.5f;
        angle.x = angle_x;
        angle.y = 0f;
        angle.z = 0f;
        transform.localEulerAngles = angle;
    }

    private void ChangeVisibility(bool vis)
    {
        for(int idx=(int)LeftHandLandmarkName.Wrist; idx<(int)LeftHandLandmarkName.HandLandmarkName_Max; idx++)
        {
            lefthand_objs[idx].GetComponent<Renderer>().enabled = vis;
        }
        for(int idx=0; idx<lefthaand_CylinderIndexList.GetLength(0); idx++)
        {
            lefthand_Cylinder_objs[idx].GetComponent<Renderer>().enabled = false;
        }
    }
}
}

基本構造は右手パーツ管理と同様です。

エディタ設定



オブジェクトがzプラス方向を正面としているので、カメラはそれと正対するように配置します。


右手の割り当てはこうなっています。
手首から第1関節に伸びるボーンはモデルにありませんが、スクリプトには存在するので、ダミーオブジェクトを割り当てています。実体は空のオブジェクトです。

動作確認



ある程度動いてはいますが、手全体の回転によって、関節の角度がおかしくなることがあります。
特に親指が顕著なのですが、おそらく、手全体の回転によってランドマークをうまく取得できない(隠れてしまう)場合に発生するものだと思います。
ただ、角度計算に用いるベクトルや調整値、回転用のオブジェクトの用意など、無理やり実装している部分が多くあります。もっと素直で高精度な計算方法があると思うので、まだまだ改善の余地はありそうです。

ともあれ、それなりに動かせるようにはなりました😁

さいごに

力技で実装した部分が多々ありますが、ある程度アバターとして動かせるようになってよかったです。
ここからどう改善していくか、これから検討していきます。

それでは、今回はここまで。
ありがとうございました😊

コメント

タイトルとURLをコピーしました