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

回答編集履歴

2

targetRotation方式のコードを追記

2020/11/10 20:00

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -227,4 +227,49 @@
227
227
 
228
228
  実行してみると、おぞましい化け物のような動きをしました...
229
229
 
230
- ![図4](14e1c62496ddfaaac22760ca7167f76a.gif)
230
+ ![図4](14e1c62496ddfaaac22760ca7167f76a.gif)
231
+
232
+ ##追記: targetRotationによるカプセル操作
233
+
234
+ ```C#
235
+ using UnityEngine;
236
+
237
+ [RequireComponent(typeof(ConfigurableJoint))]
238
+ public class CapsuleController3 : MonoBehaviour
239
+ {
240
+ [SerializeField] private float angularSpeedInDegrees = 120.0f;
241
+ [SerializeField] private float spring = 500.0f;
242
+ [SerializeField] private float damper = 100.0f;
243
+ [SerializeField] private float angularLimitInDegrees = 60.0f;
244
+
245
+ private ConfigurableJoint configurableJoint;
246
+ private float angle;
247
+
248
+ private void Start()
249
+ {
250
+ this.configurableJoint = this.GetComponent<ConfigurableJoint>();
251
+ this.configurableJoint.xMotion = ConfigurableJointMotion.Locked;
252
+ this.configurableJoint.yMotion = ConfigurableJointMotion.Locked;
253
+ this.configurableJoint.zMotion = ConfigurableJointMotion.Locked;
254
+ this.configurableJoint.angularXMotion = ConfigurableJointMotion.Free;
255
+ this.configurableJoint.angularYMotion = ConfigurableJointMotion.Locked;
256
+ this.configurableJoint.angularZMotion = ConfigurableJointMotion.Locked;
257
+ this.configurableJoint.angularXDrive = new JointDrive
258
+ {
259
+ maximumForce = float.MaxValue,
260
+ positionSpring = this.spring,
261
+ positionDamper = this.damper
262
+ };
263
+ }
264
+
265
+ private void Update()
266
+ {
267
+ var input = Input.GetAxis("Vertical");
268
+ this.angle = Mathf.Clamp(
269
+ this.angle + (this.angularSpeedInDegrees * Time.deltaTime * input),
270
+ -this.angularLimitInDegrees,
271
+ this.angularLimitInDegrees);
272
+ this.configurableJoint.targetRotation = Quaternion.Euler(this.angle, 0.0f, 0.0f);
273
+ }
274
+ }
275
+ ```

1

ヘビ型ロボット製作を想定した案を追記

2020/11/10 20:00

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -91,4 +91,140 @@
91
91
  }
92
92
  ```
93
93
 
94
- ![図](07a9cafa79ba71e319c09cc24500b3f4.gif)
94
+ ![図](07a9cafa79ba71e319c09cc24500b3f4.gif)
95
+
96
+ ##追記
97
+
98
+ 根拠のない直感なんですが、ヘビ型ロボットのようなものを作る布石ということを念頭に置くと、個々の体節が駆動する仕組みはなるべくシンプルにしておいた方がいいような予感がします。
99
+ 駆動方法についても、個人的には当初の`AddRelativeTorque`による方法は「オレンジカプセルを指でつまんでひねる」といったイメージ...つまりヘビ本体の動力ではなく、外部からの力で回転しているような感じがしました。それよりもジョイント部分に仕込まれたモーターで回転させるイメージの方がしっくりくる気がするのですが、いかがでしょうかね?
100
+
101
+ オレンジカプセルのスクリプトを下記のようにしてみました。本質的なのは`Update`と`FixedUpdate`で、`Start`内ではジョイントのパラメーターを設定しているだけです。
102
+ ここで設定しなくてもジョイントのインスペクター上に直接入力すればいいのですが、やたら設定項目が多くてどれをいじったか忘れてしまいそうになりこのようにしました。
103
+
104
+ ```C#
105
+ using UnityEngine;
106
+
107
+ [RequireComponent(typeof(ConfigurableJoint))]
108
+ public class CapsuleController2 : MonoBehaviour
109
+ {
110
+ [SerializeField] private float angularSpeedInDegrees = 120.0f;
111
+ [SerializeField] private float damper = 100.0f;
112
+ [SerializeField] private float angularLimitInDegrees = 60.0f;
113
+
114
+ private ConfigurableJoint configurableJoint;
115
+ private float input;
116
+
117
+ private void Start()
118
+ {
119
+ this.configurableJoint = this.GetComponent<ConfigurableJoint>();
120
+ this.configurableJoint.xMotion = ConfigurableJointMotion.Locked;
121
+ this.configurableJoint.yMotion = ConfigurableJointMotion.Locked;
122
+ this.configurableJoint.zMotion = ConfigurableJointMotion.Locked;
123
+ this.configurableJoint.angularXMotion = ConfigurableJointMotion.Limited;
124
+ this.configurableJoint.angularYMotion = ConfigurableJointMotion.Locked;
125
+ this.configurableJoint.angularZMotion = ConfigurableJointMotion.Locked;
126
+ this.configurableJoint.highAngularXLimit = new SoftJointLimit
127
+ {
128
+ limit = this.angularLimitInDegrees
129
+ };
130
+ this.configurableJoint.lowAngularXLimit = new SoftJointLimit
131
+ {
132
+ limit = -this.angularLimitInDegrees
133
+ };
134
+ this.configurableJoint.angularXDrive = new JointDrive
135
+ {
136
+ maximumForce = float.MaxValue,
137
+ positionSpring = 0.0f,
138
+ positionDamper = this.damper
139
+ };
140
+ }
141
+
142
+ private void Update()
143
+ {
144
+ this.input = Input.GetAxis("Vertical");
145
+ }
146
+
147
+ private void FixedUpdate()
148
+ {
149
+ this.configurableJoint.targetAngularVelocity = new Vector3(
150
+ this.input * this.angularSpeedInDegrees * Mathf.Deg2Rad,
151
+ 0.0f,
152
+ 0.0f);
153
+ }
154
+ }
155
+ ```
156
+
157
+ また、両カプセルのコライダーは`BoxCollider`に差し替えました。丸いカプセルではなく角柱にすることで、地面の上に落ちた後ふとしたはずみにコケてしまうのを防ごうという魂胆です。Freeze Rotation Yをオンにすることも考えたのですが、力の解決の結果すごく大きな力がかかることがあるようで、たまに空高く吹っ飛んでしまうことがありました。
158
+
159
+ ![図1](7f4f748617575b3ea105b94006c15b73.png)
160
+
161
+ 他にも、青カプセルとオレンジカプセルを親子関係にするのはやめました。親子関係になっていると、青カプセルが移動・回転した時に子オブジェクトであるオレンジカプセルも移動・回転してしまうことになります。経験上、物理シミュレーションの制御下にあるオブジェクトの位置や姿勢をダイレクトに操作するとろくなことにならないと思っておりまして、このようなオレンジカプセルの移動・回転もそれと同様の性質を持つだろうと考えてのことです。
162
+
163
+ 動かしてみると下図のようになりました。コメント欄で申し上げたたとえ話の、スカイダイビングをする人のような挙動になっています。
164
+
165
+ ![図2](2b61fc49470de896366802aa90894d6b.gif)
166
+
167
+ また、このようなコンセプトで駆動する体節を鎖状に繋げるとどんな挙動をするだろうかと気になりまして、下図のようにオブジェクトをセットアップしてみました。Headが青カプセル、Segment1~9がオレンジカプセルで、Segment1~9は`ConfigurableJoint`を持っており、Segment1はHeadに、Segment2はSegment1に...といった具合に鎖状につながっています。
168
+ コライダーはHeadだけ`CapsuleCollider`、他は`BoxCollider`としました。
169
+
170
+ ![図3](53b25d295260402d6ecbbca6dcee219a.png)
171
+
172
+ HeadとSegment1~8はスクリプトを持っておらず、Segment9にだけ下記のようなスクリプトをアタッチしました。
173
+ `CapsuleController2`は`targetAngularVelocity`を使ってモーターの速度を設定する方式なのに対し、こちらは`targetRotation`を使って目標角度を与える方式になっているという差異はありますが、セグメント間のジョイントが動力になっているという点は同様です。
174
+
175
+ ```C#
176
+ using System.Collections.Generic;
177
+ using UnityEngine;
178
+
179
+ [RequireComponent(typeof(ConfigurableJoint))]
180
+ public class WormController : MonoBehaviour
181
+ {
182
+ [SerializeField] private float omegaInDegrees = 90.0f;
183
+ [SerializeField] private float timeOffset = 0.5f;
184
+ [SerializeField] private float spring = 500.0f;
185
+ [SerializeField] private float damper = 100.0f;
186
+ [SerializeField] private float angularLimitInDegrees = 60.0f;
187
+
188
+ private readonly List<ConfigurableJoint> joints = new List<ConfigurableJoint>();
189
+ private float time;
190
+
191
+ private void Start()
192
+ {
193
+ var j = this.GetComponent<ConfigurableJoint>();
194
+ do
195
+ {
196
+ j.xMotion = ConfigurableJointMotion.Locked;
197
+ j.yMotion = ConfigurableJointMotion.Locked;
198
+ j.zMotion = ConfigurableJointMotion.Locked;
199
+ j.angularXMotion = ConfigurableJointMotion.Free;
200
+ j.angularYMotion = ConfigurableJointMotion.Locked;
201
+ j.angularZMotion = ConfigurableJointMotion.Locked;
202
+ j.angularXDrive = new JointDrive
203
+ {
204
+ maximumForce = float.MaxValue,
205
+ positionSpring = this.spring,
206
+ positionDamper = this.damper
207
+ };
208
+ this.joints.Add(j);
209
+ j = j.connectedBody.GetComponent<ConfigurableJoint>();
210
+ } while (j != null);
211
+ }
212
+
213
+ private void FixedUpdate()
214
+ {
215
+ for (var i = 0; i < this.joints.Count; i++)
216
+ {
217
+ var j = this.joints[i];
218
+ var t = Mathf.Max(this.time - (i * this.timeOffset), 0.0f);
219
+ var angle = this.angularLimitInDegrees * Mathf.Sin(t * this.omegaInDegrees * Mathf.Deg2Rad);
220
+ j.targetRotation = Quaternion.Euler(angle, 0.0f, 0.0f);
221
+ }
222
+
223
+ this.time += Time.deltaTime;
224
+ }
225
+ }
226
+ ```
227
+
228
+ 実行してみると、おぞましい化け物のような動きをしました...
229
+
230
+ ![図4](14e1c62496ddfaaac22760ca7167f76a.gif)