回答編集履歴

1

衝突によるパスワード入力の実験結果を追記

2017/07/14 15:20

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -79,3 +79,415 @@
79
79
  }
80
80
 
81
81
  ```
82
+
83
+
84
+
85
+ [追記]
86
+
87
+ ボールと箱の衝突でパスワードを入力するテストをやってみました。自身の勉強も兼ねて、画面上の見栄えをいくらかそれらしくするためのコードを追加したため、ちょっとシンプルさは損なわれてしまいましたがご容赦ください。
88
+
89
+ 次のような構成で作りました。
90
+
91
+
92
+
93
+ - ボール、箱0〜9、ゲートを用意する。
94
+
95
+ - ボールはプレイヤーの操作に合わせて転がるだけで、衝突判定やパスワード判定のロジックは持っていない。
96
+
97
+ - 箱が衝突判定を行う。ボールと自身の衝突を検知したら、自身が持つ文字(箱0であれば「0」...)を引数とする数字入力イベントを発生する。
98
+
99
+ - ゲートは箱からの数字入力イベントに応答し、そこで上記コードのロジックによってパスワードを入力していく。パスワードが正しければ、ゲートを開くアニメーションを行う。
100
+
101
+
102
+
103
+ 肝になる箱とゲートのスクリプトは下記のようにしました。
104
+
105
+ コード内の「※」の付いたコメントの部分は、今回のパスワード入力とは直接関係のない箇所ですので、あまり気にしないでください(見た目の制御などに関わる部分です)。
106
+
107
+
108
+
109
+ 箱のスクリプト
110
+
111
+ ```C#
112
+
113
+ using UnityEngine;
114
+
115
+ using UnityEngine.Events;
116
+
117
+
118
+
119
+ // 数字入力のためのカスタムイベント
120
+
121
+ [Serializable]
122
+
123
+ public class NumberBoxEvent : UnityEvent<string>
124
+
125
+ {
126
+
127
+ }
128
+
129
+
130
+
131
+ public class NumberBoxController : MonoBehaviour
132
+
133
+ {
134
+
135
+ public string Character; // 箱が表す数字、インスペクタで箱ごとに設定しておく
136
+
137
+ public NumberBoxEvent OnEnterNumber; // 衝突時にこのイベントを発生させる
138
+
139
+ private NumberPaneController numberPaneController; // ※箱の見た目が同じだと画面上で区別が付かないので、子オブジェクトとして数字表示板を持たせた
140
+
141
+
142
+
143
+ // 衝突発生時に物理シミュレーションエンジンによって自動的に呼び出される
144
+
145
+ private void OnCollisionEnter(Collision collision)
146
+
147
+ {
148
+
149
+ if (collision.gameObject.tag == "Player") // ボールには「Player」タグを付けておき、ボールとの衝突だけに応答する(箱同士や地面・壁との衝突があっても何もしない)
150
+
151
+ {
152
+
153
+ this.numberPaneController.Lit = true; // ※数字表示板を点灯させて衝突したことを分かりやすくした
154
+
155
+ this.OnEnterNumber.Invoke(this.Character); // Characterを引数にして数字入力イベントを起こす
156
+
157
+ }
158
+
159
+ }
160
+
161
+
162
+
163
+ // 衝突終了時に物理シミュレーションエンジンによって自動的に呼び出される
164
+
165
+ private void OnCollisionExit(Collision collisionInfo)
166
+
167
+ {
168
+
169
+ if (collisionInfo.gameObject.tag == "Player")
170
+
171
+ {
172
+
173
+ this.numberPaneController.Lit = false; // ※ボールが箱から離れたら数字表示板を消灯する
174
+
175
+ }
176
+
177
+ }
178
+
179
+
180
+
181
+ private void Start()
182
+
183
+ {
184
+
185
+ if (this.OnEnterNumber == null)
186
+
187
+ {
188
+
189
+ this.OnEnterNumber = new NumberBoxEvent();
190
+
191
+ }
192
+
193
+ if (this.numberPaneController == null)
194
+
195
+ {
196
+
197
+ this.numberPaneController = this.GetComponentInChildren<NumberPaneController>();
198
+
199
+ }
200
+
201
+ this.numberPaneController.CharacterIndex =
202
+
203
+ NumberPaneController.GetIndexForCharacter(string.IsNullOrEmpty(this.Character) ? ' ' : this.Character[0]);
204
+
205
+ this.numberPaneController.Lit = false;
206
+
207
+ }
208
+
209
+
210
+
211
+ private void Update()
212
+
213
+ {
214
+
215
+ }
216
+
217
+ }
218
+
219
+ ```
220
+
221
+
222
+
223
+ ゲートのスクリプト
224
+
225
+ ```C#
226
+
227
+ using UnityEngine;
228
+
229
+
230
+
231
+ [RequireComponent(typeof(Animator))]
232
+
233
+ public class GateController : MonoBehaviour
234
+
235
+ {
236
+
237
+ private const float BlinkingInterval = 0.5f; // ※Accepted、Deniedの時の数字表示点滅間隔
238
+
239
+ public NumberPaneController[] NumberPaneControllers; // ※ゲート前面にある4枚の数字表示板、インスペクタ上で割り当てておく
240
+
241
+ public string PasswordString; // パスワード文字列、インスペクタ上で決めておく
242
+
243
+ private Animator animator; // ※ゲート用のアニメーター
244
+
245
+ private string playersAnswer; // 入力されたパスワード候補文字列
246
+
247
+ private float time; // ※数字表示板点滅のための時間格納用変数
248
+
249
+
250
+
251
+ // 箱とボールの衝突発生時に箱で発生するOnEnterNumberに対応して、これを実行するようインスペクタで設定しておく
252
+
253
+ public void EnterDigit(string character)
254
+
255
+ {
256
+
257
+ if (!this.animator.GetCurrentAnimatorStateInfo(0).IsName("WaitingForInput"))
258
+
259
+ {
260
+
261
+ return; // パスワード入力待ち状態以外では何もしない
262
+
263
+ }
264
+
265
+ if (string.IsNullOrEmpty(character))
266
+
267
+ {
268
+
269
+ return;
270
+
271
+ }
272
+
273
+
274
+
275
+ this.playersAnswer += character; // characterには入力された文字が渡されるので、これをplayersAnswerに連結する
276
+
277
+ Debug.Log("Password : " + this.playersAnswer);
278
+
279
+ this.UpdateDisplay(); // ※playersAnswerを書き換えたので、ゲート前面の数字表示板を更新する
280
+
281
+ if (this.playersAnswer.Length >= this.PasswordString.Length)
282
+
283
+ {
284
+
285
+ if (this.playersAnswer == this.PasswordString)
286
+
287
+ {
288
+
289
+ Debug.Log("Accepted!");
290
+
291
+ this.animator.SetTrigger("PasswordAccepted"); // ※パスワードが正しければAccepted状態に遷移、その後アニメーターによって自動的にゲートを開くモーションに遷移する
292
+
293
+ }
294
+
295
+ else
296
+
297
+ {
298
+
299
+ Debug.Log("Denied!");
300
+
301
+ this.animator.SetTrigger("PasswordDenied"); // ※パスワードが間違っていればDenied状態に遷移、その後アニメーターによって自動的にパスワード入力待ちに戻る
302
+
303
+ }
304
+
305
+ this.playersAnswer = "";
306
+
307
+ }
308
+
309
+ }
310
+
311
+
312
+
313
+ // ※ゲート前面数字板表示を一斉に変えるためのメソッド...アニメーター上のステートにStateMachineBehaviourが仕掛けてあり、そこで使用している
314
+
315
+ public void ResetDisplay(string character)
316
+
317
+ {
318
+
319
+ this.time = 0.0f;
320
+
321
+ if (character == null)
322
+
323
+ {
324
+
325
+ this.UpdateDisplay();
326
+
327
+ }
328
+
329
+ else
330
+
331
+ {
332
+
333
+ var paneCount = this.NumberPaneControllers.Length;
334
+
335
+ var characterIndex = NumberPaneController.GetIndexForCharacter(character[0]);
336
+
337
+ for (var i = 0; i < paneCount; i++)
338
+
339
+ {
340
+
341
+ var pane = this.NumberPaneControllers[i];
342
+
343
+ if (pane == null)
344
+
345
+ {
346
+
347
+ continue;
348
+
349
+ }
350
+
351
+
352
+
353
+ pane.CharacterIndex = characterIndex;
354
+
355
+ }
356
+
357
+ }
358
+
359
+ }
360
+
361
+
362
+
363
+ // ※ゲート前面数字板の点灯状態を変えるためのメソッド...Accepted・Denied時の点滅エフェクト用
364
+
365
+ private void InvertLit()
366
+
367
+ {
368
+
369
+ var paneCount = this.NumberPaneControllers.Length;
370
+
371
+ for (var i = 0; i < paneCount; i++)
372
+
373
+ {
374
+
375
+ var pane = this.NumberPaneControllers[i];
376
+
377
+ if (pane == null)
378
+
379
+ {
380
+
381
+ continue;
382
+
383
+ }
384
+
385
+
386
+
387
+ pane.Lit = !pane.Lit;
388
+
389
+ }
390
+
391
+ }
392
+
393
+
394
+
395
+ private void Start()
396
+
397
+ {
398
+
399
+ this.animator = this.GetComponent<Animator>();
400
+
401
+ this.playersAnswer = "";
402
+
403
+ this.UpdateDisplay();
404
+
405
+ }
406
+
407
+
408
+
409
+ private void Update()
410
+
411
+ {
412
+
413
+ var stateInfo = this.animator.GetCurrentAnimatorStateInfo(0);
414
+
415
+ if (stateInfo.IsName("Denied") || stateInfo.IsName("Accepted"))
416
+
417
+ {
418
+
419
+ // ※Accepted・Deniedの時は数字表示板を点滅させて目立たせる
420
+
421
+ this.time += Time.deltaTime;
422
+
423
+ if (this.time > BlinkingInterval)
424
+
425
+ {
426
+
427
+ this.time -= BlinkingInterval;
428
+
429
+ this.InvertLit();
430
+
431
+ }
432
+
433
+ }
434
+
435
+ }
436
+
437
+
438
+
439
+ // ※現在のplayersAnswerに合わせて数字板の表示を切り替えるメソッド
440
+
441
+ private void UpdateDisplay()
442
+
443
+ {
444
+
445
+ var paneCount = this.NumberPaneControllers.Length;
446
+
447
+ var answerLength = string.IsNullOrEmpty(this.playersAnswer) ? 0 : this.playersAnswer.Length;
448
+
449
+ for (var i = 0; i < paneCount; i++)
450
+
451
+ {
452
+
453
+ var pane = this.NumberPaneControllers[i];
454
+
455
+ if (pane == null)
456
+
457
+ {
458
+
459
+ continue;
460
+
461
+ }
462
+
463
+
464
+
465
+ pane.CharacterIndex =
466
+
467
+ NumberPaneController.GetIndexForCharacter(i < answerLength ? this.playersAnswer[i] : '?');
468
+
469
+ pane.Lit = false;
470
+
471
+ }
472
+
473
+ }
474
+
475
+ }
476
+
477
+ ```
478
+
479
+ 箱のインスペクタ
480
+
481
+ ![箱のインスペクタ](72d789809ca7dfe0ec9a42d86578d93f.png)
482
+
483
+
484
+
485
+ ゲートのインスペクタ
486
+
487
+ ![ゲートのインスペクタ](f7ffba2b545bb04f17af3d29253ff9db.png)
488
+
489
+
490
+
491
+ 実行時の様子
492
+
493
+ ![実行時の様子](77587672d46d533dee4dad8b0456a997.gif)