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

回答編集履歴

4

コルーチン生存確認について追記

2021/02/09 21:21

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -2,33 +2,33 @@
2
2
  では、代わりに起点と終点の距離を`PingPong`の`length`とするのはどうでしょうか?
3
3
 
4
4
  ```C#
5
- [SerializeField] private GameObject startPoint;
5
+ [SerializeField] private GameObject startPoint;
6
- [SerializeField] private GameObject endPoint;
6
+ [SerializeField] private GameObject endPoint;
7
7
 
8
- float startTime;
8
+ float startTime;
9
- float speed = 2;
9
+ float speed = 2;
10
- Vector3 dir;
10
+ Vector3 dir;
11
- Vector3 startPosition;
11
+ Vector3 startPosition;
12
- float distance;
12
+ float distance;
13
13
 
14
- void Start()
14
+ void Start()
15
- {
15
+ {
16
- startTime = Time.time;
16
+ startTime = Time.time;
17
- startPosition = startPoint.transform.position;
17
+ startPosition = startPoint.transform.position;
18
- Vector3 endPosition = endPoint.transform.position;
18
+ Vector3 endPosition = endPoint.transform.position;
19
- dir = (endPosition - startPosition).normalized;
19
+ dir = (endPosition - startPosition).normalized;
20
- distance = Vector3.Distance(startPosition, endPosition);
20
+ distance = Vector3.Distance(startPosition, endPosition);
21
- }
21
+ }
22
22
 
23
- void Update()
23
+ void Update()
24
- {
24
+ {
25
- float t = (Time.time - startTime) * speed;
25
+ float t = (Time.time - startTime) * speed;
26
- transform.position = startPosition + (dir * Mathf.PingPong(t, distance));
26
+ transform.position = startPosition + (dir * Mathf.PingPong(t, distance));
27
27
 
28
- // 進行方向は、PingPongの式の中にある「t - length」の
28
+ // 進行方向は、PingPongの式の中にある「t - length」の
29
- // 符号で判断できるんじゃないでしょうか?
29
+ // 符号で判断できるんじゃないでしょうか?
30
- transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
30
+ transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
31
- }
31
+ }
32
32
  ```
33
33
 
34
34
  `PingPong`の`length`に0以下の値を入れると狂うのは、どうやら確からしいですね。[PingPong](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L362)、[Repeat](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L356)、[Clamp](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L182)の式にマイナスの`length`を代入して手計算してみると確認できるかと思います。
@@ -36,269 +36,351 @@
36
36
  ## 水平の動きに制限
37
37
 
38
38
  ```C#
39
- void Start()
39
+ void Start()
40
- {
40
+ {
41
- startTime = Time.time;
41
+ startTime = Time.time;
42
- startPosition = startPoint.transform.position;
42
+ startPosition = startPoint.transform.position;
43
- Vector3 endPosition = endPoint.transform.position;
43
+ Vector3 endPosition = endPoint.transform.position;
44
44
 
45
- // 下記のようにすればstartPositionを
45
+ // 下記のようにすればstartPositionを
46
- // 通る水平面上を動くはずです
46
+ // 通る水平面上を動くはずです
47
- endPosition.y = startPosition.y;
47
+ endPosition.y = startPosition.y;
48
48
 
49
- // あるいは、起点・終点のy成分を0にしてしまえば
49
+ // あるいは、起点・終点のy成分を0にしてしまえば
50
- // 高さを無視してY=0の平面上を移動するでしょう
50
+ // 高さを無視してY=0の平面上を移動するでしょう
51
- //startPosition.y = 0.0f;
51
+ //startPosition.y = 0.0f;
52
- //endPosition.y = 0.0f;
52
+ //endPosition.y = 0.0f;
53
53
 
54
- dir = (endPosition - startPosition).normalized;
54
+ dir = (endPosition - startPosition).normalized;
55
- distance = Vector3.Distance(startPosition, endPosition);
55
+ distance = Vector3.Distance(startPosition, endPosition);
56
- }
56
+ }
57
57
 
58
- void Update()
58
+ void Update()
59
- {
59
+ {
60
- float t = (Time.time - startTime) * speed;
60
+ float t = (Time.time - startTime) * speed;
61
- Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
61
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
62
62
 
63
- // さまざまな高さの建物がある地形とのことですが、それらの高さを無視して
63
+ // さまざまな高さの建物がある地形とのことですが、それらの高さを無視して
64
- // 貫通して動くのではなく、建物の屋上に乗っているように見せたい場合は
64
+ // 貫通して動くのではなく、建物の屋上に乗っているように見せたい場合は
65
- // Raycastも併用して位置を修正する必要があるかと思います
65
+ // Raycastも併用して位置を修正する必要があるかと思います
66
- // 「移動オブジェクトのY軸座標は変化させずに」とのことですので、
66
+ // 「移動オブジェクトのY軸座標は変化させずに」とのことですので、
67
- // そういうことをしたいのではないだろうとは思うのですが...
67
+ // そういうことをしたいのではないだろうとは思うのですが...
68
- /*
68
+ /*
69
- if (Physics.Raycast(new Ray(new Vector3(newPosition.x, 100.0f, newPosition.z), Vector3.down), out var hitInfo))
69
+ if (Physics.Raycast(new Ray(new Vector3(newPosition.x, 100.0f, newPosition.z), Vector3.down), out var hitInfo))
70
- {
70
+ {
71
- newPosition = hitInfo.point;
71
+ newPosition = hitInfo.point;
72
- }
72
+ }
73
- */
73
+ */
74
74
 
75
- transform.position = newPosition;
75
+ transform.position = newPosition;
76
- transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
76
+ transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
77
- }
77
+ }
78
78
  ```
79
79
 
80
80
  ##PingPongの一時停止
81
81
 
82
82
  ```C#
83
- [SerializeField] private GameObject startPoint;
83
+ [SerializeField] private GameObject startPoint;
84
- [SerializeField] private GameObject endPoint;
84
+ [SerializeField] private GameObject endPoint;
85
- [SerializeField] private float animationLength = 1.0f;
85
+ [SerializeField] private float animationLength = 1.0f;
86
86
 
87
- float startTime;
87
+ float startTime;
88
- float pauseTime;
88
+ float pauseTime;
89
- bool paused;
89
+ bool paused;
90
- float speed = 2;
90
+ float speed = 2;
91
- Vector3 dir;
91
+ Vector3 dir;
92
- Vector3 startPosition;
92
+ Vector3 startPosition;
93
- float distance;
93
+ float distance;
94
- int directionSign;
94
+ int directionSign;
95
95
 
96
- public void Pause()
96
+ public void Pause()
97
- {
97
+ {
98
- // 一時停止フラグを立て、現在の時刻を記録する
98
+ // 一時停止フラグを立て、現在の時刻を記録する
99
- paused = true;
99
+ paused = true;
100
- pauseTime = Time.time;
100
+ pauseTime = Time.time;
101
- }
101
+ }
102
102
 
103
- public void Resume()
103
+ public void Resume()
104
- {
104
+ {
105
- // 移動再開時に、停止していた時間をstartTimeに加算する
105
+ // 移動再開時に、停止していた時間をstartTimeに加算する
106
- paused = false;
106
+ paused = false;
107
- startTime += Time.time - pauseTime;
107
+ startTime += Time.time - pauseTime;
108
- }
108
+ }
109
109
 
110
- void Start()
110
+ void Start()
111
- {
111
+ {
112
- startTime = Time.time;
112
+ startTime = Time.time;
113
- startPosition = startPoint.transform.position;
113
+ startPosition = startPoint.transform.position;
114
- Vector3 endPosition = endPoint.transform.position;
114
+ Vector3 endPosition = endPoint.transform.position;
115
- startPosition.y = 0.0f;
115
+ startPosition.y = 0.0f;
116
- endPosition.y = 0.0f;
116
+ endPosition.y = 0.0f;
117
- dir = (endPosition - startPosition).normalized;
117
+ dir = (endPosition - startPosition).normalized;
118
- distance = Vector3.Distance(startPosition, endPosition);
118
+ distance = Vector3.Distance(startPosition, endPosition);
119
119
 
120
- // 進行方向の符号...1で順方向、-1で逆方向
120
+ // 進行方向の符号...1で順方向、-1で逆方向
121
- // スタート時点の進行方向は順方向としておく
121
+ // スタート時点の進行方向は順方向としておく
122
- directionSign = 1;
122
+ directionSign = 1;
123
- }
123
+ }
124
124
 
125
- void Update()
125
+ void Update()
126
- {
126
+ {
127
- if (paused)
127
+ if (paused)
128
- {
128
+ {
129
- return;
129
+ return;
130
- }
130
+ }
131
131
 
132
- float t = (Time.time - startTime) * speed;
132
+ float t = (Time.time - startTime) * speed;
133
- Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
133
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
134
- transform.position = newPosition;
134
+ transform.position = newPosition;
135
-
135
+
136
- int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
136
+ int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
137
- if (newDirectionSign != directionSign)
137
+ if (newDirectionSign != directionSign)
138
- {
138
+ {
139
- // もし進行方向が反転したら、一時停止して
139
+ // もし進行方向が反転したら、一時停止して
140
- // 進行方向を更新、転回コルーチンを開始する
140
+ // 進行方向を更新、転回コルーチンを開始する
141
- Pause();
141
+ Pause();
142
- directionSign = newDirectionSign;
142
+ directionSign = newDirectionSign;
143
- StartCoroutine(Turn(transform));
143
+ StartCoroutine(Turn(transform));
144
- return;
144
+ return;
145
- }
145
+ }
146
146
 
147
- transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
147
+ transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
148
- }
148
+ }
149
149
 
150
- IEnumerator Turn(Transform trf)
150
+ IEnumerator Turn(Transform trf)
151
- {
151
+ {
152
- Vector3 baseForward = trf.forward;
152
+ Vector3 baseForward = trf.forward;
153
- for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
153
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
154
- {
154
+ {
155
- float t = time / animationLength;
155
+ float t = time / animationLength;
156
- Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
156
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
157
- trf.forward = q * baseForward;
157
+ trf.forward = q * baseForward;
158
- yield return null;
158
+ yield return null;
159
- }
159
+ }
160
- trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
160
+ trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
161
161
 
162
- // 転回終了後に移動を再開
162
+ // 転回終了後に移動を再開
163
- Resume();
163
+ Resume();
164
- }
164
+ }
165
165
  ```
166
166
 
167
167
  ##目標方向へ回転
168
168
 
169
169
  ```C#
170
- [SerializeField] GameObject startPoint;
170
+ [SerializeField] GameObject startPoint;
171
- [SerializeField] GameObject endPoint;
171
+ [SerializeField] GameObject endPoint;
172
- [SerializeField] float animationLength = 1.0f;
172
+ [SerializeField] float animationLength = 1.0f;
173
- [SerializeField] Transform target;
173
+ [SerializeField] Transform target;
174
- Vector3 dir;
174
+ Vector3 dir;
175
- int directionSign;
175
+ int directionSign;
176
- float distance;
176
+ float distance;
177
- bool paused;
177
+ bool paused;
178
- float pauseTime;
178
+ float pauseTime;
179
- readonly float speed = 2;
179
+ readonly float speed = 2;
180
- Vector3 startPosition;
180
+ Vector3 startPosition;
181
181
 
182
- float startTime;
182
+ float startTime;
183
183
 
184
- void Start()
184
+ void Start()
185
- {
185
+ {
186
- startTime = Time.time;
186
+ startTime = Time.time;
187
- startPosition = startPoint.transform.position;
187
+ startPosition = startPoint.transform.position;
188
- Vector3 endPosition = endPoint.transform.position;
188
+ Vector3 endPosition = endPoint.transform.position;
189
- startPosition.y = 0.0f;
189
+ startPosition.y = 0.0f;
190
- endPosition.y = 0.0f;
190
+ endPosition.y = 0.0f;
191
- dir = (endPosition - startPosition).normalized;
191
+ dir = (endPosition - startPosition).normalized;
192
- distance = Vector3.Distance(startPosition, endPosition);
192
+ distance = Vector3.Distance(startPosition, endPosition);
193
- directionSign = 1;
193
+ directionSign = 1;
194
- }
194
+ }
195
195
 
196
- void Update()
196
+ void Update()
197
- {
197
+ {
198
- if (paused)
198
+ if (paused)
199
- {
199
+ {
200
- return;
200
+ return;
201
- }
201
+ }
202
202
 
203
- float t = (Time.time - startTime) * speed;
203
+ float t = (Time.time - startTime) * speed;
204
- Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
204
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
205
- transform.position = newPosition;
205
+ transform.position = newPosition;
206
- int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
206
+ int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
207
- if (newDirectionSign != directionSign)
207
+ if (newDirectionSign != directionSign)
208
- {
208
+ {
209
- Pause();
209
+ Pause();
210
- directionSign = newDirectionSign;
210
+ directionSign = newDirectionSign;
211
- StartCoroutine(Turn(transform));
211
+ StartCoroutine(Turn(transform));
212
- return;
212
+ return;
213
- }
213
+ }
214
214
 
215
- transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
215
+ transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
216
216
 
217
- // 実験として、スペースバーで模擬的な会話を行う
217
+ // 実験として、スペースバーで模擬的な会話を行う
218
- if (!paused && Input.GetKeyDown(KeyCode.Space))
218
+ if (!paused && Input.GetKeyDown(KeyCode.Space))
219
- {
219
+ {
220
- StartCoroutine(TalkWith(target));
220
+ StartCoroutine(TalkWith(target));
221
- }
221
+ }
222
- }
222
+ }
223
223
 
224
- public void Pause()
224
+ public void Pause()
225
- {
225
+ {
226
- // 今さらながら、もし一時停止中に再度Pauseを行うと時刻が狂うため
226
+ // 今さらながら、もし一時停止中に再度Pauseを行うと時刻が狂うため
227
- // 一時停止中には時刻の記録を行わないようにしました
227
+ // 一時停止中には時刻の記録を行わないようにしました
228
- if (paused)
228
+ if (paused)
229
- {
229
+ {
230
- return;
230
+ return;
231
- }
231
+ }
232
232
 
233
- paused = true;
233
+ paused = true;
234
- pauseTime = Time.time;
234
+ pauseTime = Time.time;
235
- }
235
+ }
236
236
 
237
- public void Resume()
237
+ public void Resume()
238
- {
238
+ {
239
- // 同じく時刻の狂いを防止するため、移動中には
239
+ // 同じく時刻の狂いを防止するため、移動中には
240
- // 開始時刻の更新を行わないようにしました
240
+ // 開始時刻の更新を行わないようにしました
241
- if (!paused)
241
+ if (!paused)
242
- {
242
+ {
243
- return;
243
+ return;
244
- }
244
+ }
245
245
 
246
- paused = false;
246
+ paused = false;
247
- startTime += Time.time - pauseTime;
247
+ startTime += Time.time - pauseTime;
248
- }
248
+ }
249
249
 
250
- IEnumerator TalkWith(Transform targetTransform)
250
+ IEnumerator TalkWith(Transform targetTransform)
251
- {
251
+ {
252
- // 念のため目標方向をXZ平面上に投影しておく
252
+ // 念のため目標方向をXZ平面上に投影しておく
253
- Vector3 targetDir = Vector3.ProjectOnPlane(targetTransform.position - transform.position, Vector3.up).normalized;
253
+ Vector3 targetDir = Vector3.ProjectOnPlane(targetTransform.position - transform.position, Vector3.up).normalized;
254
- Vector3 forward = transform.forward;
254
+ Vector3 forward = transform.forward;
255
255
 
256
- // 一時停止して目標方向へ回転し...
256
+ // 一時停止して目標方向へ回転し...
257
- Pause();
257
+ Pause();
258
- yield return YTurnToTarget(transform, targetDir);
258
+ yield return YTurnToTarget(transform, targetDir);
259
259
 
260
- // コンソールに文章を表示、少し待機する(会話しているつもり)
260
+ // コンソールに文章を表示、少し待機する(会話しているつもり)
261
- Debug.Log("Hello!");
261
+ Debug.Log("Hello!");
262
- yield return new WaitForSeconds(5.0f);
262
+ yield return new WaitForSeconds(5.0f);
263
263
 
264
- // 元の方向へ回転し、一時停止を解除する
264
+ // 元の方向へ回転し、一時停止を解除する
265
- yield return YTurnToTarget(transform, forward);
265
+ yield return YTurnToTarget(transform, forward);
266
- Resume();
266
+ Resume();
267
- }
267
+ }
268
268
 
269
- // 引数としてターゲットへの方角を受け取ることにする
269
+ // 引数としてターゲットへの方角を受け取ることにする
270
- IEnumerator YTurnToTarget(Transform trf, Vector3 targetDir)
270
+ IEnumerator YTurnToTarget(Transform trf, Vector3 targetDir)
271
- {
271
+ {
272
- Vector3 baseForward = trf.forward;
272
+ Vector3 baseForward = trf.forward;
273
273
 
274
- // Y軸周りの符号付き回転角を得て...
274
+ // Y軸周りの符号付き回転角を得て...
275
- float angle = Vector3.SignedAngle(baseForward, targetDir, Vector3.up);
275
+ float angle = Vector3.SignedAngle(baseForward, targetDir, Vector3.up);
276
- for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
276
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
277
- {
277
+ {
278
- float t = time / animationLength;
278
+ float t = time / animationLength;
279
279
 
280
- // Lerpの引数としてangleを使う
280
+ // Lerpの引数としてangleを使う
281
- Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, angle, t), 0.0f);
281
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, angle, t), 0.0f);
282
- trf.forward = q * baseForward;
282
+ trf.forward = q * baseForward;
283
- yield return null;
283
+ yield return null;
284
- }
284
+ }
285
285
 
286
- // 補間値t=1の調整処理
286
+ // 補間値t=1の調整処理
287
- trf.forward = Quaternion.Euler(0.0f, angle, 0.0f) * baseForward;
287
+ trf.forward = Quaternion.Euler(0.0f, angle, 0.0f) * baseForward;
288
- }
288
+ }
289
289
 
290
- IEnumerator Turn(Transform trf)
290
+ IEnumerator Turn(Transform trf)
291
- {
291
+ {
292
- Vector3 baseForward = trf.forward;
292
+ Vector3 baseForward = trf.forward;
293
- for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
293
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
294
- {
294
+ {
295
- float t = time / animationLength;
295
+ float t = time / animationLength;
296
- Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
296
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
297
- trf.forward = q * baseForward;
297
+ trf.forward = q * baseForward;
298
- yield return null;
298
+ yield return null;
299
- }
299
+ }
300
300
 
301
- trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
301
+ trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
302
- Resume();
302
+ Resume();
303
- }
303
+ }
304
+ ```
305
+
306
+ ##コルーチンの生存確認
307
+
308
+ **案1**
309
+
310
+ `MyCoroutine`内での`null`チェックは削除し、`Update`でのコルーチン起動判定条件として`null`チェックを行うのはどうでしょう。
311
+
312
+ ```C#
313
+ using System.Collections;
314
+ using UnityEngine;
315
+
316
+ public class TestCoroutine : MonoBehaviour
317
+ {
318
+ Coroutine RunningMyCoroutine;
319
+
320
+ void Start()
321
+ {
322
+ RunningMyCoroutine = StartCoroutine(MyCoroutine());
323
+ }
324
+
325
+ void Update()
326
+ {
327
+ if ((RunningMyCoroutine == null) && Input.GetMouseButtonDown(0))
328
+ {
329
+ RunningMyCoroutine = StartCoroutine(MyCoroutine());
330
+ }
331
+ }
332
+
333
+ IEnumerator MyCoroutine()
334
+ {
335
+ int i = 0;
336
+ while (i < 5)
337
+ {
338
+ Debug.Log($"MyCoroutine:{i}");
339
+ i++;
340
+ yield return new WaitForSeconds(2.0f);
341
+ }
342
+ RunningMyCoroutine = null;
343
+ }
344
+ }
345
+ ```
346
+
347
+ **案2**
348
+
349
+ `yield return`を行わずにいきなり終了するコルーチンを始動した場合`StartCoroutine`の返り値は`null`になるようなので、「返り値が`null`なら`RunningMyCoroutine`は元の値のままとする」ということにしてもよさそうです。
350
+
351
+ ```C#
352
+ using System.Collections;
353
+ using UnityEngine;
354
+
355
+ public class TestCoroutine : MonoBehaviour
356
+ {
357
+
358
+ Coroutine RunningMyCoroutine;
359
+
360
+ void Start()
361
+ {
362
+ RunningMyCoroutine = StartCoroutine(MyCoroutine());
363
+ }
364
+
365
+ void Update()
366
+ {
367
+ if (Input.GetMouseButtonDown(0))
368
+ {
369
+ RunningMyCoroutine = StartCoroutine(MyCoroutine()) ?? RunningMyCoroutine;
370
+ }
371
+ }
372
+
373
+ IEnumerator MyCoroutine()
374
+ {
375
+ if (RunningMyCoroutine != null) yield break;
376
+ int i = 0;
377
+ while (i<5)
378
+ {
379
+ Debug.Log($"MyCoroutine:{i}");
380
+ i++;
381
+ yield return new WaitForSeconds(2.0f);
382
+ }
383
+ RunningMyCoroutine = null;
384
+ }
385
+ }
304
386
  ```

3

目標方向への回転案を追記

2021/02/09 21:21

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -162,4 +162,143 @@
162
162
  // 転回終了後に移動を再開
163
163
  Resume();
164
164
  }
165
+ ```
166
+
167
+ ##目標方向へ回転
168
+
169
+ ```C#
170
+ [SerializeField] GameObject startPoint;
171
+ [SerializeField] GameObject endPoint;
172
+ [SerializeField] float animationLength = 1.0f;
173
+ [SerializeField] Transform target;
174
+ Vector3 dir;
175
+ int directionSign;
176
+ float distance;
177
+ bool paused;
178
+ float pauseTime;
179
+ readonly float speed = 2;
180
+ Vector3 startPosition;
181
+
182
+ float startTime;
183
+
184
+ void Start()
185
+ {
186
+ startTime = Time.time;
187
+ startPosition = startPoint.transform.position;
188
+ Vector3 endPosition = endPoint.transform.position;
189
+ startPosition.y = 0.0f;
190
+ endPosition.y = 0.0f;
191
+ dir = (endPosition - startPosition).normalized;
192
+ distance = Vector3.Distance(startPosition, endPosition);
193
+ directionSign = 1;
194
+ }
195
+
196
+ void Update()
197
+ {
198
+ if (paused)
199
+ {
200
+ return;
201
+ }
202
+
203
+ float t = (Time.time - startTime) * speed;
204
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
205
+ transform.position = newPosition;
206
+ int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
207
+ if (newDirectionSign != directionSign)
208
+ {
209
+ Pause();
210
+ directionSign = newDirectionSign;
211
+ StartCoroutine(Turn(transform));
212
+ return;
213
+ }
214
+
215
+ transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
216
+
217
+ // 実験として、スペースバーで模擬的な会話を行う
218
+ if (!paused && Input.GetKeyDown(KeyCode.Space))
219
+ {
220
+ StartCoroutine(TalkWith(target));
221
+ }
222
+ }
223
+
224
+ public void Pause()
225
+ {
226
+ // 今さらながら、もし一時停止中に再度Pauseを行うと時刻が狂うため
227
+ // 一時停止中には時刻の記録を行わないようにしました
228
+ if (paused)
229
+ {
230
+ return;
231
+ }
232
+
233
+ paused = true;
234
+ pauseTime = Time.time;
235
+ }
236
+
237
+ public void Resume()
238
+ {
239
+ // 同じく時刻の狂いを防止するため、移動中には
240
+ // 開始時刻の更新を行わないようにしました
241
+ if (!paused)
242
+ {
243
+ return;
244
+ }
245
+
246
+ paused = false;
247
+ startTime += Time.time - pauseTime;
248
+ }
249
+
250
+ IEnumerator TalkWith(Transform targetTransform)
251
+ {
252
+ // 念のため目標方向をXZ平面上に投影しておく
253
+ Vector3 targetDir = Vector3.ProjectOnPlane(targetTransform.position - transform.position, Vector3.up).normalized;
254
+ Vector3 forward = transform.forward;
255
+
256
+ // 一時停止して目標方向へ回転し...
257
+ Pause();
258
+ yield return YTurnToTarget(transform, targetDir);
259
+
260
+ // コンソールに文章を表示、少し待機する(会話しているつもり)
261
+ Debug.Log("Hello!");
262
+ yield return new WaitForSeconds(5.0f);
263
+
264
+ // 元の方向へ回転し、一時停止を解除する
265
+ yield return YTurnToTarget(transform, forward);
266
+ Resume();
267
+ }
268
+
269
+ // 引数としてターゲットへの方角を受け取ることにする
270
+ IEnumerator YTurnToTarget(Transform trf, Vector3 targetDir)
271
+ {
272
+ Vector3 baseForward = trf.forward;
273
+
274
+ // Y軸周りの符号付き回転角を得て...
275
+ float angle = Vector3.SignedAngle(baseForward, targetDir, Vector3.up);
276
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
277
+ {
278
+ float t = time / animationLength;
279
+
280
+ // Lerpの引数としてangleを使う
281
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, angle, t), 0.0f);
282
+ trf.forward = q * baseForward;
283
+ yield return null;
284
+ }
285
+
286
+ // 補間値t=1の調整処理
287
+ trf.forward = Quaternion.Euler(0.0f, angle, 0.0f) * baseForward;
288
+ }
289
+
290
+ IEnumerator Turn(Transform trf)
291
+ {
292
+ Vector3 baseForward = trf.forward;
293
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
294
+ {
295
+ float t = time / animationLength;
296
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
297
+ trf.forward = q * baseForward;
298
+ yield return null;
299
+ }
300
+
301
+ trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
302
+ Resume();
303
+ }
165
304
  ```

2

PingPong一時停止案を追記

2021/02/06 15:28

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -75,4 +75,91 @@
75
75
  transform.position = newPosition;
76
76
  transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
77
77
  }
78
+ ```
79
+
80
+ ##PingPongの一時停止
81
+
82
+ ```C#
83
+ [SerializeField] private GameObject startPoint;
84
+ [SerializeField] private GameObject endPoint;
85
+ [SerializeField] private float animationLength = 1.0f;
86
+
87
+ float startTime;
88
+ float pauseTime;
89
+ bool paused;
90
+ float speed = 2;
91
+ Vector3 dir;
92
+ Vector3 startPosition;
93
+ float distance;
94
+ int directionSign;
95
+
96
+ public void Pause()
97
+ {
98
+ // 一時停止フラグを立て、現在の時刻を記録する
99
+ paused = true;
100
+ pauseTime = Time.time;
101
+ }
102
+
103
+ public void Resume()
104
+ {
105
+ // 移動再開時に、停止していた時間をstartTimeに加算する
106
+ paused = false;
107
+ startTime += Time.time - pauseTime;
108
+ }
109
+
110
+ void Start()
111
+ {
112
+ startTime = Time.time;
113
+ startPosition = startPoint.transform.position;
114
+ Vector3 endPosition = endPoint.transform.position;
115
+ startPosition.y = 0.0f;
116
+ endPosition.y = 0.0f;
117
+ dir = (endPosition - startPosition).normalized;
118
+ distance = Vector3.Distance(startPosition, endPosition);
119
+
120
+ // 進行方向の符号...1で順方向、-1で逆方向
121
+ // スタート時点の進行方向は順方向としておく
122
+ directionSign = 1;
123
+ }
124
+
125
+ void Update()
126
+ {
127
+ if (paused)
128
+ {
129
+ return;
130
+ }
131
+
132
+ float t = (Time.time - startTime) * speed;
133
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
134
+ transform.position = newPosition;
135
+
136
+ int newDirectionSign = (int)Mathf.Sign(distance - Mathf.Repeat(t, distance * 2.0f));
137
+ if (newDirectionSign != directionSign)
138
+ {
139
+ // もし進行方向が反転したら、一時停止して
140
+ // 進行方向を更新、転回コルーチンを開始する
141
+ Pause();
142
+ directionSign = newDirectionSign;
143
+ StartCoroutine(Turn(transform));
144
+ return;
145
+ }
146
+
147
+ transform.rotation = Quaternion.LookRotation(dir * newDirectionSign);
148
+ }
149
+
150
+ IEnumerator Turn(Transform trf)
151
+ {
152
+ Vector3 baseForward = trf.forward;
153
+ for (float time = 0.0f; time < animationLength; time += Time.deltaTime)
154
+ {
155
+ float t = time / animationLength;
156
+ Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f);
157
+ trf.forward = q * baseForward;
158
+ yield return null;
159
+ }
160
+ trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward;
161
+
162
+ // 転回終了後に移動を再開
163
+ Resume();
164
+ }
78
165
  ```

1

動きを水平方向へ制限したバージョンを追記

2021/02/02 19:41

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -31,4 +31,48 @@
31
31
  }
32
32
  ```
33
33
 
34
- `PingPong`の`length`に0以下の値を入れると狂うのは、どうやら確からしいですね。[PingPong](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L362)、[Repeat](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L356)、[Clamp](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L182)の式にマイナスの`length`を代入して手計算してみると確認できるかと思います。
34
+ `PingPong`の`length`に0以下の値を入れると狂うのは、どうやら確からしいですね。[PingPong](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L362)、[Repeat](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L356)、[Clamp](https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Math/Mathf.cs#L182)の式にマイナスの`length`を代入して手計算してみると確認できるかと思います。
35
+
36
+ ## 水平の動きに制限
37
+
38
+ ```C#
39
+ void Start()
40
+ {
41
+ startTime = Time.time;
42
+ startPosition = startPoint.transform.position;
43
+ Vector3 endPosition = endPoint.transform.position;
44
+
45
+ // 下記のようにすればstartPositionを
46
+ // 通る水平面上を動くはずです
47
+ endPosition.y = startPosition.y;
48
+
49
+ // あるいは、起点・終点のy成分を0にしてしまえば
50
+ // 高さを無視してY=0の平面上を移動するでしょう
51
+ //startPosition.y = 0.0f;
52
+ //endPosition.y = 0.0f;
53
+
54
+ dir = (endPosition - startPosition).normalized;
55
+ distance = Vector3.Distance(startPosition, endPosition);
56
+ }
57
+
58
+ void Update()
59
+ {
60
+ float t = (Time.time - startTime) * speed;
61
+ Vector3 newPosition = startPosition + (dir * Mathf.PingPong(t, distance));
62
+
63
+ // さまざまな高さの建物がある地形とのことですが、それらの高さを無視して
64
+ // 貫通して動くのではなく、建物の屋上に乗っているように見せたい場合は
65
+ // Raycastも併用して位置を修正する必要があるかと思います
66
+ // 「移動オブジェクトのY軸座標は変化させずに」とのことですので、
67
+ // そういうことをしたいのではないだろうとは思うのですが...
68
+ /*
69
+ if (Physics.Raycast(new Ray(new Vector3(newPosition.x, 100.0f, newPosition.z), Vector3.down), out var hitInfo))
70
+ {
71
+ newPosition = hitInfo.point;
72
+ }
73
+ */
74
+
75
+ transform.position = newPosition;
76
+ transform.rotation = Quaternion.LookRotation(dir * Mathf.Sign(this.distance - Mathf.Repeat(t, distance * 2.0f)));
77
+ }
78
+ ```