質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2804閲覧

Unityでスクリプトから、Animatorコンポーネントの「Chest-Twist」などの値を取得する方法

yuto_onizakura

総合スコア52

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/01/14 07:08

編集2019/01/15 17:34

UnityのAnimationコンポーネントを触っていると、例えばHumanoidボーンが適応されているアバターですと、
腕や足の動きを「Chest Twist」や「Hand In-Out」なんかの値で制御することになりますが、
各項目の現在の値をスクリプトから参照するにはどうすればいいでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エディター用スクリプトでならModelImporter経由でHumanDescriptionを取ってくることができ、その内容から設定値を調べることができそうでした。

Editorフォルダ内に下記のようなスクリプトを入れ、

C#

1using UnityEditor; 2using UnityEngine; 3 4public class HumanDescriptionExtractor : MonoBehaviour 5{ 6 [MenuItem("Utility/Print HumanDescription")] 7 public static void PrintHumanDescription() 8 { 9 var model = Selection.activeGameObject; 10 var path = AssetDatabase.GetAssetPath(model); 11 var importer = AssetImporter.GetAtPath(path) as ModelImporter; 12 if (importer == null) 13 { 14 return; 15 } 16 17 var humanDescription = importer.humanDescription; 18 Debug.Log($"HumanDescription for {model.name}:"); 19 Debug.Log("\tPer-Muscle Settings:"); 20 foreach (var humanBone in humanDescription.human) 21 { 22 var humanName = humanBone.humanName; 23 24 // ボーン名一覧を見て、対応するボーン番号を見つける 25 var boneIndex = -1; 26 for (var j = 0; j < HumanTrait.BoneCount; j++) 27 { 28 if (HumanTrait.BoneName[j] == humanName) 29 { 30 boneIndex = j; 31 break; 32 } 33 } 34 35 if (boneIndex < 0) 36 { 37 continue; 38 } 39 40 // ボーンインデックスの各軸に対応するマッスル番号を見つける 41 var muscleIndexX = HumanTrait.MuscleFromBone(boneIndex, 0); 42 var muscleIndexY = HumanTrait.MuscleFromBone(boneIndex, 1); 43 var muscleIndexZ = HumanTrait.MuscleFromBone(boneIndex, 2); 44 45 // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい? 46 var limit = humanBone.limit; 47 var useDefault = limit.useDefaultValues; 48 var min = useDefault 49 ? new Vector3( 50 muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexX) : limit.min.x, 51 muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexY) : limit.min.y, 52 muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexZ) : limit.min.z) 53 : limit.min; 54 var max = useDefault 55 ? new Vector3( 56 muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.max.x, 57 muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.max.y, 58 muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.max.z) 59 : limit.max; 60 61 // カスタム値が使われている場合は青文字で表示することにしました 62 // インスペクタで範囲を個別に編集すると、その項目のuseDefaultValuesがfalseになるらしい? 63 var minMaxColor = useDefault ? "<color=black>" : "<color=blue>"; 64 if (muscleIndexX >= 0) 65 { 66 Debug.Log( 67 $"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>"); 68 } 69 70 if (muscleIndexY >= 0) 71 { 72 Debug.Log( 73 $"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>"); 74 } 75 76 if (muscleIndexZ >= 0) 77 { 78 Debug.Log( 79 $"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>"); 80 } 81 } 82 83 Debug.Log("\tAdditional Settings:"); 84 Debug.Log($"\t\tUpper Arm Twist:{humanDescription.upperArmTwist}"); 85 Debug.Log($"\t\tLower Arm Twist:{humanDescription.lowerArmTwist}"); 86 Debug.Log($"\t\tUpper Leg Twist:{humanDescription.upperLegTwist}"); 87 Debug.Log($"\t\tLower Leg Twist:{humanDescription.lowerLegTwist}"); 88 Debug.Log($"\t\tArm Stretch:{humanDescription.armStretch}"); 89 Debug.Log($"\t\tLeg Stretch:{humanDescription.legStretch}"); 90 Debug.Log($"\t\tFeet Spacing:{humanDescription.feetSpacing}"); 91 Debug.Log($"\t\tTranslation DoF:{humanDescription.hasTranslationDoF}"); 92 } 93}

プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
Ethan
エディタスクリプトでは何とかなりそうでしたが、実際の実行時にAvatarからHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
逆に、実行時にHumanDescriptionをもとにAvatarを作ることはAvatarBuilderを使えば可能らしいんですがね...

#追記
UnityEditorに頼らず実行時に値を取得する方法はないものか調べてみたものの、結局AvatarからのHumanDescription取得は困難そうでしたのであきらめました。
代案として、モデルのマッスルを最大・最小まで動かし、部位の回転を調べることでマッスル可動範囲の上限・下限を求められないかと思い、ちょっと試してみました。

モデルに対して特定のマッスル値を持つポーズを取らせるのは、HumanPoseHandlerを使えば可能なようです。そこで、各マッスルを1つずつ動かして、回転が起こった部位の角度変化を見てみました。
下記スクリプトをモデルにアタッチして実行したところ...

C#

1using System.Linq; 2using UnityEngine; 3 4[RequireComponent(typeof(Animator))] 5public class MuscleRangeInvestigator : MonoBehaviour 6{ 7 private void Start() 8 { 9 var animator = this.GetComponent<Animator>(); 10 var avatar = animator.avatar; 11 Debug.Log($"Muscle ranges of {avatar.name}:"); 12 13 // マッスル名一覧を取得 14 var muscleNames = HumanTrait.MuscleName; 15 var muscleCount = HumanTrait.MuscleCount; 16 17 // アバターと紐付いたポーズハンドラーを作成 18 var poseHandler = new HumanPoseHandler(avatar, this.transform); 19 20 // 現在のポーズを覚えておく 21 // 後で姿勢を復元する際に、リターゲッティング処理の都合上姿勢がずれる可能性が 22 // あるらしいので、ポーズを覚える前に親・位置・回転をそれぞれ覚えておき 23 // 親子関係を一旦解除、位置・回転もゼロにする 24 var initialLocalPosition = this.transform.localPosition; 25 var initialLocalRotation = this.transform.localRotation; 26 var initialParent = this.transform.parent; 27 this.transform.SetParent(null); 28 this.transform.localPosition = Vector3.zero; 29 this.transform.localRotation = Quaternion.identity; 30 var initialPose = new HumanPose(); 31 poseHandler.GetHumanPose(ref initialPose); 32 33 // マッスル値をいじって実験するためのポーズを作る 34 var pose = new HumanPose 35 { 36 bodyPosition = Vector3.zero, 37 bodyRotation = Quaternion.identity, 38 muscles = new float[initialPose.muscles.Length] 39 }; 40 41 // モデルルート以下の全Transformを配列化 42 var transforms = this.GetComponentsInChildren<Transform>(); 43 44 // ゼロ姿勢(膝や腕を半曲げにした、あの姿勢)をセット 45 poseHandler.SetHumanPose(ref pose); 46 47 // ゼロ姿勢でのローカル回転を覚えておく 48 var zeroTransforms = transforms.Select(t => (Transform: t, Rotation: t.localRotation)).ToArray(); 49 50 // 全マッスルについて... 51 for (var i = 0; i < muscleCount; i++) 52 { 53 // まずi番目マッスルを1にし、姿勢を適用 54 pose.muscles[i] = 1.0f; 55 poseHandler.SetHumanPose(ref pose); 56 57 // 回転が発生したTransformを抜き出し、おそらくそれらのうち 58 // 最大の角度変化量を可動角度上限として採用すればいいと思う...? 59 var max = zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation) 60 .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation)) 61 .OrderByDescending(a => a).FirstOrDefault(); 62 63 // 引き続きi番目マッスルを-1にし、姿勢を適用 64 pose.muscles[i] = -1.0f; 65 poseHandler.SetHumanPose(ref pose); 66 67 // 同じく最大の角度変化量を求め、符号を反転して可動角度下限として採用...? 68 var min = -zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation) 69 .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation)) 70 .OrderByDescending(a => a).FirstOrDefault(); 71 72 // コンソールに結果を表示 73 Debug.Log($"\t{muscleNames[i]}: {min:F1}~{max:F1}"); 74 75 // i番目マッスルを0に戻す 76 pose.muscles[i] = 0.0f; 77 } 78 79 // 姿勢を元に戻して終わり 80 poseHandler.SetHumanPose(ref initialPose); 81 this.transform.SetParent(initialParent); 82 this.transform.localPosition = initialLocalPosition; 83 this.transform.localRotation = initialLocalRotation; 84 } 85}

下図のように、どうやらそれらしい値が得られました。ですが少々ごまかしがありまして、表示される値を小数点以下第1位で丸めています。実際の値はインスペクタ上の値とぴったり一致はしませんでした。おそらく計算誤差によるものでしょう...
EthanAvatar
※おまけ
各マッスルを動かしているときにそれぞれどのような姿勢になっているか見てみたところ、下図のようにEthanとユニティちゃんが体操する姿を撮影できました。ちょうどインスペクタ上の各マッスルのプレビュースライダーを操作したときと同様の動きをしていることがお分かりいただけるでしょうか?
体操

投稿2019/01/16 20:16

編集2019/01/20 21:35
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問