概要
スクリプトで3Dキャラクター(Humanoid)を動かす場合、動かしたいHumanoidのavaterからHumanPoseHandler(各ボーンの状態を読み書きできるハンドラー)を作成します。
HumanPoseHandlerの値をHumanPose(各ボーンの状態が保存された構造体?)に適用し、HumanPoseを任意の値に変更した後、HumanPoseをHumanPoseHandlerにセットする事で、ボーンの変更が適用されます。
よくわからないので実際のプログラムを見ます。
サンプル
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HumanAnimation : MonoBehaviour
{
[SerializeField]
private Animator _animationTarget;
private HumanPose _targetHumanPose;
void Update ()
{
SampleAnimation();
}
private void SampleAnimation()
{
if (_animationTarget == null)
{
Debug.Log("Animatorがアタッチされてません");
return;
}
// Animator avater → handler → HumanPose と渡す
// HumanPose の値を変更して、handlerへ渡すとHumanBoneに変更が適用される。
var handler = new HumanPoseHandler(_animationTarget.avatar, _animationTarget.transform);
handler.GetHumanPose(ref _targetHumanPose);
//HumanPoseを更新
int muscles = _targetHumanPose.muscles.Length;
for (int i = 0; i < muscles; i++)
{
float move = 0.02f * Time.deltaTime; //moveの係数だけ変化します
_targetHumanPose.muscles[i] = _targetHumanPose.muscles[i] + move;
//Debug.Log(i + " : " + _targetHumanPose.muscles[i]);
}
//変更を適用する場合… SetHumanPose で変更した HumanPose を渡す
handler.SetHumanPose(ref _targetHumanPose);
}
[ContextMenu("HumanPoseの配列に対応したリグ名を表示")]
private void ShowMuscleList()
{
string[] muscleName = HumanTrait.MuscleName;
int i = 0;
while (i < HumanTrait.MuscleCount) {
Debug.Log(i + " : " + muscleName[i]);
i++;
}
}
}
_animationTarget
ボーンの制御を行いたい3Dキャラクター(Humanoid)をアタッチします。
_targetHumanPose(HumanPose)
HumanPose - スクリプトリファレンス / HumanPoseHandler - スクリプトリファレンス
この値を変更する事で、キャラクターのボーンを変更できます。
ポイントなのが 直接ボーンのtransformを制御するわけじゃない という点です。
HumanPoseは -1~1の値が入っています。なので、扱う値は正規化済みなので色々と楽です。
→ HumanPose に Mathf.Sin(Time.deltatime * 2 * Mathf.PI * 0.1f) とか入れると周期運動する。
また、変化量の1次元情報のみを扱うので回転の処理など考える必要がありません。
_targetHumanPose.muscles[i] = _targetHumanPose.muscles[i] * 何かの係数;
というような書き方をする事ができます。
分かりやすい所でいくと _targetHumanPose.muscles[] で指のボーンを指定してやれば、係数をスライダーで変更して指を開閉させる…なんて事ができます。
var handler = new HumanPoseHandler(_animationTarget.avatar, _animationTarget.transform);
handler.GetHumanPose(ref _targetHumanPose);
HumanPoseHandlerでハンドラーをインスタンス化し、ハンドラーからHumanPoseへ参照渡しします。
最終的に変更した HumanPose(_targetHumanPose) を handler.SetHumanPose(ref _targetHumanPose); で適用してやるとキャラクターの姿勢が定まります。
ShowMuscleList()
[ContextMenu()]を書いてやると、Unityを実行しなくてもメソッドを実行できます。
この場合、HumanPoseのどの配列に何のボーンが割り当てられてるか?の一覧を表示します。
まとめ
あとは難しいベクトル計算とかして、それらの値を使ってHumanPoseを制御すると自動化などができるようになる…!
また、VRIKなどで動かしたこれらの値を、すくりぷたぶるおぶじぇくとに保存する事で疑似モーションキャプチャーを作れたりします。
高額のモーションキャプチャーであればドライバの方にモーション記録機能がついてたりしますが、ViveでVtuberをやってる人は自前でモーションキャプチャーを作ってみるのも面白いかもしれません。
(VRで演技して、第三者視点からカメラ付けとかできるので)
参考
Unity、Oculus(HTC Vive)、VRIKで簡易モーションキャプチャを作った話
UniRXで書いてあって初心者の私には読めない…