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

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

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

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

Unity

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

Q&A

解決済

1回答

596閲覧

別の質問でもらったコード(unityで乱数を重複のないように繰り返す)を自分の意図するものに変える

Hayato555

総合スコア17

C#

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

Unity

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

0グッド

1クリップ

投稿2017/10/04 09:49

###前提・実現したいこと
先日 unityで乱数を重複させずにそれを繰り返すというコードの書き方がわからなくて質問させていただきました。(https://teratail.com/questions/94921)
私が書きたかったのは、https://teratail.com/questions/94741
ここを参考にして、テトリスのミノ(ブロック)7つをランダムで重複がなく生成させ、その7つが生成しきったら、またそれを繰り返す、というコードでした。
幸いなことにある方わかりやすいようにコードを書いてくださいました。
しかし、私はUnity及びC#の初心者なので、軽く理解できる程度で、
自分のUnityに組み込むことができませんでした。
実際に何をしたかったのかというと、下記のコードでは テトリスのミノを3秒(おそらく)ごとに重複のないように7つを1つずつ生成し、それを繰り返す となっているのですが、もともと私が自分で書いたコードではspawnNext()というのにしていて、別のオブジェクトからspawnNext()を出力させることで、ミノを生成させていました。 このコードのどの部分を変えれば 別のオブジェクトから出力が来るまで、ミノを出現させず、出力が来たら、生成するというコードに書き換えられますか?

###該当のソースコード

C#

1using UnityEngine; 2using System.Linq; 3using System; 4using System.Collections; 5 6public class createRandObjs : MonoBehaviour 7{ 8 //とりあえずインスペクタからオブジェクトを追加 9 [SerializeField] 10 GameObject[] objcts; 11 12 //何秒おきに出力するかを指定 13 static readonly float LOOP_INTERVAL = 3f; 14 15 //Coroutineをキャッシュする変数 16 Coroutine landObjLoopCorutine; 17 18 void Start() 19 { 20 // 実行 21 startLandObjLoop(); 22 } 23 24 25 /// <summary> 26 /// ループのスタート 27 /// </summary> 28 void startLandObjLoop() 29 { 30 //このGameObjectが非表示の時にlandObjLoopを実行されるとエラーになるのでその場合実行させない 31 if (!gameObject.activeInHierarchy) 32 { 33 #if UNITY_EDITOR 34 Debug.Log("このオブジェクト非表示中だから実行できないよ"); 35 #endif 36 37 return; 38 } 39 40 //実行中なら重複するので一旦停止 41 stopLandObjLoop(); 42 43 //オブジェクトの出力をスタート 44 StartCoroutine( landObjLoop() ); 45 } 46 47 48 /// <summary> 49 /// ループのストップ 50 /// </summary> 51 void stopLandObjLoop() 52 { 53 if (landObjLoopCorutine != null) StopCoroutine(landObjLoopCorutine); 54 } 55 56 57 /// <summary> 58 /// オブジェクトを出力する 59 /// </summary> 60 void instantiateObj(int idx) 61 { 62 //とりあえずVector3.zero地点にオブジェクトを生成 63 Instantiate(objcts[idx], Vector3.zero, Quaternion.identity); 64 } 65 66 67 /// <summary> 68 /// objctsをランダムに出力し続ける 69 /// </summary> 70 IEnumerator landObjLoop() 71 { 72 //オブジェクトが無ければ何もせず終了 73 if (objcts.Length == 0) yield break; 74 75 var waitTime = new WaitForSeconds( LOOP_INTERVAL ); 76 77 //順番にカウントするための変数 78 var idx = 0; 79 //ランダムなインデックスを格納する配列 80 var objIdxAry = new int[objcts.Length]; 81 82 while(true) 83 { 84 //idxが0またはobjctsの数に達したらリセット 85 if(idx == 0 || idx == objcts.Length) 86 { 87 idx = 0; 88 objIdxAry = Enumerable.Range(0, objcts.Length).OrderBy(n => Guid.NewGuid()).Take(objcts.Length).ToArray(); 89 } 90 91 instantiateObj(objIdxAry[idx]); 92 idx++; 93 94 yield return waitTime; 95 } 96 } 97}

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

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

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

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

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

kikukiku

2017/10/05 00:14

unityの開発経験がないため意図した具体的な回答はだせませんが、本問題を解決するための方向性だけ、意見させてください。プログラマであるならば質問者さんは自身の書いたコードはせめてすべて理解すべきかと思います。理解できていないと今回のように改修することで出来ません。現在理解できていないのであるならば、現在コードの理解できていない部分を理解してから、今回の質問に取り組んだ方が良いと思います。急がば回れです。なので、まずは現在理解できてない部分を個別に切り出して、具体的に質問した方が良いと思います。
guest

回答1

0

ベストアンサー

遅くなりました。すみません。

怒られちゃいましたねw
ご自分でわかっていらっしゃると思うので折り合いつくとこまで進めたら少し勉強時間をはさんでみてもいいかもしれませんね。

Unityの本を買って説明を読みながら掲載されているサンプルを手打ちで打っていくとある程度わかってくると思います。Unityはどんどこ進んでいるので今は本で学ぶのが一番です。

ということでspawnNext()で呼ぶタイプに書き直しました。
相変わらず未検証です。コメントをたっぷり入れたので思ったような挙動にならなければご自身で修正してみてください。そして、どうしても分からなければその部分のコードと一緒に質問してみてください。

使い方
0. 下記コードをコピーしcreateRandObjsという名前でスクリプトとして保存
createRandObjs

  1. ゲームオブジェクトをヒエラルキーに作成

ゲームオブジェクト

  1. createRandObjs.csをオブジェクトにアタッチ

イメージ説明

  1. ゲームオブジェクトのインスペクタに配列が入れられるようになっているのでオブジェクトを追加

オブジェクトを追加

5. 別スクリプトから「createRandObjs.Instance.spawnNext();」を呼ぶ

以上です。

外から呼ばれるということなのでシングルトンにしました。

C#

1using UnityEngine; 2using System.Linq; 3using System; 4using System.Collections; 5 6public class createRandObjs : MonoBehaviour 7{ 8 //とりあえずインスペクタからオブジェクトを追加 9 [SerializeField] 10 GameObject[] objcts; 11 12 13 //外部から呼び出せるように静的なフィールドとプロパティを定義 14 //そうしておくと外部からcreateRandObjs.Instance.spawnNext()で呼び出せます。 15 static createRandObjs instance; 16 public static createRandObjs Instance 17 { 18 get { 19 //インスタンスがなければインスタンスを生成 Awakeより先にこのプロパティが呼ばれる可能性があるのでここでもチェックとnullであればインスタンス化します。 20 if(instance == null) instance = (createRandObjs) FindObjectOfType( typeof(createRandObjs) ); 21 22 return instance; 23 } 24 } 25 26 27 // オブジェクトを順番にカウントするための変数 28 int objIdx; 29 30 // オブジェクトのランダムなインデックスを格納する配列 31 int[] objIdxAry; 32 33 34 void Awake() 35 { 36 //インスタンスがなければインスタンス化 37 if(instance == null) instance = (createRandObjs) this; 38 //すでにインスタンスが存在すれば自身を破棄 インスタンスは1つでいいので重複対策です。 39 else if( Instance != this ) Destroy(this); 40 } 41 42 43 /// <summary> 44 /// オブジェクトを出力する 45 /// </summary> 46 void instantiateObj(int idx) 47 { 48 //とりあえずVector3.zero地点にオブジェクトを生成 49 Instantiate(objcts[idx], Vector3.zero, Quaternion.identity); 50 } 51 52 53 /// <summary> 54 /// 外部から呼ばれる オブジェクトを出力する 55 /// </summary> 56 public void spawnNext() 57 { 58 //オブジェクトが無ければ何もせず終了 59 if(objcts.Length == 0) return; 60 61 //objIdxが0またはobjctsの数に達したらリセット 62 if(objIdx == 0 || objIdx == objcts.Length) 63 { 64 //objIdxをリセット 65 objIdx = 0; 66 //ランダムな配列を生成 67 objIdxAry = Enumerable.Range(0, objcts.Length).OrderBy(n => Guid.NewGuid()).ToArray(); 68 } 69 70 //objIdxAryのインデックスを元にオブジェクトを生成 71 instantiateObj( objIdxAry[objIdx] ); 72 //objIdxを+1する 73 objIdx++; 74 } 75}

呼ぶ側

C#

1createRandObjs.Instance.spawnNext();

[以降は読まなくてもいいです] とりあえずコード量を抑える為、必要最低限にしています。

どこまで完成しているか分からないのでなんとも言えないのですが今のコードでは不十分に感じます。考えられるのはこんな感じです。

  • オブジェクトの移動完了などの状態を直接知ることができない
  • テトリスの場合、マップ的なものを用意してx軸が埋まっていれば(略)などの処理の対応
  • 消える時のアニメーション

などですかね?僕ならオブジェクト一つ一つにスクリプトをつけてそこで、自身の調整や管理と外部への自身の状態や消えるアニメーションのスタートやエンドなどを提供をする形にすると思います。

それをこんな感じでコントロール側で持つと思います。

C#

1//これをオブジェクトひとつひとつにアタッチする 2public class objScript : MonoBehaviour 3{ 4 public bool destroyAnime(int[] targetYs) 5 { 6 //略 7 //色々して全部消えたらtrueを送る 8 return true; 9 } 10} 11 12//引数などは適当です。実際はint[]を渡す形で組まないかもしれません。 13public class createRandObjs : MonoBehaviour 14{ 15 // 生成する時についでにこの配列に参照を渡しておく 16 List<objScript> objScripts = new List<objScript>(); 17 18 void destroyAnime() 19 { 20 //略 21 22 //1,2列目を破棄 23 var isDeleteObj = objScripts[0].destroyAnime(new int[]{ 1,2 }); 24 //もしオブジェクトが完全に消えたら何かする 25 if(isDeleteObj) objScripts.RemoveAt(0); 26 }

投稿2017/10/05 19:52

編集2017/10/06 16:24
IShix

総合スコア1724

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

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

Hayato555

2017/10/07 09:26

ご丁寧な説明 本当にありがとうございます。 書いていただいたコードを自分のものに合うように少しだけ手を加えた結果、無事自分の思っていたものにできました。 unityとC#については頑張って学んでいきたいと思います。ですが今作っているゲームをよりよくするためにまたご質問をさせていただくかもしれません。 また機会がありましたら 答えてください。m(_ _)mここまで長い間 初心者の自分にご丁寧に答えてくださり、本当にありがとうございました。
Hayato555

2017/10/08 14:30

ベストアンサーにしてからかなり立ちましたが一つ気が付いたことがあります。 このオブジェクトを格納する配列に数字が入れられるときにランダムになっていないのでないでしょうか? テトリスのミノを出現させると、オブジェクトの1から7と出てきます。  もし時間がありましたら、もう一度コードを編集してはくれませんか?
Hayato555

2017/10/08 14:35

大変申し訳ございません。 テトリスのミノのネクストというものを作っていて、このコードをいじってしまって、どうやらそれが原因でランダムに出てこなかったようです。 もしこれが自分で解決できないようだったら、別で質問させていただきます。m(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問