
前回はプロジェクト「0190_randomize」を新しく作り、疑似乱数についてお話ししました。
今回は、新しくプロジェクト「0195_generate_rand_with_range」を作り、前回からの続きで任意の幅で疑似乱数を生成する方法についてお話します。疑似乱数は、ランダムに生成する地形や、出現する敵など、いろいろなゲーム要素で使われる重要な概念です。
実際にプログラムを動かして結果を見てみましょう。プロジェクト「0195_generate_rand_with_range」を新しく作成し、ソースコード「RangedRandomize.c」を作成してみてください。
#include <stdio.h>
#include <stdlib.h>
int main() {
srand(12345);
printf("〇か×かを乱数で表示:");
for (int i = 0; i < 10; i++) {
int kisuuGuusuu = rand() % 2;
if (kisuuGuusuu == 0) {
printf("〇");
} else {
printf("×");
}
}
printf("\n");
printf("与えるダメージを乱数で表示:");
int baseDamage = 10; // 必ず与える基礎ダメージ
int habaDamage = 4; // 与えるダメージの幅
for (int j = 0; j < 5; j++) {
int damage = rand() % ( habaDamage + 1 ) + baseDamage;
printf("%d, ", damage);
}
printf("\n");
}
このコードを実行してみるとこんな結果になったかと思います。
〇か×かを乱数で表示:〇〇××〇×××〇〇
与えるダメージを乱数で表示:14, 10, 13, 12, 14,
〇か×かをそれぞれ確率50%で表示しています。(実際には rand() 関数のなかで使われている疑似乱数生成法によって偏りがあったりします。)
またダメージを10~14の幅でランダムに生成して表示しています。
実行するパソコンの環境によっては、違う結果が出てくるかもしれません。
前回お話した、乱数を生成する rand() 関数ですが、0 ~ RAND_MAX の間の整数を生成する機能を持っていました。
実際にゲームなどで乱数を使うのであれば、0~10だったり、200~220 など、目的によって指定した範囲の中で乱数を生成してほしいですよね。
rand() 関数を使って 0 か 1 の 2パターンを生成するには、下の計算で実現できます。
rand() % 2
ランダムに生成される rand() を 2 で割った余りですね。2で割った余りの結果は 0 か 1 のみとなるため、 0 か 1 の 2 パターンを生成することができました。
例えば 10から30の範囲で乱数を生成したい時は、さきほどの 0 か 1 の乱数を生成する式を少し応用してあげることで実現できます。
rand() % 21
という式で、まず 0~20 の範囲で乱数を取り出すことができますね。
これに対して、10 を足すと、 範囲は 0+10 ~ 20+10 の乱数が生成されることになります。
rand() % 21 + 10
結果として、20 ~ 30 の範囲が生成できました。もうすこし式を汎用的にしてみると、
rand() % ( 生成したい数値の幅 + 1 ) + 生成したい下限値
となります。
今回のソースコードの中を追ってみましょう。2行目ですが、rand関数、srand関数を使うためにはソースコードの頭に #include <stdlib.h> を書く必要があります。
#include <stdlib.h>
これで rand 関数とsrand 関数がつかえるようになりました。
4行目で srand 関数を使用しています。
前のページでお話しましたが、srand 関数は乱数の種を設定する関数です。設定した値に従って乱数の系列を生成します。
今回は適当に乱数の種は「12345」で設定しました。
srand(12345);
次に、for 文と if~else文、rand文を組み合わせて、〇×をランダムで表示している箇所を見ていきましょう。
printf("〇か×かを乱数で表示:");
for (int i = 0; i < 10; i++) {
int kisuuGuusuu = rand() % 2;
if (kisuuGuusuu == 0) {
printf("〇");
} else {
printf("×");
}
}
printf("\n");
5行目で「〇か×かを乱数で表示:」という文字を表示しています。右側に改行は入っていません。
6行目~13行目は for 文でのループ処理となっています。
for ( int i = 0 ; i < 10 ; i++ ) {…}
ですので、 i が 0~9の間繰り返し処理を実行する(つまり合計 10 回実行される)ことになります。
7行目で、整数 kisuuGuusuu に 「rand() % 2」 の結果を代入しています。 「rand() % 2 」は「rand() で生成された乱数を、2で割った余り」ですので、答えは 0 か 1 になりすね。kisuuGuusuu に 0 か 1 が代入されることになります。
8行目~12行目は if ~ else 文です。こちらのページでお話した、条件によって違う処理をさせたい時に使う文を使っています。
9行目は、「もし kisuuGuusuu が 0」のときのみ実行されます。
11行目は、「もし kisuuGuusuu が 0 でないとき」のみ実行されます。
このランダムに〇か×を表示する処理を 10 回ほど繰り返し、for 文が完了します。
14行目の printf("\n"); で改行され、見た目をわかりやすく区切っています。
ソースコードの続きで、今度は指定した範囲の整数をランダムで生成しています。
printf("与えるダメージを乱数で表示:");
int baseDamage = 10; // 必ず与える基礎ダメージ
int habaDamage = 4; // 与えるダメージの幅
for (int j = 0; j < 5; j++) {
int damage = rand() % ( habaDamage + 1 ) + baseDamage;
printf("%d, ", damage);
}
printf("\n");
15行目は「与えるダメージを乱数で表示:」を改行なしで表示しています。
16~17行目では変数を宣言&初期化しています。
18~21行目では for 文で繰り返し処理を行っています。
「for ( int j = 0 ; j < 5 ; j++ ) {…}」ですので、j が 0 から 4 の間、繰り返し処理を行うことになりますね。
19行目では下のような式になっています。
damage = rand() % ( habaDamage + 1 ) + baseDamage;
右辺の「rand() % ( habaDamage + 1 )」は「rand() % 5」と置き換えられます。乱数を 5 で割った余りですので、答えは 0~4 の乱数となります。
「0~4の乱数 + baseDamage」は「10~14の乱数」であり、変数 damage に結果が代入されます。
20行目では乱数の結果を表示しています。
「printf("%d, ", damage);」で 「damage の中身」+「,」カンマ を表示して、一つ一つのダメージがわかりやすいようにしています。
for 文が終了した後、22行目で改行して結果を読みやすくしています。
今回はゲームで非常によく使われる rand 関数の使い方をお話しました。実際にゲーム内で使うときは、ゲームの環境によって乱数の結果が変わらないように、自分で乱数を生成するプログラムを書いてしまうことが多いと思います。
(乱数で生成されるマップデータなどを考えた時、別のPC上にデータを持って行って遊ぼうとしたらまったく違うマップになってしまっては困るなどの場合があります。)
更新日 | 更新内容 |
---|---|
2018.7.31 | 初版公開 |
2018.11.25 | ソースコードのインデックスがおかしかったのを修正 アイキャッチ画像、トップ画像を追加 |
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: