質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

5回答

3198閲覧

条件付きの乱数の作り方を質問させてください

sukiyakigozens

総合スコア28

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2017/10/11 10:18

条件付きの乱数の作り方を質問させてください。
スマホのパズルゲームを製作しております。(初心者、unity5.6使用)

10ピースのパズルがあり、
ゲーム開始時に、パズルピースをシャッフルします。


パズルピース
|1|2|3|4|5|6|7|8|9|10|

スロット
|A|B|C|D|E|F|G|H|I|J|


スロット「A」に対しては「1」が正解でスロット「B」に対しては「2」が正解とします。(同様に「J」まで続く)
(スロットは固定で、パズルピースがシャッフルされます)

その際に問題になる条件が、
シャッフル時に、偶然に正解位置に割り振られる事を避けたい、という条件です。
つまり、スロット「A」には「1」以外を、スロット「B」には「2」以外を割り振りたいです。
(もちろん数字の重複も避けたいです)

この場合、
下記のような、正解位置を避けたシャッフルパターンを手動で作るのがいいでしょうか?
例えば
|10|6|2|1|7|8|6|4|5|9|
のような数列をあらかじめ5パターンくらい用意して、
それをランダムに使い回すのがいいでしょうか?
(自分で作った数列5パターンをシーンロード時に
5パターンの中からランダムにどれかを選択し使用する)

または

正解位置を避けながら、なおかつ同じ数字の重複も避けたの乱数の制作はC#で可能でしょうか?

※シーンが進むにつれてパズルのピースは増えて50個~60個になる予定です。


現状のプログラムの状態
1 まずシャッフルしたパズルピースを実際にシーン上に配置
2 シャッフル時に偶然に正解位置に置かれていないかを、コリジョン判定を利用して調べる
3 偶然に正解があった場合はシーンをリロードして再シャッフル
(全部のピースが完全に正解位置を避けるまで何度もシャッフルする)

というプログラムを作ったのですが、
この方法ですとスマホの実機上で実行した場合、
特にパズルピースの数が多い場合に、実機のメモリの消費が激しいようなので、
メモリ消費の少ない方法を探しております。

ご教授よろしくおねがいいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

動作未検証&どこまでメモリ消費するか分かりませんが、以下はいかがでしょう?

  1. 最初に正解のスロットにピースをはめる。
  2. そこから1スロット毎に、別のランダムなスロットとピースを入れ替える。

2-1. ただし入れ替えた結果どちらかのスロットが正解となる時は次のスロットを入れ替え先とする。次のスロットも正解になる場合は更に次のスロットを入れ替え先とする。
2-2. 最後までスロットが選択出来ず、自分のスロットに正解ではないピースが入っていれば2へ戻る。
3. 並び替え後の配列の通りにオブジェクトを配置する。

動作例

・開始時(正解):分かりやすいように数を減らしています
|A-1|B-2|C-3|

・スロットAのピースと、別のランダムなスロット(Cが選ばれたとする)のピースを入れ替え
(どちらも正解ピースではないことをチェック→問題無し)
|A-3|B-2|C-1|

・スロットBのピースと、別のランダムなスロット(Aが選ばれたとする)のピースを入れ替え
(どちらも正解ピースではないことをチェック→問題無し)
|A-2|B-3|C-1|

・スロットCのピースと、別のランダムなスロット(Aが選ばれたとする)のピースを入れ替え
Aと入れ替えるとAが正解になってしまうので次のスロットを見る
→Bと入れ替えるとCが正解になってしまうので次のスロットを見る
→最後のスロットまで来たが、スロットC(自分自身)が正解ではないので問題無し(入れ替えはしない)
|A-2|B-3|C-1|

・全てのスロットを入れ替え終わったのでループ処理終了

・出来上がった順にピースを配置すればOK
|2|3|1|


※速度や負荷を考慮すると入れ替え前に「現在のスロットに入っているピースが正解ではない場合、入れ替えを行わない」という判定を入れてもいいかもしれません。
※下手にやると「終了しない処理」になりそうなのでご注意ください。「n回ループしてたら最初からやり直し」とか必要かも。

投稿2017/10/11 11:18

sakura_hana

総合スコア11427

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sakura_hana

2017/10/11 11:20 編集

deigoさんとやっていることは基本的に一緒です。回答書いてる間に被りました……
sukiyakigozens

2017/10/11 11:39

ご返答ありがとうございます。 シーンにパズルピースを配置してから、ピースを入れ替えするのではなく、 メモリ消費を考慮して、 完全順列(下記の方のご回答ではじめてこの言葉を知りました)の 乱数を作った後に、 シーンにパズルピースを配置したいと考えております。
sakura_hana

2017/10/11 11:54 編集

面倒な処理を書きましたが、元も子もないことを言うと、現状のままでもそこまで負荷は変わらないかもしれません。 「現状のやり方」でメモリを多く消費しているのは恐らく「シーンリロード」の部分です。 なので「コリジョン判定後に再シャッフル」の部分をシーンリロードで行わない(メソッド再呼び出しだけにする)だけでも『メモリ消費については』改善すると思います。 また、コリジョン判定も負荷となるので、それをやめるだけでも高速化します。 上記を踏まえると、最小のスロット数にもよりますが下手なアルゴリズムを組むより「ランダムな配列作成→正解と合致しているかチェック→合致していたら再シャッフル」の繰り返しの方が早いかもしれないです。 (deigoさんのロジックを更に追加すると無限ループも防げるかと思います)
sukiyakigozens

2017/10/11 18:12

「コリジョン判定も負荷となるので、それをやめるだけでも高速化します。」とありますが、 コリジョン判定を使用せずにパズルピースが正解スロットに置かれているかどうかを 判定する方法はあるのでしょうか?
sakura_hana

2017/10/12 00:47

ピースをシーン上に配置する時に位置を指定しているかと思いますが、その際にスロットオブジェクトの位置と比較すれば「どのピースがどのスロットにあるか」は分かるのではないでしょうか。 (実際の動きがどうなのかにもよるので、上記は的外れな可能性があります。現状のスクリプトを提示してもらえればもっと的確に言えます)
sukiyakigozens

2017/10/12 00:53

すいません。もうひとつ質問させてください。同じ名前の重なりによりコリジョン判定をしている為、 各piece1からpiece10まで同名のオブジェクトがそれぞれ2つ存在しております。(piece1が2つ、piece2も2つ、、、) 例えばpiece1が正解位置に来てしまったのでpiece2と入れ替えしたい場合、 2つのオブジェクトを取得するのに手間取っております。 piece1は、GameObject irekae1 = other.gameObject; と指定できますが、 piece2は同名オブジェクトが2つ存在しているので名前で検索できないと思われます。 また、シャッフルして階層も変わるのでパスでの指定もできません。 FindGameObjectWithTagで検索し、(これだと10個の全部のpieceが該当してしまいますが) 何かよい方法はありませんでしょうか? すいませんが、可能でしたらご教授おねがいいたします。
sakura_hana

2017/10/12 01:02

「同じ名前の重なりによりコリジョン判定をしている」というのがよく分かりません。 別名には出来ないのでしょうか? また、「FindGameObjectWithTagで検索→ヒットしたオブジェクトの名前で検索」という二段階での判定でもいいと出来ます。 一番良いのは、最初に GameObject[] pieces; GameObject[] slots; という形でオブジェクトの配列を持っておき、スクリプトで利用することです。 (Find系メソッドもやっぱり負荷が高い為)
sukiyakigozens

2017/10/12 01:22

ご返答ありがとうございます。 void OnTriggerStay2D(Collider2D other){ if (other.gameObject.name == gameObject.name) //ピースのpiece1と背景のpiece1の重なり {正解時の動作} }という形を利用していおりました。 違う方法を探してみたいと思います。 今回は大変ありがとうございました。
sakura_hana

2017/10/12 01:47

BAありがとうございます。余談的に。 オブジェクト名判定で単純にやるなら ピースの名前:piece1、piece2…… スロットの名前:slot_piece1、slot_piece2…… みたいにしておき、 if ("slot_"+other.gameObject.name == gameObject.name) { //正解時の動作 } という形でも出来ると思います。 (これならGameObject.Find("piece1")で1つしかヒットしない) 色々書いてしまいましたが「とりあえず動けばいいんだよ」等の都合があると思うので、検討の上お好きな方法でやってもらえればいいと思います。
sukiyakigozens

2017/10/12 09:28

さらなるご説明ありがとうございます。 if ("slot_"+other.gameObject.name == gameObject.name) をやってみましたが動作しませんでした。 if (other.gameObject.name == gameObject.name && other.gameObject.tag == "slot") との二段階での判定なら動きました。できましたら同名オブジェクトは避けてコリジョン判定を"slot_"+のように”同じ名前を含む”で動作できるとベストなんですが無理のようです。
sukiyakigozens

2017/10/17 11:47

申し訳ありません。上記に当方が「無理のようです」と書きましたが、 コリジョン判定条件の「名前の一部が同じ場合に命令実行」は >>この指定方法>> if ("slot_"+other.gameObject.name == gameObject.name) sakura_hanaさんのご指摘どおり、作動しました。すいませんでした。ご教授ありがとうございました。
guest

0

正解あり

正解があったピースと隣のピースを入れ替える

再評価

というのはどうでしょう。
交換された2つの箇所は必ず正解でない組み合わせになるはず。

投稿2017/10/11 10:46

deigo

総合スコア200

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

deigo

2017/10/11 11:36

雑すぎたので補足  普通にランダムリストを作る  ↓ ┌評価 ↑↓ │正解があれば、正解のピースと隣のピースを入れ替える └┤  ↓  正解がなくなったら評価終了 片方が正解の場合、隣合うもうひとつの値と入れ替えると、必ず両方とも不正解となる。 そのためコストは ・1回分のランダムリスト生成 ・リストの要素が60として最大30回の位置交換
guest

0

完全順列(derangement)というやつですね。

ランダムなderangementの生成はこの質問で紹介されてる論文が参考になるのではないでしょうか。
https://math.stackexchange.com/questions/302057/generating-a-random-derangement

投稿2017/10/11 10:36

karamarimo

総合スコア2551

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

0

C#やったことないんで的外れになるかもしれませんが・・・
あとシャッフルというのが視覚的効果を伴うものであれば役に立たないかもしれません。

1.一旦パズルピースを未使用リストみたいなのに格納
2.頭から順にシャッフルしたパズルピースを決めていくとして、
3.その位置の正解が未使用リストに残っている場合は、そのピースをリストから退避
4.残ったリストの範囲で乱数を取得してピースを決定
5.位置が決定されたピースは未使用リストから削除
6.退避していたピースがあれば未使用リストに戻す
7.次の位置へ
8.3に戻る

これを全てピースが決まるまで繰り返します。

C#でリスト内のオブジェクトを特定する際にかかるコストがわからないので
もしかしたら処理時間が大変なことになるかもしれませんが・・・(60程度問題ないですよね?)

メモリ消費についてはわからないです。
繰り返し処理内で変にインスタンスを生成しなければそこまで大量に消費することはないと思いますが・・・

突っ込みどころがあれば教えてください。

投稿2017/10/12 01:35

abs123

総合スコア1280

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sukiyakigozens

2017/10/12 09:05

ご返答ありがとうございます。初心者で上記の方法のコードが思い浮ばないのですが、できるのではと思います。ありがとうございました。
guest

0

追記:以下は回答者の勘違いにより的を外した内容になっています。読み飛ばしてくださるようお願いします。

次のようなアルゴリズムではダメでしょうか。

n個のスロットがあるとする。
m<=1, m=>nの範囲で以下を繰返す。
0. 乱数を1からn-1までの間で生成する。これをrとする。
0. m==r ならば r=r+1とする。
0. スロットmの値はrとする。

投稿2017/10/11 11:49

編集2017/10/11 18:33
KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sukiyakigozens

2017/10/11 15:10

ご返答ありがとうございます。 すいませんが、もう少し詳しく教えていただけませんでしょうか? (初心者なもので) 例えばスロット数が4つの場合 n=3 rは1、2、3を使った乱数 mは繰り返しの数?でとの理解で間違ってないでしょうか?
KojiDoi

2017/10/11 18:31

申し訳ないです。前提条件を勘違いしていました。私の回答は役に立たないと思います。
sukiyakigozens

2017/10/11 20:34

とんでもないです。ご返答ありがとうございます。私がもっと知識があれば上記の方法でもできそうな気がします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問