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

回答編集履歴

1

実験結果を追記

2021/01/11 14:57

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -4,4 +4,199 @@
4
4
  - 交換対象の番号がいずれも現在の正解カップ番号と異なる場合は、正解カップ番号を変更しない...たとえば現在の正解カップ番号が2の状態で、カップ1とカップ3の交換が行われた時には、正解カップ番号は2のまま維持する。
5
5
  - 交換対象の番号のいずれかが現在の正解カップ番号と一致する場合は正解カップ番号も入れ替える...たとえば現在の正解カップ番号が2の状態で、カップ1とカップ2の交換が行われた時には、正解カップ番号は1に変更する。現在の正解カップ番号が1の状態で、カップ1とカップ3の交換が行われた時には、正解カップ番号は3に変更する。
6
6
 
7
- というルールで正解カップ番号を入れ替えていけば、シャッフルが終わった時点で何番目のカップが正解であるか分かるのではないでしょうか。
7
+ というルールで正解カップ番号を入れ替えていけば、シャッフルが終わった時点で何番目のカップが正解であるか分かるのではないでしょうか。
8
+
9
+ ##追記
10
+
11
+ 何かご参考になれば...と思いまして、下図のような実験シーンを作ってみました。
12
+
13
+ ![図1](29a4107c7e37202d01e8fd363588dc7b.gif)
14
+
15
+ 3つのカップは空のオブジェクトの子オブジェクトとしてカップのモデルと景品のモデルを配置したものです。今回の実験ではカップ内に最初から景品を仕込んでおき、ハズレのカップは景品を非表示にすることで当たりのカップだけに景品が入っているように見せることにしました。
16
+ また、カップの動きを画面上で確認しやすくするため、各カップにはそれぞれ別々の色を付けました。実際のゲームだと、カップの見た目が違うと当たりカップを簡単に見破られてしまってまずいでしょうね。
17
+ これに下記のようなカップを開閉して景品を見せる動きをさせるためのスクリプトをアタッチしています。
18
+
19
+ ```C#
20
+ using System.Collections;
21
+ using UnityEngine;
22
+
23
+ public class Mug : MonoBehaviour
24
+ {
25
+ [SerializeField] private float openHeight = 2.0f;
26
+ [SerializeField] private float animationLength = 1.0f;
27
+
28
+ private Coroutine currentAnimation;
29
+ private bool hasPrize;
30
+ private Transform mugModel;
31
+ private Transform prize;
32
+
33
+ public bool HasPrize
34
+ {
35
+ get => this.hasPrize;
36
+ set
37
+ {
38
+ this.prize.gameObject.SetActive(value);
39
+ this.hasPrize = value;
40
+ }
41
+ }
42
+
43
+ private void Awake()
44
+ {
45
+ this.mugModel = this.transform.Find("MugModel");
46
+ this.prize = this.transform.Find("Prize");
47
+ }
48
+
49
+ public void Open()
50
+ {
51
+ if (this.currentAnimation != null)
52
+ {
53
+ this.StopCoroutine(this.currentAnimation);
54
+ }
55
+
56
+ this.currentAnimation = this.StartCoroutine(this.DoAnimation(this.openHeight));
57
+ }
58
+
59
+ public void Close()
60
+ {
61
+ if (this.currentAnimation != null)
62
+ {
63
+ this.StopCoroutine(this.currentAnimation);
64
+ }
65
+
66
+ this.currentAnimation = this.StartCoroutine(this.DoAnimation(0.0f));
67
+ }
68
+
69
+ private IEnumerator DoAnimation(float destinationHeight)
70
+ {
71
+ var initialHeight = this.mugModel.localPosition.y;
72
+ for (var time = 0.0f; time < this.animationLength; time += Time.deltaTime)
73
+ {
74
+ var t = time / this.animationLength;
75
+ this.mugModel.localPosition = new Vector3(
76
+ 0.0f,
77
+ Mathf.SmoothStep(initialHeight, destinationHeight, t),
78
+ 0.0f);
79
+ yield return null;
80
+ }
81
+
82
+ this.mugModel.localPosition = new Vector3(0.0f, destinationHeight, 0.0f);
83
+ this.currentAnimation = null;
84
+ }
85
+ }
86
+ ```
87
+
88
+ Operatorがカップのシャッフル役で、下記のようなスクリプトをアタッチしました。インスペクター上で`mugs`にシーン上の3つのカップをセットしています。
89
+
90
+ ```C#
91
+ using System;
92
+ using System.Collections;
93
+ using System.Collections.Generic;
94
+ using System.Linq;
95
+ using UnityEngine;
96
+ using Random = UnityEngine.Random;
97
+
98
+ public class Operator : MonoBehaviour
99
+ {
100
+ [SerializeField] private float animationLength = 1.0f;
101
+ [SerializeField] private Mug[] mugs;
102
+
103
+ private int bingoIndex;
104
+
105
+ private IEnumerator Start()
106
+ {
107
+ // まずランダムな当たり番号を決め、その番号のカップに景品を仕込む
108
+ this.bingoIndex = Random.Range(0, this.mugs.Length);
109
+ for (var i = 0; i < this.mugs.Length; i++)
110
+ {
111
+ this.mugs[i].HasPrize = i == this.bingoIndex;
112
+ }
113
+
114
+ // ここで確認のためにコンソールに当たり番号を出力してみる
115
+ Debug.Log($"Bingo index: {this.bingoIndex}");
116
+
117
+ yield return new WaitForSeconds(4.0f);
118
+
119
+ // カップを全部開き、中身を見せてみる
120
+ foreach (var mug in this.mugs)
121
+ {
122
+ mug.Open();
123
+ yield return new WaitForSeconds(0.5f);
124
+ }
125
+ yield return new WaitForSeconds(2.0f);
126
+
127
+ // カップを全部閉じ...
128
+ foreach (var mug in this.mugs)
129
+ {
130
+ mug.Close();
131
+ yield return new WaitForSeconds(0.5f);
132
+ }
133
+ yield return new WaitForSeconds(2.0f);
134
+
135
+ // ランダムに選んだ2つの番号を入れ替える操作を10回行う
136
+ var randomIndices = new List<int>();
137
+ for (var i = 0; i < 10; i++)
138
+ {
139
+ randomIndices.Clear();
140
+ randomIndices.AddRange(
141
+ Enumerable.Range(0, this.mugs.Length)
142
+ .Select(i => (i, guid: Guid.NewGuid()))
143
+ .OrderBy(p => p.guid)
144
+ .Select(p => p.i).Take(2));
145
+ yield return this.Swap(randomIndices[0], randomIndices[1]);
146
+
147
+ // 1回交換するたびにコンソールに当たり番号を出力してみる
148
+ Debug.Log($"Bingo index: {this.bingoIndex}");
149
+ }
150
+ yield return new WaitForSeconds(2.0f);
151
+
152
+ // カップを全部開き、中身を見せてみる
153
+ foreach (var mug in this.mugs)
154
+ {
155
+ mug.Open();
156
+ yield return new WaitForSeconds(0.5f);
157
+ }
158
+ }
159
+
160
+ private IEnumerator Swap(int indexA, int indexB)
161
+ {
162
+ // 2つのカップが半円を描くようにアニメーションさせ...
163
+ var mugA = this.mugs[indexA];
164
+ var mugB = this.mugs[indexB];
165
+ var transformA = mugA.transform;
166
+ var transformB = mugB.transform;
167
+ var positionA = transformA.position;
168
+ var positionB = transformB.position;
169
+ var center = (positionA + positionB) * 0.5f;
170
+ var vectorA = positionA - center;
171
+ var vectorB = positionB - center;
172
+ for (var time = 0.0f; time < this.animationLength; time += Time.deltaTime)
173
+ {
174
+ var t = time / this.animationLength;
175
+ var r = Quaternion.Euler(0.0f, Mathf.SmoothStep(0.0f, 180.0f, t), 0.0f);
176
+ transformA.position = center + (r * vectorA);
177
+ transformB.position = center + (r * vectorB);
178
+ yield return null;
179
+ }
180
+ transformA.position = center + vectorB;
181
+ transformB.position = center + vectorA;
182
+
183
+ // mugs配列上の2つのカップを入れ替え...
184
+ this.mugs[indexA] = mugB;
185
+ this.mugs[indexB] = mugA;
186
+
187
+ // 必要に応じ当たり番号を更新する
188
+ if (this.bingoIndex == indexA)
189
+ {
190
+ this.bingoIndex = indexB;
191
+ }
192
+ else if (this.bingoIndex == indexB)
193
+ {
194
+ this.bingoIndex = indexA;
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ 動かしてみると、カップ入れ替え操作によって`mugs`配列の順序が変化していき、併せて`bingoIndex`の値も変化していきました。
201
+
202
+ ![図2](4e4b3f25bfe9dea4f8d40b6cece9d5d4.gif)