おっしゃることは何となくイメージできるのですが、意図を取り違えている部分もあるかもしれませんので、適宜カスタマイズしてみてください...
あるフレームから次のフレームにかけてルートオブジェクトが回転した時、その回転軸ベクトルをねじれ軸...つまり腕の軸に射影してやれば、そのルートオブジェクトの回転のうちどれだけがねじれ軸周り回転成分であるか分かるのではないでしょうか?
こんなスクリプトをルートオブジェクトにアタッチし、インスペクタでSegments
に3つの子オブジェクトをセット、それぞれのねじれ解消率を1、0.5、0にして...
C#
1using System;
2using UnityEngine;
3#if UNITY_EDITOR
4using UnityEditor;
5#endif
6
7[ExecuteInEditMode]
8public class CounterTwister : MonoBehaviour
9{
10 public Segment[] Segments;
11 private Quaternion previousRotation;
12 private Quaternion[] currentSegmentRotations = new Quaternion[0];
13
14 private void OnEnable()
15 {
16 this.previousRotation = this.transform.rotation;
17 }
18
19 private void Update()
20 {
21 var rootTransform = this.transform;
22 var currentRotation = rootTransform.rotation;
23
24 // 前のフレームとのルートオブジェクトの回転差分を求める
25 var deltaRotation = currentRotation * Quaternion.Inverse(this.previousRotation);
26 float deltaRotationAngle;
27 Vector3 deltaRotationAxis;
28 deltaRotation.ToAngleAxis(out deltaRotationAngle, out deltaRotationAxis);
29
30 // 回転量が0なら何もする必要はない
31 if (deltaRotationAngle == 0.0f)
32 {
33 return;
34 }
35
36 var segmentCount = this.Segments.Length;
37 if (this.currentSegmentRotations.Length < segmentCount)
38 {
39 Array.Resize(ref this.currentSegmentRotations, segmentCount);
40 }
41
42 // 今回は各セグメントが親子関係を作っているので、ねじれを解消する過程で
43 // セグメントを回転させると、他のセグメントも回転してしまう
44 // そこで、ねじれ解消に入る前に各セグメントの現在の回転を覚えておく
45 for (var i = 0; i < segmentCount; i++)
46 {
47 var segmentTransform = this.Segments[i].Transform;
48 if (segmentTransform != null)
49 {
50 this.currentSegmentRotations[i] = segmentTransform.rotation;
51 }
52 }
53
54 // ねじれ解消処理を行う
55 // Segmentsを0から順番に操作していますので、Segmentsには親子関係がルートに近いセグメントほど
56 // 若い番号になるようTransformをセットしてください
57 // さもないと、せっかく設定した子の回転が親の回転設定時に崩れてしまうかもしれません
58 for (var i = 0; i < segmentCount; i++)
59 {
60 var segment = this.Segments[i];
61 var segmentTransform = segment.Transform;
62 var weight = segment.Weight;
63
64 // セグメントが未設定なら何もする必要はない
65 if (segmentTransform == null)
66 {
67 continue;
68 }
69
70 // ねじれを抽出する軸...さしあたりルートに対するセグメントの相対位置の方角としました
71 var axis = (segmentTransform.position - rootTransform.position).normalized;
72
73 // ルートの差分回転のうち、セグメントのねじれに寄与する成分の割合を求める
74 var twistFactor = Vector3.Dot(deltaRotationAxis, axis);
75
76 // ねじれ軸周りの逆回転を作成する
77 // 回転角にセグメントごとに決めたWeightを掛け、Weightが1に近いほどねじれが解消されるようにする
78 var counterTwist = Quaternion.AngleAxis(-deltaRotationAngle * twistFactor * weight, axis);
79
80 // セグメントに回転を適用する
81 segmentTransform.rotation = counterTwist * this.currentSegmentRotations[i];
82 }
83
84 this.previousRotation = currentRotation;
85 }
86
87 [Serializable]
88 public struct Segment
89 {
90 public Transform Transform;
91 [Range(0.0f, 1.0f)] public float Weight;
92 }
93
94 // おまけ機能...インスペクタにセグメントのlocalRotationを無回転にするボタンを追加する
95 // 差分回転からねじれ成分のみ抽出してセグメント回転を調整するようにした都合上、ルートの回転を無回転に戻しても
96 // セグメントの回転が元に戻るとは限らなくなりました
97 // いちいちセグメントを選択して戻すのが面倒でしたので、一括設定するボタンを設けました
98#if UNITY_EDITOR
99 [CustomEditor(typeof(CounterTwister))]
100 public class CounterTwisterEditor : Editor
101 {
102 public override void OnInspectorGUI()
103 {
104 if (GUILayout.Button("Set Segment Local Rotations to Zero"))
105 {
106 var counterTwister = target as CounterTwister;
107
108 foreach (var segment in counterTwister.Segments)
109 {
110 var segmentTransform = segment.Transform;
111 if (segmentTransform != null)
112 {
113 segmentTransform.localRotation = Quaternion.identity;
114 }
115 }
116 }
117
118 this.DrawDefaultInspector();
119 }
120 }
121#endif
122}
シーンビューで操作してみると、下図のように腕を振る動きではねじれ解消回転が発生せず、腕をねじる動きをした時にはねじれ解消回転が起こるようになりました。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/14 04:43