Edited at

【Unity】スクリプトからキャラクターのボーンを制御する準備 (備忘録)


概要

スクリプトで3Dキャラクター(Humanoid)を動かす場合、動かしたいHumanoidのavaterからHumanPoseHandler(各ボーンの状態を読み書きできるハンドラー)を作成します。

HumanPoseHandlerの値をHumanPose(各ボーンの状態が保存された構造体?)に適用し、HumanPoseを任意の値に変更した後、HumanPoseをHumanPoseHandlerにセットする事で、ボーンの変更が適用されます。

よくわからないので実際のプログラムを見ます。


サンプル


HumanAnimation.cs


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で書いてあって初心者の私には読めない…

ランタイムでAvatarを生成してアニメーションに利用する

Animator - スクリプトリファレンス