【Unity】ゲームといえばランダム要素。Randomクラスを使って遊ぼう
ゲームで大事な要素といえばランダム要素。
RPGならランダムな確率でアイテムドロップが発生したり、エンカウントがランダムな歩数で発生したりとほぼ全ての要素がランダムです。
モンハンなら逆鱗が出る確率にも関わってきますし、ソシャゲならSSRが出る確率にも関わってきます。
ゲームにおいてこうしたランダム要素を実現しているのが乱数の存在です。
Unityではランダムな値を生成するためのRandomクラスを内蔵しており、簡単に乱数を発生させることができます。今回はこれを使って遊んでみたいと思います。
環境
macOS 10.13 High Sierra
Unity2018.1.0f2
乱数について
コンピュータは万能のようでいて、決められたことしかできないという弱点があります。
乱数もその弱点のひとつで、コンピュータは次に何が起こるか分からない完全にランダムな状態を自分で作れません。
なので、擬似的に乱数を用意して、あたかもランダムにイベントが起きているように振舞っているんです。
スーファミのゲームとかだと、カセット内に乱数表が用意されていて、それを使ってランダムな要素を表現しています。
例えばロマサガ3では閃きのタイミングやら、ダメージのぶれ、アイテムの入手タイミングに乱数を使用しており、1024個の乱数表から値をピックアップして各種計算に使っています。
ピックアップした値を100で割った余りの数字を使っているので、竜槍スマウグがなかなか出ないのも、黄龍剣をなかなか閃かないのも、この乱数を使った判定のせいなのです。
スーファミだとカセット内に乱数表を値の配列として保持する必要がありましたが、これってなんらかのアルゴリズムで自動的に生成してくれたら便利ですよね。
その便利さを実現しているのがUnityのRandom関数なんです。
Randomの使い方
使い方はとっても簡単。
ランダムな値が欲しい時にRandom.valueを使うだけ。これで0fから1.0fまでの間にあるランダムな値を返してくれます。1.0fも含むことに注意。
例えば上のスクリプトであれば、ゲーム開始時にランダムな値を表示してくれます。
実際に実行してみると、毎回値が変わっているのが分かります。
このように変動する値を使えば、ゲーム内でランダムな要素を扱うことができます。
サイコロの目をランダムに取得する
ランダム感をイメージしやすいのは、サイコロをふることでしょうか。6面ダイスであれば、6つの目が同じ確率で出ます。
これを簡単に取得する方法として、Random.Rangeを使います。
Random.Rangeは指定した範囲の値をランダムに取得することができ、今回のようにサイコロの目を取得するのに便利です。
Random.Rangeの指定方法はfloat型の引数を使う方法と、int型の引数を使う方法の2通りがあります。
float型を引数に取る場合は、Random.Range(0f, 50f)のように最小値と最大値を指定します。float型の場合は最小値も、最大値も含まれます。
int型の場合は、Random.Range(0, 10)のように最小値と最大値を指定する点はfloat型と同じですが、返ってくる値の中に最大値は含みません。Random.Range(0, 10)なら、0から9が返ってきます。ここは注意。
サイコロの目は配列で用意するのが簡単なので、int型でRandom.Rangeを使ってインデックスを指定するのが楽ちんです。
Unityのマニュアルで『ランダムなゲームプレイ要素の追加』として、Randomクラスの使い方を掲載してくれているので、それを参考にしつつ以下のようにスクリプトを作りました。
このRollADice()をStart()の中で呼ぶと、ゲームを実行するたびに違う目が出るはず。
うまくいきました。範囲指定でもランダムな値が取得されます。
確率の偏りはないかな
乱数といえば偏りがあるのが世の常。
どうしてもしょうがない部分はありますが、UnityのRandomクラスではどれくらいの偏りがあるんでしょうか。
と気になったのでデータを取って確かめてみましょ。
確認方法
Unityにサイコロを何回も投げてもらって、それぞれの目が出る確率に大きな差がないことを確認します。
具体的には、上記のRollADice()を決められた回数呼び出し、その時に各目が出た確率を計算します。
デバッグ文としてコンソールに出力したものをExcelで集計、とかだと苦行なので、csvで出力することとします。
試行回数
試行回数に関してはあまり深く考えず、回数が多ければいいので、1セットあたり10000回にしておきましょうか。
セット数は1000回もあれば大丈夫ですかね。
CSVへの出力
UnityではSystem.IOを使ってファイルへの書き込みを行うことができます。
「Unityでは」というよりはC#では、というのが正しいですね。
RPGなどを作っている時に、敵のパラメータ一覧やアイテム一覧を出力して、設定値とあっているか確認すると楽ちんです。
スクリプト
Unityにガンガンサイコロを振ってもらうスクリプトはこちら。ちょっと長くなっちゃった。
メンバ変数として、サイコロを振ってそれぞれの目が出た回数を記録する辞書と、それを確率に直した値を保持する辞書を追加しました。
この辞書を使って値を保存し、出力する文字列を生成しています。
セット数と、1セットあたりの試行回数はInspectorで設定できるようにしています。
スクリプトの流れは、CSVファイルを開く -> 辞書を初期化 -> サイコロを振って出た目を記録 -> 確率に直してファイルに書き込み -> セット数分ループ -> ファイルを閉じて終了、となっています。
CSVファイルを出力した後は、MacのNumbersでグラフ化します。うちのMac、Excel入れてないんです……。
実行結果
1セットあたりの試行回数10000回、それを1000セット行なった結果が以下のもの。3回やってみたけど、だいたいいいところにいる気がします(主観)
だいたい±0.02%くらいの範囲にはいるので、多分人が振っても似たような確率になるはず。
試行回数が大きいせいもあるかもしれませんが、確率の面であからさまな偏りはなさそう。
念のためサンプル数を変える
試行回数が増えれば安定するのは確かにそうですが、念のため少ないサンプル数でもやってみます。
こっちはグラフ化せず確率を表で載せる感じで。
試行回数 |
サイコロの目 |
|||||
1 |
2 |
3 |
4 |
5 |
6 |
|
1 |
0% |
0% |
0% |
0% |
100% |
0% |
10 |
20.0% |
20.0% |
0% |
30.0% |
30.0% |
0% |
100 |
15.00% |
15.00% |
7.00% |
21.00% |
23.00% |
19.00% |
1000 |
16.30% |
16.30% |
14.20% |
16.40% |
16.60% |
20.20% |
10000 |
16.61% |
16.59% |
17.65% |
15.57% |
17.01% |
16.57% |
100000 |
16.738% |
16.640% |
16.694% |
16.596% |
16.445% |
16.887% |
セット数を1にしたのである程度ばらつきがあるものの、100回サイコロを投げて1回も6が出ない、なんてこともないみたい。
流石に10回だと出ない目があるけど、10回投げてひとつの目が出ない確率は約50.6%、ふたつの目が出ない確率は約20.3%だから割とよくあるケース。
なんとなく1から6まで満遍なく出るような気がしてしまいますが、冷静に考えたらそれぞれの目が出る確率は独立だからそんなことないんですよね。全部の目が揃う期待値も14.7回ですし、10回投げて6つの数字が揃う確率は27.14%。結構低いんですぜ、旦那。
確率論的にはそうであっても、ゲームをやっている時に遭遇すると、多分冷静に見れず「乱数が偏ってる!」と感じてしまうかも。
まとめ
Unityで簡単に使えるRandomクラスの使い方はRandom.valueで値を取得するだけ。
今回はサイコロのように等確率の事象について実験を行いました。
マクロ的には確率が収束しているのでばらつきはなさそう……なのですが、ミクロ的にはぶれることがあるので、人間の感覚で言うと偏りがあるように感じるかも。
もうちょっと調査してみたいなー。
おまけ
乱数を使った脱出ゲームを作りました。脱出に必要なのはゴールを引き当てる運だけです。
ブラウザで遊べるのでこちらも是非。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity】Capsule Colliderのパラメータについて。向きも変えられるよ 2018.06.16
-
次の記事
GitHubでアラート! Your account has been flaggedが表示されたよ 2018.06.21
コメントを書く