見出し画像

[Unity]動画から読み取った値でUnityChan(Humanoid)の関節をスクリプトから操作する

はじめに

最初は動画から人の姿勢を取得してそこからアニメーションを作ろうかと思いましたが、あまり良い結果が得られそうになかったためスクリプトからの操作としています。

操作対象

UnityChanを使用しています。というかHumanoidです。

© Unity Technologies Japan/UCL

移動目標

人が歩いている動画からPythonのMediaPipeで姿勢の推定を行い、そのデータを使っています。

MediapipeとUnityに関しては以下の方法でやり取りしています。

使用した動画

動画はこちらを使いました。

ステップ1:指定した向きに関節を動かす

まずは簡単に指定した位置に関節が向くようにします。

スクリプトです。

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

public class UntiyChanController : MonoBehaviour
{
   //各関節のトランスフォーム
   public List<Transform> Part;
   //各関節の繋がり
   List<int[]> ConnectionList = new List<int[]>
   {
       new int[]{0, 2},    //0
       new int[]{1, 3},    //1
       new int[]{2, 4},    //2
       new int[]{3, 5},    //3
       null,               //4
       null,               //5
       null,               //6
       null,               //7
       null,               //8
       null,               //9
       null,               //10
       null,               //11
       new int[]{12, 14},  //12
       new int[]{13, 15},  //13
       new int[]{14, 16},  //14
       new int[]{15, 17},  //15
       null,               //16
       null,               //17
       null,               //18
       null,               //19
       null,               //20
       null,               //21
   };
   //各関節のデフォルトの向き
   List<Vector3> DefaultDirList = new List<Vector3>
   {
       new Vector3(-1, 0, 0),      //0
       new Vector3(1, 0, 0),       //1
       new Vector3(-1, 0, 0),      //2
       new Vector3(1, 0, 0),       //3
       Vector3.zero,               //4
       Vector3.zero,               //5
       Vector3.zero,               //6
       Vector3.zero,               //7
       Vector3.zero,               //8
       Vector3.zero,               //9
       Vector3.zero,               //10
       Vector3.zero,               //11
       new Vector3(0, -1, 0),      //12
       new Vector3(0, -1, 0),      //13
       new Vector3(0, -1, 0),      //14
       new Vector3(0, -1, 0),      //15
       Vector3.zero,               //16
       Vector3.zero,               //17
       Vector3.zero,               //18
       Vector3.zero,               //19
       Vector3.zero,               //20
       Vector3.zero,               //21
   };

   List<Quaternion> InverseList = new List<Quaternion>();
   //2つのゲームオブジェクトの位置関係を関節の向きにする
   public Transform FromObject;
   public Transform ToObject;

   void Start()
   {
       //※主な処理
       int ConnectionIndex = -1;
       foreach (int[] Connection in ConnectionList)
       {
           ConnectionIndex++;
           if (Connection == null)
               continue;
           InverseList.Add
           (
               Quaternion.Inverse((Quaternion.Inverse(Part[Connection[0]].rotation) * Quaternion.LookRotation(DefaultDirList[ConnectionIndex])))
               // Quaternion.Inverse((Quaternion.Inverse(Part[Connection[0]].rotation) * Quaternion.LookRotation(Part[Connection[1]].position - Part[Connection[0]].position)))
           );
       }
   }

   void Update()
   {
       int ConnectionIndex = -1;
       int InverseIndex = 0;
       if (Input.GetKeyDown(KeyCode.A))
           foreach (int[] Connection in ConnectionList)
           {
               ConnectionIndex++;
               if (Connection == null)
                   continue;
               //※主な処理
               Part[ConnectionIndex].rotation = Quaternion.LookRotation(ToObject.position - FromObject.position) * InverseList[InverseIndex];

               //足が少しでも反るとつま先の向きが逆になるので、簡単に対処している。
               if (Part[ConnectionIndex].rotation.eulerAngles.y > 180 && ConnectionIndex >= 10)
                   Part[ConnectionIndex].rotation = Quaternion.AngleAxis(180f, ToObject.position - FromObject.position) * Part[ConnectionIndex].rotation;

               InverseIndex++;
           }
       return;
   }
}

PartにはUnityChanの各関節のオブジェクトのトランスフォームが入っています。

スクリーンショット 2022-04-04 192346

各トランスフォームの順番はMediaPipeに合わせています。(顔が0~10なので飛ばし、左腕付け根が11、右腕付け根が12・・・)

主な処理

まずは左腕のみを考えると、最初のTポーズの時点で腕の向いている方向は(-1, 0, 0)とします。これはDefaultDirListに直接書き込んでいます。
また、左腕にはすでに回転が加えられているものとすると、以下の式で考えられます。

(オブジェクトの回転)×(何かしらの回転)=(-1, 0, 0)

ここで”何かしらの回転”を得るには

(何かしらの回転)=(-1, 0, 0)×(オブジェクトの回転)^-1

となります。回転の-1乗はInverse関数で取得できます。
ここから新たに(0, 0, 1)に向けるとすると

(0, 0, 1)×(何かしらの回転)^-1=(オブジェクトの回転)

とすることで左腕が理想の方向に向いてくれます。

Start関数では各関節の(何かしらの回転)^-1をListで保存しています。
Update関数ではオブジェクトのrotationに(0, 0, 1)×(何かしらの回転)^-1を代入するようにしています。

結果

画像2

中央付近の立方体がFrom、動かしている立方体がToとなります。
良い感じに動いてくれています。

ちなみに、コメントアウトに書いているように関節の繋がりの情報からDefaultDirを作ることもできますが、若干のずれがあるため微妙でした。(足が内股になったり)

ステップ2

動画から情報を取って出力する

結果

画像3

とりあえず簡単な動きはできてそうですね。胴体の動きは全く設定はしていません。そこも動くともう少しそれっぽい動きに見えるでしょうか。

結果

とりあえず関節を思うように動かせたとは思いますが、やはり人間味のない動きになってしまいました。体全体を動かせるようにするともう少しマシかもしれないですね。

この記事が気に入ったらサポートをしてみませんか?