回答編集履歴

1

水平移動モーションを追加

2018/11/17 00:38

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -1,3 +1,375 @@
1
1
  一案としましては、去年の話ですが[C# - Unityでゲーム内の物理的負荷をトリガーにしたい|teratail](https://teratail.com/questions/102066)のご質問に案を投稿した際には、一連の動きを表現するのに入れ子のコルーチンを使ってみました。
2
2
 
3
3
  投稿したコードでは、ご要望を実現しようと試行錯誤した結果クローをアームにジョイントで繋いで開閉していますが、クローもKinematicにしてしまい[MoveRotation](https://docs.unity3d.com/ja/current/ScriptReference/Rigidbody.MoveRotation.html)で回転させる方が制御が簡単かもしれません。
4
+
5
+
6
+
7
+ #追記
8
+
9
+ 水平位置を元に戻すには、Craneがアタッチされたオブジェクトを元の位置に戻してやればよさそうですね。
10
+
11
+ Craneスクリプトに水平移動モーションを追加してみました。元々のコメントは削除し、今回変更があった部分にのみコメントを加えています。
12
+
13
+ ```C#
14
+
15
+ using System.Collections;
16
+
17
+ using System.Linq;
18
+
19
+ using UnityEngine;
20
+
21
+
22
+
23
+ public class Crane : MonoBehaviour
24
+
25
+ {
26
+
27
+ public Transform armTransform;
28
+
29
+ public Joint LoadDetectorJoint;
30
+
31
+ public HingeJoint[] ClawHinges;
32
+
33
+
34
+
35
+ private bool playing;
36
+
37
+ private float homePositionY; // フィールド名をhomePositionYに変更
38
+
39
+ private Vector2 homePositionXZ; // 水平位置用のフィールドを追加
40
+
41
+
42
+
43
+ private void Start()
44
+
45
+ {
46
+
47
+ this.homePositionY = armTransform.position.y; // フィールド名をhomePositionYに変更
48
+
49
+ this.homePositionXZ = new Vector2(this.transform.position.x, this.transform.position.z); // アーム上昇後、水平位置をここへ移動する
50
+
51
+ }
52
+
53
+
54
+
55
+ private void Update()
56
+
57
+ {
58
+
59
+ if (!this.playing && Input.GetKeyDown(KeyCode.Space))
60
+
61
+ {
62
+
63
+ this.StartCoroutine(this.StartGame());
64
+
65
+ }
66
+
67
+ }
68
+
69
+
70
+
71
+ private IEnumerator StartGame()
72
+
73
+ {
74
+
75
+ this.playing = true;
76
+
77
+
78
+
79
+ Debug.Log("Start!");
80
+
81
+
82
+
83
+ yield return this.StartCoroutine(this.MoveClawsMotion(60.0f));
84
+
85
+ yield return new WaitForSeconds(1.0f);
86
+
87
+ yield return this.StartCoroutine(this.MoveDownMotion());
88
+
89
+ yield return new WaitForSeconds(1.0f);
90
+
91
+ yield return this.StartCoroutine(this.MoveClawsMotion(0.0f));
92
+
93
+ yield return new WaitForSeconds(1.0f);
94
+
95
+ yield return this.StartCoroutine(this.MoveUpMotion());
96
+
97
+ yield return new WaitForSeconds(1.0f);
98
+
99
+ yield return this.StartCoroutine(this.ReturnHomeMotion()); // 水平位置を元に戻す
100
+
101
+ yield return new WaitForSeconds(1.0f); // 少し待機
102
+
103
+ yield return this.StartCoroutine(this.MoveClawsMotion(60.0f));
104
+
105
+ yield return new WaitForSeconds(1.0f);
106
+
107
+ yield return this.StartCoroutine(this.MoveClawsMotion(0.0f));
108
+
109
+
110
+
111
+ this.playing = false;
112
+
113
+ }
114
+
115
+
116
+
117
+ private IEnumerator MoveDownMotion()
118
+
119
+ {
120
+
121
+ const int loadMovingAverageLength = 30;
122
+
123
+ const float loadThreshold = 0.0f;
124
+
125
+ const float acceleration = 0.5f;
126
+
127
+ const float velocityMax = 1.0f;
128
+
129
+
130
+
131
+ var loads = Enumerable.Repeat<float>(this.LoadDetectorJoint.currentForce.y, loadMovingAverageLength).ToArray();
132
+
133
+ var loadSum = loads.Sum();
134
+
135
+ var loadIndex = 0;
136
+
137
+
138
+
139
+ var velocity = 0.0f;
140
+
141
+
142
+
143
+ while (true)
144
+
145
+ {
146
+
147
+ var previousLoad = loads[loadIndex];
148
+
149
+ var newLoad = this.LoadDetectorJoint.currentForce.y;
150
+
151
+
152
+
153
+ loadSum += (newLoad - previousLoad);
154
+
155
+
156
+
157
+ var averageLoad = loadSum / loadMovingAverageLength;
158
+
159
+
160
+
161
+ Debug.LogFormat("Load:{0}", averageLoad);
162
+
163
+
164
+
165
+ if (averageLoad >= loadThreshold)
166
+
167
+ {
168
+
169
+ Debug.Log("Stop!");
170
+
171
+ break;
172
+
173
+ }
174
+
175
+
176
+
177
+ velocity = Mathf.Max(velocity - acceleration * Time.deltaTime, -velocityMax);
178
+
179
+ this.armTransform.Translate(0.0f, velocity * Time.deltaTime, 0.0f, Space.World);
180
+
181
+ loadIndex = (loadIndex + 1) % loadMovingAverageLength;
182
+
183
+
184
+
185
+ yield return null;
186
+
187
+ }
188
+
189
+ }
190
+
191
+
192
+
193
+ private IEnumerator MoveUpMotion()
194
+
195
+ {
196
+
197
+ const float acceleration = 0.5f;
198
+
199
+ const float velocityMax = 1.0f;
200
+
201
+
202
+
203
+ var velocity = 0.0f;
204
+
205
+
206
+
207
+ while (this.armTransform.position.y < this.homePositionY) // フィールド名をhomePositionYに変更
208
+
209
+ {
210
+
211
+ velocity = Mathf.Min(velocity + acceleration * Time.deltaTime, velocityMax);
212
+
213
+ this.armTransform.Translate(0.0f, velocity * Time.deltaTime, 0.0f, Space.World);
214
+
215
+
216
+
217
+ yield return null;
218
+
219
+ }
220
+
221
+
222
+
223
+ // コメントで申し上げたように、アームのX、Zを0にしてしまわないように変更しました
224
+
225
+ var armPosition = this.armTransform.position;
226
+
227
+ armPosition.y = this.homePositionY; // フィールド名をhomePositionYに変更
228
+
229
+ this.armTransform.position = armPosition;
230
+
231
+ }
232
+
233
+
234
+
235
+ // 元の位置に戻すためのモーションを追加
236
+
237
+ // クレーンゲームらしくしようと思い、景品を持ち上げた位置から一直線に戻るのではなく、XとZを独立して動かしています
238
+
239
+ private IEnumerator ReturnHomeMotion()
240
+
241
+ {
242
+
243
+ const float acceleration = 0.5f; // X軸またはZ軸の移動加速度
244
+
245
+ const float velocityMax = 1.0f; // X軸またはZ軸の最大移動速度
246
+
247
+
248
+
249
+ var velocity = 0.0f;
250
+
251
+ var position = new Vector2(this.transform.position.x, this.transform.position.z); // 現在の水平位置
252
+
253
+
254
+
255
+ // ホームポジションへの向きの符号を求める...移動方向のコントロールに使う
256
+
257
+ var sign = this.homePositionXZ - position;
258
+
259
+ sign.x = Mathf.Sign(sign.x);
260
+
261
+ sign.y = Mathf.Sign(sign.y);
262
+
263
+
264
+
265
+ while (true)
266
+
267
+ {
268
+
269
+ // ホームポジションへの相対位置を求め、十分近づいたら移動を終える
270
+
271
+ var left = this.homePositionXZ - position;
272
+
273
+ if (Mathf.Approximately(left.sqrMagnitude, 0.0f))
274
+
275
+ {
276
+
277
+ break;
278
+
279
+ }
280
+
281
+
282
+
283
+ velocity = Mathf.Min(velocity + acceleration * Time.deltaTime, velocityMax);
284
+
285
+ var delta = velocity * Time.deltaTime; // XまたはZ方向の、このフレームでの移動量
286
+
287
+ var nextPosition = position + sign * delta; // このフレームでの移動先
288
+
289
+
290
+
291
+ // 目標地点を越えないように座標をクランプしてから、そこへ移動
292
+
293
+ position.x = sign.x > 0.0f ? Mathf.Min(nextPosition.x, this.homePositionXZ.x) : Mathf.Max(nextPosition.x, this.homePositionXZ.x);
294
+
295
+ position.y = sign.y > 0.0f ? Mathf.Min(nextPosition.y, this.homePositionXZ.y) : Mathf.Max(nextPosition.y, this.homePositionXZ.y);
296
+
297
+ this.transform.position = new Vector3(position.x, this.homePositionY, position.y);
298
+
299
+
300
+
301
+ yield return null;
302
+
303
+ }
304
+
305
+
306
+
307
+ this.transform.position = new Vector3(this.homePositionXZ.x, this.homePositionY, this.homePositionXZ.y);
308
+
309
+ }
310
+
311
+
312
+
313
+ private IEnumerator MoveClawsMotion(float angle, float timeout = 3.0f)
314
+
315
+ {
316
+
317
+ const float angularSpeed = 30.0f;
318
+
319
+
320
+
321
+ var time = 0.0f;
322
+
323
+
324
+
325
+ while (time < timeout)
326
+
327
+ {
328
+
329
+ var reached = true;
330
+
331
+ var angleDeltaMax = angularSpeed * Time.deltaTime;
332
+
333
+
334
+
335
+ foreach (var clawHinge in this.ClawHinges)
336
+
337
+ {
338
+
339
+ var spring = clawHinge.spring;
340
+
341
+
342
+
343
+ spring.targetPosition = Mathf.MoveTowardsAngle(spring.targetPosition, angle, angleDeltaMax);
344
+
345
+ clawHinge.spring = spring;
346
+
347
+ reached &= Mathf.Approximately(spring.targetPosition, angle);
348
+
349
+ }
350
+
351
+
352
+
353
+ if (reached)
354
+
355
+ {
356
+
357
+ break;
358
+
359
+ }
360
+
361
+
362
+
363
+ time += Time.deltaTime;
364
+
365
+
366
+
367
+ yield return null;
368
+
369
+ }
370
+
371
+ }
372
+
373
+ }
374
+
375
+ ```