こんな感じでどうでしょう。
C#
1using UnityEngine;
2
3public class PQRotator : MonoBehaviour
4{
5 private void Start()
6 {
7 Transform P = GameObject.Find("P").GetComponent<Transform>();
8 Transform Q = GameObject.Find("Q").GetComponent<Transform>();
9
10 Transform A = GameObject.Find("A").GetComponent<Transform>();
11 Transform B = GameObject.Find("B").GetComponent<Transform>();
12
13 A.position = Vector3.zero;
14 A.localScale = Vector3.one;
15 A.eulerAngles = new Vector3(12, 34, 56);
16
17 // 初期状態でのP、Qのグローバル座標
18 Vector3 OG = new Vector3(3.4f, 5.6f, 7.8f);
19
20 P.SetParent(A);
21 P.position = OG;
22
23 B.position = Vector3.zero;
24 B.localScale = Vector3.one;
25 B.eulerAngles = new Vector3(78, 90, 12);
26
27 Q.SetParent(B);
28 Q.position = OG;
29
30 // 多少の計算誤差には目をつぶるとして、下記4つの座標はすべてOG...(3.4, 5.6, 7.8)を示すはず
31 Debug.LogFormat("Pワールド座標 : {0}", P.position.ToString("F6"));
32 Debug.LogFormat("A回転 * Pローカル座標 : {0}", (A.rotation * P.localPosition).ToString("F6"));
33 Debug.LogFormat("Qワールド座標 : {0}", Q.position.ToString("F6"));
34 Debug.LogFormat("B回転 * Qローカル座標 : {0}", (B.rotation * Q.localPosition).ToString("F6"));
35
36 // 以下、コメント中の「==」は計算誤差を許容して一致することを意味するものと見なしてください
37
38 // P.position == A.rotation * P.localPosition であるので、両辺に左側からAの逆回転をかけると
39 // Quaternion.Inverse(A.rotation) * P.Position == Quaternion.Inverse(A.rotation) * A.rotation * P.localPosition となり
40 // 右辺の順回転・逆回転は打ち消し合ってQuaternion.identityとなるため
41 // Quaternion.Inverse(A.rotation) * P.position == P.localPosition となる
42 // つまり、P.localPosition == Quaternion.Inverse(A.rotation) * OG である...①
43
44 // また、同じくQについても Quaternion.Inverse(B.rotation) * Q.position == Q.localPosition となる
45 // つまり、Q.localPosition == Quaternion.Inverse(B.rotation) * OG である...②
46
47 // Pのローカル座標に適用したい回転
48 Quaternion RP = Quaternion.Euler(12, 34, 56);
49
50 // Pの新しいローカル座標
51 Vector3 NewPL = RP * P.localPosition;
52
53 // Pの新しいグローバル座標
54 Vector3 NewPG = A.rotation * NewPL;
55
56 // Qのローカル座標に適用するべき回転をRQ、回転を適用して得られた新しいローカル座標をNewQL、
57 // 新しいグローバル座標をNewQGとすると、NewQG == NewPG であって欲しいので
58 // NewQG == B.rotation * NewQL == NewPG == A.rotation * NewPL という関係となる
59 // NewPL = RP * P.localPosition 、NewQL == RQ * Q.localPosition であるので、これを上式に代入し
60 // B.rotation * RQ * Q.localPosition == A.rotation * RP * P.localPosition となる
61
62 // ここで、両辺に左側からBの逆回転をかけて左辺のB.rotationを消すと
63 // RQ * Q.localPosition == Quaternion.Inverse(B.rotation) * A.rotation * RP * P.localPosition
64 // さらに上式に、先ほど示した①、②を代入すると
65 // RQ * Quaternion.Inverse(B.rotation) * OG == Quaternion.Inverse(B.rotation) * A.rotation * RP * Quaternion.Inverse(A.rotation) * OG
66 // 次に、両辺に右側からBの順回転をかけて左辺のQuaternion.Inverse(B.rotation)を消すと
67 // RQ * OG == Quaternion.Inverse(B.rotation) * A.rotation * RP * Quaternion.Inverse(A.rotation) * B.rotation * OG
68
69 // これで両辺のVector3因子はどちらもOGになったので消すことができ
70 // RQ == Quaternion.Inverse(B.rotation) * A.rotation * RP * Quaternion.Inverse(A.rotation) * B.rotation となる...③
71
72 // ところで、上式の Quaternion.Inverse(B.rotation) * A.rotation と Quaternion.Inverse(A.rotation) * B.rotation は
73 // 2つの回転がそれぞれ逆回転になり、さらに回転順序が逆になったものなので、お互いに逆回転の関係にある
74
75 // そこで、B逆回転とA順回転を組み合わせた回転をRBiAとすると
76 Quaternion RBiA = Quaternion.Inverse(B.rotation) * A.rotation;
77
78 // A逆回転とB順回転を組み合わせた回転は、RBiAの逆回転...Quaternion.Inverse(RBiA) である
79
80 // これらを③に代入すれば式がすっきりし
81 Quaternion RQ = RBiA * RP * Quaternion.Inverse(RBiA);
82
83 // よって、Qの新しいローカル座標は
84 Vector3 NewQL = RQ * Q.localPosition;
85
86 // 求めた回転を適用して、結果を確認してみる
87
88 Debug.Log("----------------");
89
90 Debug.LogFormat("古いPローカル座標 : {0}", P.localPosition.ToString("F6"));
91 Debug.LogFormat("古いPグローバル座標 : {0}", P.position.ToString("F6"));
92
93 Debug.LogFormat("Pローカル座標に適用する回転 : {0}", RP.eulerAngles.ToString("F6"));
94
95 P.localPosition = RP * P.localPosition;
96
97 Debug.LogFormat("新しいPローカル座標 : {0}", P.localPosition.ToString("F6"));
98 Debug.LogFormat("新しいPグローバル座標 : {0}", P.position.ToString("F6"));
99
100 Debug.Log("----------------");
101
102 Debug.LogFormat("古いQローカル座標 : {0}", Q.localPosition.ToString("F6"));
103 Debug.LogFormat("古いQグローバル座標 : {0}", Q.position.ToString("F6"));
104
105 Debug.LogFormat("Qローカル座標に適用する回転 : {0}", RQ.eulerAngles.ToString("F6"));
106
107 Q.localPosition = RQ * Q.localPosition;
108
109 Debug.LogFormat("新しいQローカル座標 : {0}", Q.localPosition.ToString("F6"));
110 Debug.LogFormat("新しいQグローバル座標 : {0}", Q.position.ToString("F6"));
111 }
112}
うまくいきましたでしょうか?ちょっと誤差はでるものの、おおむね一致するかと思います。数学の問題みたいで面白いですね。
注意するべき点としては、クォータニオン型の因子は掛ける順序が非可換なので、方程式を変形していく際にクォータニオンを両辺に掛ける時は、左辺が右からなら右辺も右から、左からなら左から...となるようにすることでしょうか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/08/28 06:07