UnityでHumanoidアバターをProgramaticallyに動かす方法3つ


Avatarについて

Unityでは、3Dのキャラクターモデルにはボーンの設定がされていることが多いですが、
ボーンの名前はモデルごとに異なり、Programaticallyに扱う際は不便が多いです。
Unityでは、Avatarという仕組みがあり、共通のボーン名にモデルのボーン名をマッピングでき、
共通ボーン名でボーンにアクセスできます。

VRMのボーンは形式上、UnityのAvatarにマッピングされているようなので、
モデルごとのボーン名の違いに煩わされないために、
Avatar共通ボーンの操作のみで済む場合は、共通ボーンを使うようにするべきです。
(もちろんモデル固有のボーンを操作したいなら、Avatarは使えません。)

ボーンに関しては次のような資料があります。
* Avatarに用意されているボーン
* string型でボーンの名前

また、Avatarを生成するAvatarBuilderというものも用意されているようで、
これを使えば、Macanimの共通ボーンへマッピングしたAvatorを作成できます。

Avatorクラスは、HumanDescriptionクラスを持っていて、その中には共通ボーンとモデルのボーンのマッピングや、関節Twiestの伝搬の仕方や、ボーンの角度の制約などがはいっています。
Avatorでは、HumanBoneクラスに標準ボーンとモデルで使われているボーン名とのペア情報が格納されていて、その配列がマッピング情報として使われていますが、その情報に加えて、ボーンの角度の制限も付与できます。
例えば、HumanDescription.lowerArmTwiestは、IKの処理で使われているようです。

共通ボーン名でTransformの操作

Transform.FindでもボーンのTransformの取得と操作は、できるでしょうが、ボーン名を指定して、取得するため、モデルが変わると使えなることがほとんどでしょう。

GetBoneTransformメソッドを使えば、ボーンのTransformを取得して、
回転させたり移動させたりできます。取得したTransformに幾何的な操作、例えば、RotateAroundメソッドの呼び出しやrotation, positionフィールドの変更などをしていけばいいです。

ただ、これらの幾何的な操作を使う場合、PhysXベースの衝突判定がなされないようです。
(幾何学的操作全てかは不明。RotateAroundメソッドでは物理を無視して貫通することを確認。)

例えば、次の文で右肩から肘のボーンを取得できます。

animator.GetBoneTransform(HumanBodyBones.RightLowerArm)

HumanPoseによる操作

MikuMikuAkushuでは、HumanPoseによって、
ヒューマノイドモデルのポーズ変更をしたようです。

物理モデルで動かす

物理モデルを当てはめて、それによりキャラクターを動かす方法もあります。
人の物理モデルとしては様々なモデルが考えられますが、
ここでは、関節をHingeJointでつなげて、HingeJointのMotorでキャラクターの関節を動かす方法を説明します。

HingeJointとはヒンジ(蝶番)で連結されたJointで、マニュアルには、

It is perfect for doors, but can also be used to model chains, pendulums, etc.
と書かれています。

この回答とか面白いです。
HingeJointでモデル作って歩かせようとしています。

HingeJointの入門記事を読んでおいてください。この記事で解説されているように、RigidBody同士をHingeJointでつなげて、HingeJointは1軸に対する回転ができます。この回答のように、programaticallyに回転軸を変えられるみたいですが、動的に毎フレーム変えたりした場合に、物理計算にどう悪影響があるか不明です。パット見問題なさそうでした。

今回は、右手、前腕、上腕、肩の4つにRigidBody Componentを追加して、
手首、肘、肩関節にHingeJoint Componentを追加しています。
Avatorの名前を元に操作したいHingeJointを取るには、
次のようなコードを書けばいいです。これは右手前腕を動かすHingeを取るコードです。

var i = System.Array.IndexOf(HumanTrait.BoneName, "RightLowerArm");
var hingeJoint = GameObject.Find(bone_mapper[i].boneName).GetComponent<HingeJoint>();

このhingeJointに対して、motorを駆動させれば、
肘を動かせます。motorのサンプルコードのとおり、hingeにmotorを代入し直す、useMotorを設定しなおさないとモーターが動きません。hinge.motorのフィールドを直接操作することもできません。