teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

念のため角度算出過程にProjectOnPlaneを追加

2020/12/10 20:57

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -97,16 +97,20 @@
97
97
  this.joint.connectedBody.transform.InverseTransformDirection(
98
98
  this.transform.TransformDirection(this.joint.axis));
99
99
  this.polarAxis =
100
+ Vector3.ProjectOnPlane(
100
- this.joint.connectedBody.transform.InverseTransformDirection(
101
+ this.joint.connectedBody.transform.InverseTransformDirection(
101
- this.transform.TransformDirection(this.joint.secondaryAxis));
102
+ this.transform.TransformDirection(this.joint.secondaryAxis)),
103
+ this.pole);
102
104
  }
103
105
 
104
106
  // また、現在のsecondaryAxisの向きをconnectedBodyの座標系に変換して
105
107
  // polarAxisの向きと比較することで角度の変化を算出するプロパティを用意しておく
106
108
  private float Azimuth => Vector3.SignedAngle(
107
109
  this.polarAxis,
110
+ Vector3.ProjectOnPlane(
108
- this.joint.connectedBody.transform.InverseTransformDirection(
111
+ this.joint.connectedBody.transform.InverseTransformDirection(
109
- this.transform.TransformDirection(this.joint.secondaryAxis)),
112
+ this.transform.TransformDirection(this.joint.secondaryAxis)),
113
+ this.pole),
110
114
  this.pole);
111
115
 
112
116
  // そしてロック状態を操作するためのプロパティを作る

1

ロック時にリミットを更新する案を追記

2020/12/10 20:57

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -69,4 +69,117 @@
69
69
 
70
70
  すみませんが、そもそも`swapBodies`がどういう役割を持つのかよく分からないです。リファレンスの説明から想像すると「オブジェクトAにジョイントをアタッチしてオブジェクトBと接続した状態で`swapBodies`をオンにすると、オブジェクトBにジョイントをアタッチしてオブジェクトAと接続した状態と同じ挙動をする」といった感じでしょうかね?
71
71
 
72
- 実際のところは何をやっているのかを追おうとしても、こういった物理シミュレーション関連はほとんどがネイティブコードになっていて([UnityCsReference/Dynamics.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHub](https://github.com/Unity-Technologies/UnityCsReference/blob/master/Modules/Physics/ScriptBindings/Dynamics.bindings.cs))手詰まりになり、結局謎のままでした。まあともかく、この両`Rigidbody`の関係を入れ替える過程のどこかに初期姿勢を再設定する部分があるのかもしれません。
72
+ 実際のところは何をやっているのかを追おうとしても、こういった物理シミュレーション関連はほとんどがネイティブコードになっていて([UnityCsReference/Dynamics.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHub](https://github.com/Unity-Technologies/UnityCsReference/blob/master/Modules/Physics/ScriptBindings/Dynamics.bindings.cs))手詰まりになり、結局謎のままでした。まあともかく、この両`Rigidbody`の関係を入れ替える過程のどこかに初期姿勢を再設定する部分があるのかもしれません。
73
+
74
+ ##ロック時にリミットを更新する案
75
+
76
+ 実験用のスクリプトを下記のように変更しました。
77
+
78
+ ```C#
79
+ using UnityEngine;
80
+ using UnityEngine.UI;
81
+
82
+ [RequireComponent(typeof(ConfigurableJoint), typeof(Rigidbody))]
83
+ public class JointController : MonoBehaviour
84
+ {
85
+ [SerializeField] private Text stateText;
86
+ [SerializeField] private float torqueMagnitude = 20.0f;
87
+ private ConfigurableJoint joint;
88
+ private new Rigidbody rigidbody;
89
+ private bool isLocked;
90
+ private Vector3 polarAxis;
91
+ private Vector3 pole;
92
+
93
+ // axisとsecondaryAxisを接続相手の座標系に変換して保存するメソッドを作っておく
94
+ private void ResetPolarAxis()
95
+ {
96
+ this.pole =
97
+ this.joint.connectedBody.transform.InverseTransformDirection(
98
+ this.transform.TransformDirection(this.joint.axis));
99
+ this.polarAxis =
100
+ this.joint.connectedBody.transform.InverseTransformDirection(
101
+ this.transform.TransformDirection(this.joint.secondaryAxis));
102
+ }
103
+
104
+ // また、現在のsecondaryAxisの向きをconnectedBodyの座標系に変換して
105
+ // polarAxisの向きと比較することで角度の変化を算出するプロパティを用意しておく
106
+ private float Azimuth => Vector3.SignedAngle(
107
+ this.polarAxis,
108
+ this.joint.connectedBody.transform.InverseTransformDirection(
109
+ this.transform.TransformDirection(this.joint.secondaryAxis)),
110
+ this.pole);
111
+
112
+ // そしてロック状態を操作するためのプロパティを作る
113
+ private bool IsLocked
114
+ {
115
+ get => this.isLocked;
116
+ set
117
+ {
118
+ if (value == this.isLocked)
119
+ {
120
+ return;
121
+ }
122
+
123
+ this.isLocked = value;
124
+ if (value)
125
+ {
126
+ // まずロック時点のリミットを保存しておき...
127
+ var oldHighLimit = this.joint.highAngularXLimit;
128
+ var oldLowLimit = this.joint.lowAngularXLimit;
129
+
130
+ // ロック時点の基準方向からの回転角を取得しておく
131
+ var azimuth = this.Azimuth;
132
+
133
+ // そして回転をロック、基準方向を更新し...
134
+ this.joint.angularXMotion = ConfigurableJointMotion.Locked;
135
+ this.joint.swapBodies = !this.joint.swapBodies;
136
+ this.joint.swapBodies = !this.joint.swapBodies;
137
+ this.ResetPolarAxis();
138
+
139
+ // ロック時点の回転角を使ってリミットを修正する
140
+ var newHighLimit = oldHighLimit;
141
+ newHighLimit.limit += azimuth;
142
+ var newLowLimit = oldLowLimit;
143
+ newLowLimit.limit += azimuth;
144
+ this.joint.highAngularXLimit = newHighLimit;
145
+ this.joint.lowAngularXLimit = newLowLimit;
146
+
147
+ // 参考情報としてリミットをコンソールに出力する
148
+ Debug.Log($"High Limit: {oldHighLimit.limit} -> {newHighLimit.limit}, Low Limit: {oldLowLimit.limit} -> {newLowLimit.limit}");
149
+ }
150
+ else
151
+ {
152
+ this.joint.angularXMotion = ConfigurableJointMotion.Limited;
153
+ }
154
+ }
155
+ }
156
+
157
+ private void Start()
158
+ {
159
+ this.joint = this.GetComponent<ConfigurableJoint>();
160
+ this.rigidbody = this.GetComponent<Rigidbody>();
161
+
162
+ // Start時点でResetPolarAxisを実行して、初期基準方向を覚えておく
163
+ this.ResetPolarAxis();
164
+ }
165
+
166
+ private void Update()
167
+ {
168
+ if (Input.GetKeyDown(KeyCode.Space))
169
+ {
170
+ this.IsLocked = !this.IsLocked;
171
+ }
172
+
173
+ this.stateText.text = this.joint.angularXMotion.ToString();
174
+ }
175
+
176
+ private void FixedUpdate()
177
+ {
178
+ this.rigidbody.AddRelativeTorque(this.joint.axis * (this.torqueMagnitude * Input.GetAxis("Vertical")));
179
+ }
180
+ }
181
+ ```
182
+
183
+ 動かした様子も撮影してみたものの、この図ではちょっと分かりにくかったかもしれません。すみません...
184
+
185
+ ![図3](92865ad4f1fb8ae0bf02361898334efc.gif)