マケプレのセールで買ったアセットが4.18未対応で泣いた。
ブループリントで乱数を生成するには、Rand Float in Range 等を使う。で、この ブログ を見ると分かる通り、帰ってくる乱数は例えば1-10までの整数乱数を生成するとだいたいどの数字も同じ確率で出現する。これを一様分布に従う一様乱数って言ったりする。まあ読んで字の如くなので難しくはない。
しかし一様乱数だけしかBPで使えないってのは不便。例えばコマンド式のRPG(ドラクエみたいな)でダメージ計算する時、計算結果に乱数を乗じて実際敵に与えるダメージに幅を持たせるといったことをするがこの時乗じる乱数が一様乱数だとどうも違和感が出てしまう。他にも敵キャラが自分に向けて銃で遠くから攻撃する場合、毎回正確にプレイヤーに弾が飛んできたらたまったもんじゃないので乱数を使って照準のズレをもたせたくなる。この場合も一様乱数を使うと外す確率も当たる確率も同じになってしまい、プレイヤーはこれなら殆ど弾飛んでこないし特攻しても大丈夫だろう、と思われかねない(勿論この心理を逆についてプレイヤーとの距離が閾値を超えたら乱数のレンジを狭めエイム精度を大幅に上げるみたいなことも有りだろうが)。
というわけでゲームで使う乱数はある程度偏りを持っていてほしいとなるのである。それもある値が頻繁に出現する乱数が。
で作り方。
Float型乱数を5個取ってきてその平均をとるだけ。これでMaxとMinのちょうど真ん中の値(Max100,Min-100なら0)付近が頻繁に出現し、MaxとMinに近づくほどその値の出現頻度が低い乱数が生成できる。
これを応用してTwinStickShooterテンプレートの敵キャラAI(制作の様子は 過去ブログ 参照)。
このエネミーはプレイヤーの位置と自身の位置を取得してベクトル減算して攻撃の単位方向ベクトルを得ているのだが、この時使用するプレイヤーの位置ベクトルを「Break Vector」し、X座標の値だけ「GaussRandomFloatInRange」で取得した乱数(エイムの振れ幅)を足してベクトルに戻している。こうすることでエネミーの攻撃を「違和感なく」散らすことができた。
UE4での偏った乱数の生成方法の話はおしまい。以下ちょっとしたなんでこうなるかの理論の話と本当に偏った乱数を生成できるのかExcelで実験する話なので興味のない方は読み飛ばして頂いて構いません。
なんで偏った乱数になるのか
ここで擬似的に生成している乱数というのは正規分布に従う乱数、正規乱数と言われるもので、なぜこうなるか説明するには中心極限定理やら大数の法則の話をしなきゃならない。更にいうともっと正確な(厳密に正確ではない。PCで作る乱数はあくまで擬似的なもので真に正しい乱数は作れない)正規乱数を作るには一様乱数から正規乱数を取得できる Box-Muller法(wikipedia) を使ったほうがいいのだがlogやらcosやら出てくるのでBPで実装するのはよろしくない。こんな面倒なことをしなくてもC++の乱数ライブラリstd::random で正規乱数が生成できるらしいが現時点で筆者がC++を書けないのでこのやり方は説明できない(このせいでUE4のソースコードから乱数生成アルゴリズム読み解こうとしたのにさっぱりわからなかった。いつかリベンジしたい)。
中心極限定理はざっくり言えば
X が平均 μ,標準偏差 σ のある分布に従うならば,大きさ n の無作為標本に基づく標本平均 は,n が無限に大きくなるとき,平均μ,標準偏差 σ/√n の正規分布に近づく。
とのこと。わかり辛い。ようはある分布に従ってるサンプルを幾つか取ってきて得た平均を並べると正規分布っぽくなりますよって話。ここでは一様分布に従う乱数を5個取ってきて得た平均が正規分布に従っているように見えることを利用して正規乱数を得ていたのである。
Excelで検証
UE4で検証した方が良いのだろうがBPで実装する時間がないのですぐできるExcelで検証してみた。
○ Rand()で生成した乱数10000個のヒストグラム
○ ( Rand() + Rand() + Rand() + Rand() + Rand() ) / 5で生成した乱数10000個のヒストグラム
あとがき
まだまだUE4を触りだして間もないがBPは偉大だ。しかしやはりC++は知っていると便利。いつか勉強したい(するのだろうか)。
ちなみに以前乱数について簡単なスライドを作成して公開したので寒いネタのオンパレードですが興味のある方は是非見て下さい。