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

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

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

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

2回答

877閲覧

HumanoidタイプのAnimationClipで腰の回転を実現したい

user10

総合スコア37

C#

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

0クリップ

投稿2022/08/12 03:32

編集2022/08/12 03:34

アバターにポーズを付けるためMecanimを利用しています。
https://teratail.com/questions/368014でくの字姿勢を起き上がらせる件は解決したのですが、
ポーズをAnimationClipに保存するにあたりではダメなようです。

HumanoidタイプのAnimationClipではHipsの回転を含めようとするとGenericタイプだとダメだとエラーになるため、
解決策を探しています。
その際にHipsを使っても他の回転を使っても方法は問いません。

試したこと

・Hipsのプロパティを追加しましたが上記の通りHumanoidタイプのクリップでは動かないようです。
・ルート回転?animator.rootQのプロパティをクリップに追加しました。体自体は回転します。
ただHipsの回転分をそれに充てる方法がわかりません。

補足情報(FW/ツールのバージョンなど)

どのようなことを実現したいかは下記のプロジェクトで確認にいただければと思います。
https://drive.google.com/file/d/1iPKP2H-OpzbnvpEu5CNRw0NeYECY51QJ/view?usp=sharing

一番上のスライダーで姿勢が変わります。一番下のスライダーでHipsの回転を元の値へ戻します。
そうすると足が着いて起き上がった形になります。それをクリップで保存したいです。

一応、一番下のボタンでクリップを保存できます。

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

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

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

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

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

y_waiwai

2022/08/13 14:07

ダメ、とは、なにがどうなるのか詳しく説明しよう
user10

2022/08/13 16:57

ダメの前に書いてあります
guest

回答2

0

アニメーション周りはリファレンスの説明だけだと謎の部分が多くてやっかいですね...
手探り状態でいじり回して、下記のような形に改変してみました。

C#

1using UnityEngine; 2using System.Collections.Generic; 3using System.IO; 4using UnityEditor; 5 6[RequireComponent(typeof(Animator))] 7public class PoseModifier2 : MonoBehaviour 8{ 9 [SerializeField][Range(-2.0f, 2.0f)] private float spineFrontBack; 10 [SerializeField][Range(-2.0f, 2.0f)] private float chestFrontBack; 11 [SerializeField][Range(-2.0f, 2.0f)] private float upperChestFrontBack; 12 [SerializeField][Range(0.0f, 1.0f)] private float revertHipsTransform; 13 [SerializeField][Range(0.0f, 1.0f)] private float lieOnBack; 14 private Animator animator; 15 private Transform hips; 16 private HumanPose pose; 17 private HumanPoseHandler poseHandler; 18 private Vector3 initialHipsPosition; 19 private Quaternion initialHipsRotation; 20 private Dictionary<int, string> data1 = new Dictionary<int, string>(); 21 private AnimationClip clip; 22 private AnimationClipOverrides clipOverrides; 23 private AnimatorOverrideController overrideAnimetorController; 24 private AnimationCurve curve; 25 26 // OnEnableはご質問者さんの方法をそのまま使用 27 private void OnEnable() 28 { 29 animator = GetComponent<Animator>(); 30 poseHandler = new HumanPoseHandler(animator.avatar, transform); 31 poseHandler.GetHumanPose(ref pose); 32 hips = animator.GetBoneTransform(HumanBodyBones.Hips); 33 initialHipsPosition = hips.localPosition; 34 initialHipsRotation = hips.localRotation; 35 //あらかじめ、AnimatorControllerをアバターにアタッチ、中にはclipが一つ入っていて、そのclipのインスタンスを作成。 36 //インスタンスのclipに任意のポーズを設定し、それを既存のclipに上書きすることによって、LateUpdateに頼らずポーズを設定できる? 37 clip = (AnimationClip)AnimationClip.Instantiate(Resources.Load("clip")); 38 overrideAnimetorController = new AnimatorOverrideController(); 39 overrideAnimetorController.runtimeAnimatorController = Resources.Load<RuntimeAnimatorController>("AC"); 40 curve = new AnimationCurve(); 41 curve.AddKey(0,1); 42 clip.legacy = true; 43 //RootT.yを1にしてアバターが地面に埋まることを回避する 44 clip.SetCurve("", typeof(Animator), "RootT.y", curve); 45 TextAsset csvFile= Resources.Load("HumanTrait.MuscleNames2") as TextAsset; 46 StringReader reader = new StringReader(csvFile.text); 47 //clipにポーズをセットするにあたってプロパティ名がHumanTrait.MuscleNamesと異なる体の部位があるため、体の部位とプロパティ名を対応したデータを作る 48 while (reader.Peek() != -1) 49 { 50 string line = reader.ReadLine(); 51 string[] data=line.Split(','); 52 data1.Add(int.Parse(data[0]),data[2]); 53 } 54 //Clipに何もない状態だとバイクポーズになってしまうため、アバターの初期状態(Tポーズ)を登録する 55 for (int i = 0; i < 95; i++){ 56 AnimationCurve tempcurve=new AnimationCurve(); 57 tempcurve.AddKey(0,pose.muscles[i]); 58 clip.SetCurve("", typeof(Animator), data1[i], tempcurve); 59 } 60 clip.legacy = false; 61 animator.runtimeAnimatorController = overrideAnimetorController; 62 clipOverrides = new AnimationClipOverrides(overrideAnimetorController.overridesCount); 63 overrideAnimetorController.GetOverrides(clipOverrides); 64 65 clipOverrides["clip"]= clip; 66 overrideAnimetorController.ApplyOverrides(clipOverrides); 67 } 68 public void sFB(float newValue){ 69 spineFrontBack=newValue; 70 } 71 public void cFB(float newValue){ 72 chestFrontBack=newValue; 73 } 74 public void uFB(float newValue){ 75 upperChestFrontBack=newValue; 76 } 77 public void rHT(float newValue){ 78 revertHipsTransform=newValue; 79 } 80 public class AnimationClipOverrides : List<KeyValuePair<AnimationClip, AnimationClip>>{ 81 public AnimationClipOverrides(int capacity) : base(capacity) {} 82 83 public AnimationClip this[string name] 84 { 85 get { return Find(x => x.Key.name.Equals(name)).Value; } 86 set 87 { 88 int index = FindIndex(x => x.Key.name.Equals(name)); 89 if (index != -1) 90 this[index] = new KeyValuePair<AnimationClip, AnimationClip>(this[index].Key, value); 91 } 92 } 93 } 94 private void OnDisable() 95 { 96 poseHandler.Dispose(); 97 } 98 private void Update(){} 99 100 private void OnAnimatorIK() 101 { 102 clip.legacy = true; 103 // マッスル書き換え部分は変更なし 104 pose.muscles[0] = spineFrontBack; 105 AnimationCurve tempcurve1=new AnimationCurve(); 106 tempcurve1.AddKey(0,pose.muscles[0]); 107 clip.SetCurve("", typeof(Animator), data1[0], tempcurve1); 108 109 pose.muscles[3] = chestFrontBack; 110 AnimationCurve tempcurve2=new AnimationCurve(); 111 tempcurve2.AddKey(0,pose.muscles[3]); 112 clip.SetCurve("", typeof(Animator), data1[3], tempcurve2); 113 114 pose.muscles[6] = upperChestFrontBack; 115 AnimationCurve tempcurve3=new AnimationCurve(); 116 tempcurve3.AddKey(0,pose.muscles[6]); 117 clip.SetCurve("", typeof(Animator), data1[6], tempcurve3); 118 119 poseHandler.SetHumanPose(ref pose); 120 121 // アニメーションクリップに書き込むHipsの位置は 122 // humanScaleで割って地面に埋まるのを防ぐ 123 hips.localPosition = Vector3.Lerp(hips.localPosition, initialHipsPosition, revertHipsTransform); 124 var clipHipsPosition = hips.localPosition * (1.0f / animator.humanScale); 125 AnimationCurve hipscurve1=new AnimationCurve(); 126 hipscurve1.AddKey(0,clipHipsPosition.x); 127 clip.SetCurve("", typeof(Animator), "RootT.x", hipscurve1); 128 AnimationCurve hipscurve2=new AnimationCurve(); 129 hipscurve2.AddKey(0,clipHipsPosition.y); 130 clip.SetCurve("", typeof(Animator), "RootT.y", hipscurve2); 131 AnimationCurve hipscurve3=new AnimationCurve(); 132 hipscurve3.AddKey(0,clipHipsPosition.z); 133 clip.SetCurve("", typeof(Animator), "RootT.z", hipscurve3); 134 135 // アニメーションクリップに書き込むHipsの回転はlocalRotationそのものではなく、 136 // くの字姿勢から初期姿勢への相対回転とする 137 var clipHipsRotation = Quaternion.Slerp(Quaternion.identity, initialHipsRotation * Quaternion.Inverse(hips.localRotation), revertHipsTransform); 138 hips.localRotation = Quaternion.Slerp(hips.localRotation, initialHipsRotation, revertHipsTransform); 139 AnimationCurve hipscurve4=new AnimationCurve(); 140 hipscurve4.AddKey(0,clipHipsRotation.x); 141 clip.SetCurve("", typeof(Animator), "RootQ.x", hipscurve4); 142 AnimationCurve hipscurve5=new AnimationCurve(); 143 hipscurve5.AddKey(0,clipHipsRotation.y); 144 clip.SetCurve("", typeof(Animator), "RootQ.y", hipscurve5); 145 AnimationCurve hipscurve6=new AnimationCurve(); 146 hipscurve6.AddKey(0,clipHipsRotation.z); 147 clip.SetCurve("", typeof(Animator), "RootQ.z", hipscurve6); 148 AnimationCurve hipscurve7=new AnimationCurve(); 149 hipscurve7.AddKey(0,clipHipsRotation.w); 150 clip.SetCurve("", typeof(Animator), "RootQ.w", hipscurve7); 151 152 transform.localRotation = Quaternion.Euler(Mathf.Lerp(0.0f, -90.0f, lieOnBack), 0.0f, 0.0f); 153 clip.legacy = false; 154 } 155 #if UNITY_EDITOR 156 public void SaveClip() 157 { 158 var newClip = Instantiate(clip); 159 if (File.Exists("Assets/saveclip/clip.anim")) 160 { 161 AssetDatabase.DeleteAsset("Assets/saveclip/clip.anim"); 162 } 163 AssetDatabase.CreateAsset(newClip, "Assets/saveclip/clip.anim"); 164 } 165 #endif 166}

ひとまずは、おじぎ姿勢のアニメーションクリップになったようでした。
ですが足元の位置合わせが不完全で、地面からちょっと浮かんでしまう状態です。

図1

何とかならないかと思って検索してみたところ、いつの間にかHumanPoseHandlerに追加されていたSetInternalAvatarPoseとかGetInternalHumanPoseが使えるかもしれないと思いまして、ちょっと試してみることにしました。

(すみませんが字数がかさんできたためパート2へ移動します...)

投稿2022/08/15 15:05

編集2022/08/15 15:09
Bongo

総合スコア10807

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

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

user10

2022/08/16 04:02

私の記した情報不足で意図が伝えるために加筆する予定でしたが、汲み取っていただけて良かったです。 Hipsの回転差分をQuaternion.Inverseで求めそれをroot回転になんとか充てようとかそういうところで延々と出来ずにはまっていました。 リファレンスや国内外の検索結果をずーっと探して試行錯誤しながら結構な時間がかかっても糸口も見えなかったのに「方法があるのか」と 驚きました回答者様の知見の広さ、専門知識の深さはすごいとしか言いようがありません。 >>リファレンスの説明だけだと謎の部分が多くてやっかいですね そうですね。 英語ができないので自動翻訳に頼っていますがHumanPoseHandlerの説明をただみたきりではさっぱりです。 見当をつけて実際動かして確かめるパターンしか自分にはないので、ざっくり見出しの説明ではスルーしていって見逃してる 場合かなり多いと思います。 素晴らしい回答をありがとうございます。
guest

0

ベストアンサー

パート2

リファレンスの説明が簡素で、はたしてこんな使い方でいいのか自信はないのですが、下記PoseModifier3を作成して...

C#

1using System.Collections.Generic; 2using System.IO; 3using Unity.Collections; 4using UnityEditor; 5using UnityEngine; 6 7[RequireComponent(typeof(Animator))] 8public class PoseModifier3 : MonoBehaviour 9{ 10 [SerializeField][Range(-2.0f, 2.0f)] private float spineFrontBack; 11 [SerializeField][Range(-2.0f, 2.0f)] private float chestFrontBack; 12 [SerializeField][Range(-2.0f, 2.0f)] private float upperChestFrontBack; 13 [SerializeField][Range(0.0f, 1.0f)] private float revertHipsTransform; 14 [SerializeField][Range(0.0f, 1.0f)] private float lieOnBack; 15 private Transform hips; 16 private HumanPose pose; 17 private HumanPoseHandler poseHandler; 18 private Vector3 initialHipsPosition; 19 private Quaternion initialHipsRotation; 20 private readonly Dictionary<int, string> data1 = new Dictionary<int, string>(); 21 private AnimationClip clip; 22 private AnimationClipOverrides clipOverrides; 23 private AnimatorOverrideController overrideAnimatorController; 24 private AnimationCurve curve; 25 26 public void sFB(float newValue) 27 { 28 spineFrontBack = newValue; 29 } 30 31 public void cFB(float newValue) 32 { 33 chestFrontBack = newValue; 34 } 35 36 public void uFB(float newValue) 37 { 38 upperChestFrontBack = newValue; 39 } 40 41 public void rHT(float newValue) 42 { 43 revertHipsTransform = newValue; 44 } 45 46 private void OnEnable() 47 { 48 // 初期化処理はPoseModifier2と同様に行う 49 var animator = GetComponent<Animator>(); 50 poseHandler = new HumanPoseHandler(animator.avatar, transform); 51 poseHandler.GetHumanPose(ref pose); 52 hips = animator.GetBoneTransform(HumanBodyBones.Hips); 53 initialHipsPosition = hips.localPosition; 54 initialHipsRotation = hips.localRotation; 55 clip = (AnimationClip)Instantiate(Resources.Load("clip")); 56 overrideAnimatorController = new AnimatorOverrideController(); 57 overrideAnimatorController.runtimeAnimatorController = Resources.Load<RuntimeAnimatorController>("AC"); 58 curve = new AnimationCurve(); 59 curve.AddKey(0, 1); 60 clip.legacy = true; 61 clip.SetCurve("", typeof(Animator), "RootT.y", curve); 62 var csvFile = Resources.Load("HumanTrait.MuscleNames2") as TextAsset; 63 var reader = new StringReader(csvFile.text); 64 while (reader.Peek() != -1) 65 { 66 var line = reader.ReadLine(); 67 var data = line.Split(','); 68 data1.Add(int.Parse(data[0]), data[2]); 69 } 70 for (var i = 0; i < 95; i++) 71 { 72 curve.MoveKey(0, new Keyframe(0, pose.muscles[i])); 73 clip.SetCurve("", typeof(Animator), data1[i], curve); 74 } 75 clip.legacy = false; 76 animator.runtimeAnimatorController = overrideAnimatorController; 77 clipOverrides = new AnimationClipOverrides(overrideAnimatorController.overridesCount); 78 overrideAnimatorController.GetOverrides(clipOverrides); 79 clipOverrides["clip"] = clip; 80 overrideAnimatorController.ApplyOverrides(clipOverrides); 81 } 82 83 private void OnDisable() 84 { 85 poseHandler.Dispose(); 86 } 87 88 private void OnAnimatorIK() 89 { 90 // ポーズ修正はマッスルだけにとどめ、まだHips回転はアニメーションクリップへ書き込まない 91 pose.muscles[0] = spineFrontBack; 92 curve.MoveKey(0, new Keyframe(0, pose.muscles[0])); 93 clip.SetCurve("", typeof(Animator), data1[0], curve); 94 pose.muscles[3] = chestFrontBack; 95 curve.MoveKey(0, new Keyframe(0, pose.muscles[3])); 96 clip.SetCurve("", typeof(Animator), data1[3], curve); 97 pose.muscles[6] = upperChestFrontBack; 98 curve.MoveKey(0, new Keyframe(0, pose.muscles[6])); 99 clip.SetCurve("", typeof(Animator), data1[6], curve); 100 poseHandler.SetHumanPose(ref pose); 101 hips.localPosition = Vector3.Lerp( 102 hips.localPosition, 103 initialHipsPosition, 104 revertHipsTransform); 105 hips.localRotation = Quaternion.Slerp( 106 hips.localRotation, 107 initialHipsRotation, 108 revertHipsTransform); 109 transform.localRotation = Quaternion.Euler(Mathf.Lerp(0.0f, -90.0f, lieOnBack), 0.0f, 0.0f); 110 } 111 112 public class AnimationClipOverrides : List<KeyValuePair<AnimationClip, AnimationClip>> 113 { 114 public AnimationClipOverrides(int capacity) : base(capacity) 115 { 116 } 117 118 public AnimationClip this[string name] 119 { 120 get { return Find(x => x.Key.name.Equals(name)).Value; } 121 set 122 { 123 var index = FindIndex(x => x.Key.name.Equals(name)); 124 if (index != -1) 125 { 126 this[index] = new KeyValuePair<AnimationClip, AnimationClip>(this[index].Key, value); 127 } 128 } 129 } 130 } 131 132 #if UNITY_EDITOR 133 private static void ParseAvatarTransformRecursive( 134 Transform t, 135 string parentPath, 136 List<string> jointPaths, 137 List<Transform> transforms) 138 { 139 var jointPath = parentPath.Length == 0 ? t.gameObject.name : $"{parentPath}/{t.gameObject.name}"; 140 jointPaths.Add(jointPath); 141 transforms.Add(t); 142 foreach (Transform child in t) 143 { 144 ParseAvatarTransformRecursive(child, jointPath, jointPaths, transforms); 145 } 146 } 147 148 private static void ParseAvatarRootTransform( 149 Transform rootTransform, 150 List<string> jointPaths, 151 List<Transform> transforms) 152 { 153 jointPaths.Add(""); 154 transforms.Add(rootTransform); 155 foreach (Transform t in rootTransform) 156 { 157 ParseAvatarTransformRecursive(t, "", jointPaths, transforms); 158 } 159 } 160 161 public void SaveClip() 162 { 163 // このオブジェクトを起点とする子孫オブジェクトを取得し、併せてそれらへのパス文字列を作成する 164 var animator = GetComponent<Animator>(); 165 var avatar = animator!.avatar; 166 var jointPaths = new List<string>(); 167 var avatarTransforms = new List<Transform>(); 168 ParseAvatarRootTransform(transform, jointPaths, avatarTransforms); 169 170 // 位置・回転からポーズへの変換用のHumanPoseHandlerを作成する 171 using var poseConverter = new HumanPoseHandler(avatar, jointPaths.ToArray()); 172 173 // NativeArrayを作成し、子孫オブジェクトの位置・回転データを詰める 174 var avatarPose = new NativeArray<float>(jointPaths.Count * 7, Allocator.Temp); 175 for (var i = 0; i < jointPaths.Count; i++) 176 { 177 var position = avatarTransforms[i].localPosition; 178 var rotation = avatarTransforms[i].localRotation; 179 var j = i * 7; 180 avatarPose[j] = position.x; 181 avatarPose[j + 1] = position.y; 182 avatarPose[j + 2] = position.z; 183 avatarPose[j + 3] = rotation.x; 184 avatarPose[j + 4] = rotation.y; 185 avatarPose[j + 5] = rotation.z; 186 avatarPose[j + 6] = rotation.w; 187 } 188 189 // 子孫オブジェクトの位置・回転データをもとにHumanPoseを作成する 190 poseConverter.SetInternalAvatarPose(avatarPose); 191 var humanPose = new HumanPose(); 192 poseConverter.GetInternalHumanPose(ref humanPose); 193 avatarPose.Dispose(); 194 195 // 作成したポーズデータをアニメーションクリップに書き込む 196 var newClip = Instantiate(clip); 197 newClip.legacy = true; 198 for (var i = 0; i < humanPose.muscles.Length; i++) 199 { 200 curve.MoveKey(0, new Keyframe(0, humanPose.muscles[i])); 201 newClip.SetCurve("", typeof(Animator), data1[i], curve); 202 } 203 var p = humanPose.bodyPosition; 204 var r = humanPose.bodyRotation; 205 curve.MoveKey(0, new Keyframe(0, p.x)); 206 newClip.SetCurve("", typeof(Animator), "RootT.x", curve); 207 curve.MoveKey(0, new Keyframe(0, p.y)); 208 newClip.SetCurve("", typeof(Animator), "RootT.y", curve); 209 curve.MoveKey(0, new Keyframe(0, p.z)); 210 newClip.SetCurve("", typeof(Animator), "RootT.z", curve); 211 curve.MoveKey(0, new Keyframe(0, r.x)); 212 newClip.SetCurve("", typeof(Animator), "RootQ.x", curve); 213 curve.MoveKey(0, new Keyframe(0, r.y)); 214 newClip.SetCurve("", typeof(Animator), "RootQ.y", curve); 215 curve.MoveKey(0, new Keyframe(0, r.z)); 216 newClip.SetCurve("", typeof(Animator), "RootQ.z", curve); 217 curve.MoveKey(0, new Keyframe(0, r.w)); 218 newClip.SetCurve("", typeof(Animator), "RootQ.w", curve); 219 newClip.legacy = false; 220 221 // アニメーションクリップを保存する 222 if (File.Exists("Assets/saveclip/clip.anim")) 223 { 224 AssetDatabase.DeleteAsset("Assets/saveclip/clip.anim"); 225 } 226 AssetDatabase.CreateAsset(newClip, "Assets/saveclip/clip.anim"); 227 228 // 足元の位置を合わせるため、Root Transform Position (XZ)をオンにする 229 var serializedClip = new SerializedObject(newClip); 230 serializedClip.FindProperty("m_AnimationClipSettings.m_KeepOriginalPositionXZ").boolValue = true; 231 serializedClip.ApplyModifiedProperties(); 232 } 233 #endif 234}

PoseModifier2と入れ替えてアニメーションクリップを出力したところ、どうやら体を屈曲させた姿勢でも足元を接地できたように見えました。

図2

余談ですが、この方法なら体を寝かせて腹筋運動ポーズをとらせることもできそうな感じです。

図3

投稿2022/08/15 15:05

Bongo

総合スコア10807

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問