はじめに
Humanoid型アバターの姿勢変更について調べたメモです。
Humanoid型アバターの姿勢を変更する方法として
私が把握しているのは次の2つです。
・マッスル値を変更する
・ボーンの角度を変更する
今回はマッスル値による姿勢の変更について書きます。
ちなみにボーンの角度変更はこちらで可能です。
マッスル値とは?
Unity上ではmusclesという値です。
マッスル値は、文字通り筋肉の収縮を制御するイメージです。
-1 ~ +1の値となっており、人間の骨格ルールが適用されたものになっています。
例えば、左膝(Left Lower Leg Stretch)の設定を変えた場合はこのようになります。
・マッスル値 -1
限界まで曲げる
・マッスル値 0
-1と+1の中間
・マッスル値 +1
限界まで伸ばす
このように -1 〜 +1 の値の範囲であれば、
骨格的にありえないポーズにならないようにしてくれる仕組みです。
ちなみに使いどころはあまり無いかと思いますが、
-1 〜 +1を超えた入力もできます。
(例)マッスル値 -1.62
足がヤバい方向に・・・
マッスル値の種類ですが、こちらの95個から構成されています。
gist.github.com
スクリプトからのマッスル値変更
では、スクリプトからマッスル値を変更していきます。
具体例
サンプルから載せます。
ボタンを押すことで現状のポーズを取得し、
スライダーで左足のマッスル値を-1~+1の範囲で変更しています。
前提
・アバターが設置されている
・アバターにAnimatorが設定されている
・Animatorにアニメーションが設定されている
・ボタンが設置されている
・スライダーが設置されている
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MusclesTest : MonoBehaviour { public Slider slider;//操作するスライダーを割り当て スライダーの値は-1~+1の範囲設定にして下さい。 public Animator animator;//対象のキャラクターのAnimatorコンポーネントを割り当て private HumanPoseHandler humanPoseHandler; private HumanPose humanpose; private bool isSetPose = false;//ポーズがセットされたかどうか //この関数をボタンに割り付け public void AAA() { humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform); humanpose = new HumanPose(); humanPoseHandler.GetHumanPose(ref humanpose);//現状のマッスル値を取得 なぜかアバターのY方向が180度回転する トランスフォームの値が変わるわけではない humanPoseHandler.SetHumanPose(ref humanpose);//マッスル値をセット GetHumanPoseで回転→SetHumanPose回転しない→GetHumanPoseで回転するので回転を相殺するため GetHumanPoseを2回呼ぶ ※GetHumanPoseを連続しても変わらない humanPoseHandler.GetHumanPose(ref humanpose);//現状のマッスル値を取得 animator.enabled = false;//Animatorをオフ Animatorで実行しているアニメーションが優先されるためオフにする isSetPose = true;//マッスル値を取得してからtrueにする } void Update() { if (isSetPose == true) { //マッスル値の値を変更 //今回は配列[21]でLeft Upper Leg Front-Backを動かす。 //musclesの配列番号の対応はこちらを参考 //https://gist.github.com/neon-izm/0637dac7a29682de916cecc0e8b037b0#file-humantrait-musclenames-csv humanpose.muscles[21] = Mathf.Clamp(slider.value, -1.0f, 1.0f); humanPoseHandler.SetHumanPose(ref humanpose);//マッスル値をセット } } }
このコードで動かしたものがこちら
ブログ用 マッスル値の変更
— まる|ダンスドリームMV & ポーズミキサーAR【公式】 (@Maru60014236) 2022年3月8日
→ボタン
現状ポーズの取得
→スライダー(範囲設定 -1 〜 +1)
「Left Upper Leg Front-Back」の変更 pic.twitter.com/DBgup1EPi6
処理流れ
処理の流れです。
マッスル値は、アバターが持っている
HumanPoseHandlerの中のHumanPoseの中に入っています。
なので、下記のように処理します。
1.アバターからHumanPoseHandlerを取得
2.HumanPoseHandlerからHumanPoseを取得
3.HumanPoseが持っているマッスル値を変更
4.変更したマッスル値を適用
1.アバターからHumanPoseHandlerを取得
HumanPoseHandlerを取得するのに必要な
第一引数:Avatar
第二引数:Transform
の情報をアバターに割りついているAnimatorから取得します。
HumanPoseHandler humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform);
2.HumanPoseHandlerからHumanPoseを取得
HumanPoseHandlerからポーズ情報を取得します。
取得はGetHumanPosed(引数はHumanPose型)で行います。
humanPoseHandler.GetHumanPose(ref humanpose);
refは参照渡しの意味なので、これを実行することでhumanposeの変数が更新されます。
3.HumanPoseが持っているマッスル値を変更
マッスル値の変更は下記で変更しています。
humanpose.muscles[マッスル値の配列番号] = マッスル値の値;
今回のサンプルコードでは、
配列番号[21]のLeft Upper Leg Front-Back のマッスル値を変更しています。
また、Mathf.Clampクラスを使用し、
下限の値と上限の値を設定しています。
(スライダーの範囲設定を-1~+1にしているので、
今回はMatuf.Clampは無くても同じ挙動になります。)
humanpose.muscles[21] = Mathf.Clamp(slider.value, -1.0f, 1.0f);
4.変更したマッスル値を適用
最後にHumanPoseHandlerに設定したポーズ情報を反映します。
反映は、SetHumanPose(引数はHumanPose型)で行います。
humanPoseHandler.SetHumanPose(ref humanpose);
refは参照渡しの意味なので、これを実行することでhumanposeの変数が更新されます。
追記 マッスル値列挙型
マッスル値の列挙型を載せておきます。
// マッスル値の列挙型 public enum Muscle_TYPE { SpineFrontBack = 0, SpineLeftRight = 1, SpineTwistLeftRight = 2, ChestFrontBack = 3, ChestLeftRight = 4, ChestTwistLeftRight = 5, UpperChestFrontBack = 6, UpperChestLeftRight = 7, UpperChestTwistLeftRight = 8, NeckNodDownUp = 9, NeckTiltLeftRight = 10, NeckTurnLeftRight = 11, HeadNodDownUp = 12, HeadTiltLeftRight = 13, HeadTurnLeftRight = 14, LeftEyeDownUp = 15, LeftEyeInOut = 16, RightEyeDownUp = 17, RightEyeInOut = 18, JawClose = 19, JawLeftRight = 20, LeftUpperLegFrontBack = 21, LeftUpperLegInOut = 22, LeftUpperLegTwistInOut = 23, LeftLowerLegStretch = 24, LeftLowerLegTwistInOut = 25, LeftFootUpDown = 26, LeftFootTwistInOut = 27, LeftToesUpDown = 28, RightUpperLegFrontBack = 29, RightUpperLegInOut = 30, RightUpperLegTwistInOut = 31, RightLowerLegStretch = 32, RightLowerLegTwistInOut = 33, RightFootUpDown = 34, RightFootTwistInOut = 35, RightToesUpDown = 36, LeftShoulderDownUp = 37, LeftShoulderFrontBack = 38, LeftArmDownUp = 39, LeftArmFrontBack = 40, LeftArmTwistInOut = 41, LeftForearmStretch = 42, LeftForearmTwistInOut = 43, LeftHandDownUp = 44, LeftHandInOut = 45, RightShoulderDownUp = 46, RightShoulderFrontBack = 47, RightArmDownUp = 48, RightArmFrontBack = 49, RightArmTwistInOut = 50, RightForearmStretch = 51, RightForearmTwistInOut = 52, RightHandDownUp = 53, RightHandInOut = 54, LeftThumb1Stretched = 55, LeftThumbSpread = 56, LeftThumb2Stretched = 57, LeftThumb3Stretched = 58, LeftIndex1Stretched = 59, LeftIndexSpread = 60, LeftIndex2Stretched = 61, LeftIndex3Stretched = 62, LeftMiddle1Stretched = 63, LeftMiddleSpread = 64, LeftMiddle2Stretched = 65, LeftMiddle3Stretched = 66, LeftRing1Stretched = 67, LeftRingSpread = 68, LeftRing2Stretched = 69, LeftRing3Stretched = 70, LeftLittle1Stretched = 71, LeftLittleSpread = 72, LeftLittle2Stretched = 73, LeftLittle3Stretched = 74, RightThumb1Stretched = 75, RightThumbSpread = 76, RightThumb2Stretched = 77, RightThumb3Stretched = 78, RightIndex1Stretched = 79, RightIndexSpread = 80, RightIndex2Stretched = 81, RightIndex3Stretched = 82, RightMiddle1Stretched = 83, RightMiddleSpread = 84, RightMiddle2Stretched = 85, RightMiddle3Stretched = 86, RightRing1Stretched = 87, RightRingSpread = 88, RightRing2Stretched = 89, RightRing3Stretched = 90, RightLittle1Stretched = 91, RightLittleSpread = 92, RightLittle2Stretched = 93, RightLittle3Stretched = 94 }
謎
GetHumanPoseをするとキャラクターの向きが180度反転します。
・キャラクターのトランスフォームの値は変わっていない
・humanpose.bodyRotationでキャラクターの回転情報?を見てみると
同じポーズでもGetHumanPoseをするたびに数値が変わっていました。
今は無理矢理2回呼び出して修正している感じです。
改善点ご存知の方がおられましたらご指導お願い致します。
余談
アバターの設定をミスったときによくみるこの中腰のポーズ
これはマッスル値の値がすべて0のときのポーズになっています。
ずっと「なんでこのポーズなんだろう?」と思っていたので謎が解けてスッキリしました。
Unity本を出版しました!
突然ですが、Unity本を出版しました!
こちらを読むことで、スクリプトの基礎固めができます!
現在、kindle unlimitedで読み放題設定中です。今のうちにどうぞ!
もっと早く教えてほしかった!Unity C#入門