dポイントプレゼントキャンペーン実施中!

ゲームプログラミングで、乱数を使っていて、どうしてもできない事があるのですが、例えば複数の物があるとします。その物を、乱数により位置を決めたいのですが、乱数で複数個の位置を繰り返し、取得したいのですが、いかんせん、値が更新しないのです。もうまったくどのようにしたらいいのかわかりません。どなたか、教えてくれませんか?

A 回答 (11件中1~10件)

#7回答者です。

レスポンスが無いと回答される方が少なくなる傾向ですね。とりあえずサンプル書いてみましたので参考にして下さい。サンプルは繰り返し呼ばれることを想定しています。

static int srandCallFlag = 0;
static int state = 1;
static int saveValueX;
static int saveValueY;
static int saveValueZ;
static int waitCount = 0;

int c1;
int d2;

if (0 == srandCallFlag) {
srandCallFlag = 1;
srand((unsigned)time(NULL));
}

switch (state)
{
case 1 : // 1.乱数を使いX,Y座標表を決める
c1 = (A1 + B1) / 2;
d1 = (B1 - A1) / 2;
saveValueX = c1 + d1 * ((float)rand() / RAND_MAX - 0.5) *2;
saveValueY = c1 + d1 * ((float)rand() / RAND_MAX - 0.5) *2;
saveValueZ = c1 - d1; // 次回使用するので保存しておく
saveMaxZ = c1 + d1;
object->_SIO2transform->loc->x = saveValueX; // X軸
object->_SIO2transform->loc->y = saveValueY; // Y軸
object->_SIO2transform->loc->z = saveValueZ; // Z軸
state = 2; // 次に状態を移す
break;
case 2 : // 2.Z軸移動
object->_SIO2transform->loc->x = saveValueX;
object->_SIO2transform->loc->y = saveValueY;
saveValueZ += 1; // 加算して移動させる
object->_SIO2transform->loc->z = saveValueZ;
if (saveValueZ >= saveMaxZ) {
state = 1; // 繰り返し処理を行う
break;
}
state = 3; // 表示ウェイトが必要であればこの処理を行う
waitCount = 10000; // 表示ウェイトが必要であればこの処理を行う。数値は10000回case3処理を繰り返す
break;
case 3 : // 3.表示ウェイトが必要な場合
object->_SIO2transform->loc->x = saveValueX;
object->_SIO2transform->loc->y = saveValueY;
object->_SIO2transform->loc->z = saveValueZ;

waitCount--;
if ( 0 >= waitCount ) { // waitCountが0以下になるとstateが変更される
state = 2;
}
break;
}
    • good
    • 0

#3です



だいぶ混乱している様子ですので考え方だけ。

現在の手続き型プログラム(まあ、主流のプログラムだと思ってください。C、C++もその一つです。)ではやりたいことを以下のように考えます。

1)(もし必要であれば)初期化する。
2)何か処理をする。
3)(もし必要であれば)後始末をする。

複雑なアルゴリズムもこの組み合わせです。

何かをどこか(画面上、仮想的な空間上、他どこでも)において動かすには次のように考えます。

(何かとどこかは自分で定義してください)
あ)置きます
い)動かします

これは以下に分解されます。

あ-1)置く場所を初期化します。
あ-2)置きます。

い-1)現在位置はあ)で初期化されているので、移動量を初期化します。
い-2)現在位置に移動量を加えて現在位置を更新します。(つまり、「動かします」の物理学的翻訳です。)

その後何か必要な処理があるまでい)を繰り返します。(つまり、動かし続けるわけです)

このとき乱数が必要そうなものは
a)初期配置の位置
b)移動量
ぐらいでしょう。(移動量に乱数を使用しないことは大いにあり得ることです)
a)で配置される場所(以下配置空間と呼びます)とb)の移動量の大きさの比がほぼ等しいなら何かは配置空間を一回の移動ででたらめに移動します。(境界条件は適当に定義してください)
もし移動量の大きさが(配置空間と比較して)小さければ少しずつ動きます。

貴方のプログラムに必要な挙動は不明ですが、移動における基本的な考え方は以上です。もし早い何かと遅い何かが必要なら一回の移動量に差をつけます。

これは移動に限りませんが、もし何かやりたいことがあれば、1,2,3に分解しましょう。分解したものをさらに分解してそれぞれをプログラムでの処理まで落とし込んだら貴方のアルゴリズムの完成です。
    • good
    • 0

#7回答者です。

補足の回答をします。
>一応、指定されたとおり、やってみたんですが、言うのをわすれていたんんですが、このプログラムは移動するのですが、問題はその移動している最中にあちらこちらと物の位置が変わるんです。
→今回の私の回答はrand()関数の使い方について説明しただけですので,ゲーム内のロジックとなりますと全く別の話です。
 どのようにしたいのか具体的に説明していただければ,それなりの回答があると思います。
 例えばrnd1[]配列は何の為に用意したとか,どうような物体がどのように動かしたいのか。
 
>「あちらこちらと物の位置が変わる」
 詳しい情報が無いのでなんとも言えませんが「A1」から「B1」の座標軸の間の動きを乱数を使って処理しているのでしょうか?
 もしこの仮定があっていれば「c1」は中心点で,「d1」は移動範囲の半分,「rnd1[0] = c1 + d1 * ((float)z1 /z2 - 0.5) *2;」は「c1-d1」~「c1+d1」の範囲で乱数的に更新されると思います。結果的に前回の座標とは全く関連性のない移動のようにみえます。(sio2でどのような処理を行っているかわかりませんが。)
 これをある一定の動きにするのであれば,どのようにしたいか明示して下さい。(乱数を使ってどのように動かしたいのか具体的な例が必要です。一般的に移動してるように見せるのであれば一定の時間一定の方向に連続的に描画する必要があります。)

>どうしたら、安定するでしょうか?srand(time(&nt))の単位をus単位に変えればいいのでしょうか?
→μsec単位に出来ても何もかわらないと思います。

この回答への補足

最初に、僕のしたい事と全く違ってました。すいません。まず、おおまかに説明しますと、箱がありその中にカメラを置いて、中を一つの部屋のようにします。そこで、y方向に向かって、-1~1の範囲、またx方向に向かって-1~1の範囲で乱数により、ランダムな位置で物を発生させたいんです。(例えるとy*xの四角形上の範囲で)次に、発生した物をカメラ方向(z軸方向)に向かって、移動させる事がしたいんです。rnd[]配列は、変数では値の更新が確認しづらいかなと思って書きました。

補足日時:2009/09/26 00:45
    • good
    • 0

>us:マイクロセカンド



マイクロを表わすギリシャ語のμ(ミュー)と、
アルファベットのu(ユー)とは、全くの別物です。
マイクロセカンドを表わすのにユーエスと書くのは
あまり適切ではないように思います。
初学者のかたが混乱してしまうかもしれません。

この回答への補足

僕も教えるとき、気をつけるようにします。ありがとうございます。

補足日時:2009/09/25 06:08
    • good
    • 0

回答者#2の注意点とあげられていることが今回の問題のように見えます。

他の回答者の方も同じ指摘はされています。

「srand(time(&nt)); 」

srand()関数は乱数のseed(種)を設定します。
このseedが毎回同じ値であればrand()関数の戻り値はいつも同じ順番の数値を返す可能性があります。これでは乱数としては使えません。
そこで,この「srand(time(&nt));」のようにtime()関数の戻り値を使いseedが同じ値にならないように工夫されています。

time()関数は1970年1月1日からの経過秒数が返されます。この為,1度time()関数をコールしてから1秒以上経過したあとにtime()関数をコールすればtime()関数の戻り値は変化しますが,1秒より短い時間にtime()関数が続けてコールされると戻り値が変わらないことがあります。
プログラムは非常に短い時間で実行される為,time()の戻り値は同じ値を返す可能性が高く「srand(time(&nt)); 」関数がコールされ,rand()関数をコールすると同じ値が返されます。
通常は「srand(time(&nt));」は1度だけ実行して,何度もコールするようなことはしません。どこでもいいのでrand()関数が呼ばれる前に一度だけコールすれば問題が解決されると思います。

とりあえず問題が改善されるか確認する為には次の様にコード修正してみては。
【修正前】
for(j = 0; j < 4; ++j) {
srand(time(&nt));
z1 = rand();

【修正後】
for(j = 0; j < 4; ++j) {
/* srand(time(&nt)); 不要の為削除 */
z1 = rand();

この回答への補足

すいません。おくれました。一応、指定されたとおり、やってみたんですが、言うのをわすれていたんんですが、このプログラムは移動するのですが、問題はその移動している最中にあちらこちらと物の位置が変わるんです。どうしたら、安定するでしょうか?srand(time(&nt))の単位をus単位に変えればいいのでしょうか?

補足日時:2009/09/25 18:08
    • good
    • 0

C 標準ライブラリの rand は腐っていることがあるので, あんまり使わない方がいいっていいますね. もちろんこれは実装によるところなので, 実装によっては物理乱数を使って「本当に乱数」のこともあるでしょう.


疑似乱数を使うなら Mersenne Twister あたりが「わりと安全」といわれています.

この回答への補足

ありがとうございます。参考になりました。メルセンヌツイスタを使う事を検討してみます。

補足日時:2009/09/24 05:20
    • good
    • 0

#3です



ms:ミリセカンド
us:マイクロセカンド

単位です

人に聞くことはいいことだと思いますが、
あまり脊椎反射的ではなく、自分でも考えて(or 調べて)から聞いた方がよろしいかと。老婆心ながらご忠告申し上げます。

現在は単に調べるだけなら驚くほど低コストでできる時代ですから。

ちなみにiphone 3GSは800MのCPUを633Mで使っていたと思いますが、システムクロックについては知りません。乱数発生はハードウエアとの絡みもあり大変興味深いところでもありますが、逆に言えば環境依存でもあるのである程度押さえておかないと不明なバグに陥ります。
気をつけましょう。

この回答への補足

すいません。ちょっと、急いでいました。あと、ご忠告ありがとうございます。

補足日時:2009/09/24 05:31
    • good
    • 0

まさに #3 の 1), 2) の複合技ですな.


ループの中で毎回初期化してどうするよ? 最初に 1回初期化してるからそれで十分じゃないか.
ああ, 実行環境に余裕があるなら C についてる rand 系はさっさとすべて別物にした方がいいかもね. 今だと Mersenne Twister がはやりかな?

この回答への補足

すいません。勉強不足です。メルセンヌツイスタとrandはどうちがうのですか?

補足日時:2009/09/23 18:49
    • good
    • 0

ソースコードが不明なのでありがちなところだけ。



1)毎回乱数発生コマンドを初期化している。乱数発生関数は疑似乱数を発生させますから、同じseedで初期化すれば同じ値が帰ってきます。

2)時刻による疑似乱数発生コマンドに対してアクセス間隔が短すぎる。例えばmsオーダーの時刻を乱数の発生源とする関数にusオーダーでアクセスすれば多くの場合、同じ値が帰ってきます。

現在のCPUの速度はタイマの速度と比較して非常に速いので、昔は充分役に立った乱数発生器も今では注意が必要になっています。

多くの場合上記の2項目のどれかに該当すると思いますので検討してみてください。

この回答への補足

すいません。msオーダー、usオーダーって、なんですか?

補足日時:2009/09/23 18:52
    • good
    • 0

具体的な状況がわからないのですが、


rand()関数をそのまま使っていませんか。

プログラムの中で、rand()関数を使うと、
常に同じ乱数数列を返してきます。

ではどうすればいいかというと、rand()に「乱数の種」を設定する
srand()関数を使用します。

この「種」には通常、現在の時刻を使用します。
このようになります。

#include <stdlib.h>
#include <time.h>

....
srand((unsigned int)time(NULL));
....
a = rand();

注意点は、「srand()は、プログラムの中で1回だけ呼び出せばいい」
ということです。
main()関数の最初のあたりで呼び出してください。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!