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

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

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

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

2回答

5205閲覧

Unityで、ボタンを動的に生成したいです。

blueyellow

総合スコア4

C#

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

0クリップ

投稿2022/01/01 18:17

前提・実現したいこと

Unityで、ボタンを動的に生成したいです。
具体的には、for文で回した数だけ生成して、uGUIボタンのテキストを1から順に表示させたいです。
それを、グリッド表示させたいです。

イメージとしては、実行して以下のようにボタンが並ぶ感じです。

➀➁➂
➃⑤⑥
⑦⑧⑨

発生している問題・エラーメッセージ

ソースコードではforで5回繰り返しているのですが、なぜか2つしか表示されません。 しかも、数字も反映されません。 下から4行目、3行目を消すと、一応5回分ボタンが生成されるのですが、クローン元のボタンも含まれて6つになってしまいます。

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5using TMPro; 6 7public class hoge : MonoBehaviour 8{ 9 [SerializeField] 10 GameObject hogehoge; 11 12 [SerializeField] 13 GameObject canvas; 14 15 void Start() 16 { 17 for (int i = 0; i < 5; i++ ) { 18 var obj = Instantiate(hogehoge, canvas.transform) as GameObject; 19 var hogebutton = GameObject.Find("Button").GetComponent<Text>(); 20 hogebutton.text = i.ToString(); 21 } 22 }

試したこと

GameObjectにアタッチしたもののインスペクタでhogehogeとcanvasをドラッグで持ってきました。
インスタンス?プレハブ?を使うといいと思うのですが、なかなかうまくいきません...

補足情報(FW/ツールのバージョンなど)

unity 2020
windows

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

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

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

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

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

guest

回答2

0

ベストアンサー

書いてたら長くなったので結論だけ先書いときます。

  1. Instantiateで生成されるオブジェクト名は語尾に(Clone)が付く
  2. GetComponentだと子のオブジェクトまで探してくれない
  3. GameObject.Findは同名オブジェクトが複数あるとHierarchyから見て一番上の奴しか取ってこない

色々解決すべき点があるので順にやっていきましょう。

1: Instantiateで生成されたオブジェクト名について

var obj = Instantiate(hogehoge, canvas.transform) as GameObject; var hogebutton = GameObject.Find("Button").GetComponent<Text>();

(hogehogeオブジェクトの名前が分かりませんがひとまず"Button"として話を進めますね。)
hogehogeという変数に格納されたボタンをInstantiateで生成し、直後に"Button"という名のオブジェクトを探しその中にあるTextコンポーネントを取り出そうとしていると思います。

ですが生成されたオブジェクトは、元々のオブジェクト名 + "(Clone)"という名前に変化します。
元々"Button"って名前なら"Button(Clone)"になりますね。

var obj = Instantiate(hogehoge, canvas.transform) as GameObject; var hogebutton = GameObject.Find("Button(Clone)").GetComponent<Text>();

2: Buttonに含まれるTextコンポーネントの階層について

var hogebutton = GameObject.Find("Button(Clone)").GetComponent<Text>();

これでGameObject.Findで無事ボタンオブジェクトを取得出来、TextもGetComponentで取得!..出来ないんです。
最初にButtonをPrefab化する為にHierarchy欄にて、Buttonを新規作成すると思いますが階層が以下のようになっています。(括弧内にその階層に付与されているコンポーネントを記載)

Button (RectTransform/CanvasRenderer/Image/Button) └Text (RectTransform/CanvasRenderer/Text)

Textコンポーネントが親(Button)ではなく子(Text)に付与されているのが分かります。
ここが原因であり、GetComponentは探してきて欲しいオブジェクトから指定したコンポーネントを探してきてくれます。
GameObject.Find("Button(Clone)")は一番上のButtonオブジェクトから探しますが、子のTextオブジェクトは探索外になります。

解決策としては、子もまとめて探索してくれるGetComponentInChildren, Textコンポーネントが含まれているTransformを渡し探す等あります。

// こちらの方がおススメ (処理が重いので毎フレーム使ったりするのは非推奨) var hogebutton = GameObject.Find("Button(Clone)").GetComponentInChildren<Text>(); // Button(Clone)の0(1)番目の子供を取り、そこからGetComponentで探す var hogebutton = GameObject.Find("Button(Clone)").transform.GetChild(0).GetComponent<Text>();

3: 同名のオブジェクトがある時のGameObject.Findの挙動

var hogebutton = GameObject.Find("Button(Clone)").GetComponentInChildren<Text>();

これで"Button(Clone)"オブジェクトの(子も含め)Textコンポーネントを探してくる処理になりました。
ですがまだ上手くいきません。具体的には一つのTextしか変化していないと思います。
理由としてはGameObject.Findが毎回同じオブジェクトを取ってきてしまうからです。
GameObject.FindはHierarchyの一番上から下まで順に名前で検索し合致していればそのオブジェクトを取ってきます。

さらにInstantiateで生成し、同じ階層に設置したオブジェクトは前に生成されたオブジェクトの下に生成されます。 つまり...

1. 1つめのButtonが生成される Canvas └Button(Clone) 2. GameObject.Findが上から順に"Button(Clone)"を探す Canvas ← 名前が違う └Button(Clone) ← これだ! 3. 取ってきたTextを変更する (1つめのButtonオブジェクト内のText) 4. 2つめのButtonが生成される Canvas ├Button(Clone) └Button(Clone) ← 新しいButton 5. GameObject.Findが上から順に"Button(Clone)"を探す Canvas ├Button(Clone) ← 名前合ってるなこいつ取ってきます! └Button(Clone) ← (本当はこいつから取ってきて欲しい) 6. 取ってきたTextを変更する (また1つめのButtonオブジェクト内のText) 7. 以下略

...という訳で毎回一番上である1つめのボタンオブジェクトからTextを取ってきてしまいます。
対処法としてはそもそもGameObject.Findを使わず、出来立てホヤホヤのButtonを取りましょう、というより投稿者様が挙げたコード内で既に取ってます。

var obj = Instantiate(hogehoge, canvas.transform) as GameObject;

実はInstantiateは作ったオブジェクトを渡してくれます。
上の1行だと var objに出来立てのButtonオブジェクトが入っています。
と言う事はわざわざGameObject.Findなんてしなくても...

var obj = Instantiate(hogehoge, canvas.transform) as GameObject; var hogebutton = obj.GetComponentInChildren<Text>();

これで片付きます。

投稿2022/01/02 01:13

編集2022/01/02 01:18
mushipan0929

総合スコア56

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

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

blueyellow

2022/01/02 15:54

ご丁寧にお教え頂きありがとうございます!mushipan929さんの説明がとってもわかりやすく、読んでいるとすんなりと頭に入ってきました…!おかげさまでモヤモヤしていたところが一瞬で理解でき、感謝しております。 その後、i.ToStringで数字も表示することができ、また1つ多かったクローン元のボタンはfor文の外でDestroyすることで期待どおりのものが完成しました。 また何かあれば質問させてください…!ありがとうございました。
blueyellow

2022/01/06 07:57

何度もすみません。 並べたボタンですが、どのボタンが押されたかの判定ができず行き詰ってしまいました... 以下のような関数を新たに作ったのですが、どのボタンを押しても最後のボタンのテキストを返してしまいます。 public void ButtonPushed() { Debug.Log(hogebutton.text); hogebutton2 = this.GetComponentInChildren<Text>(); Debug.Log(hogebutton2.text); } mushipan0929さんの前回の説明がすごくわかりやすかったので、もし差し支えございませんでしたら今回もご教授いただければ幸いです。頼りにしております、よろしくお願いいたします。
mushipan0929

2022/01/06 08:11

ButtonPushed()が記載されているクラスがどのオブジェクトにアタッチされているか分かりませんが this.GetComponentInChildrenと記載されている様子を見ると ボタンObjを生成するクラス内に書かれているのでしょうか?
mushipan0929

2022/01/06 08:45 編集

ボタンが押された際の判定についてですが、Unity側でボタンが押された際に自動的にButton.onClickというモノが呼ばれるようになっています。 後はこちらが呼ばれた際にどんな処理をして欲しいか登録するだけです。 ``` Button button; button.onClick.Addlistner( () => { // ここに押したらやって欲しい事を書く }); ``` 今回の場合、質問者様のコード内にてButtonが含まれたObjを変数として取っているので var obj = Instantiate(hogehoge, canvas.transform) as GameObject; obj.GetComponent<Button>()で取れるかと思います。
blueyellow

2022/01/07 16:48

何度もご丁寧にご回答していただきありがとうございます!返信遅れまして申し訳ございません。 button.onClick.Addlistner( () =>...としたところ無事に期待通りの結果を得ることができました! ご推察の通り、ボタンObjを生成するクラス内にButtonPushed()の関数でしたので(質問しておきながら説明不足で大変恐縮です)、obj.GetComponent<Button>()で取得できました。 また >Unity側でボタンが押された際に自動的にButton.onClickというモノが呼ばれるようになっています。 ということも知ることができ、mushipan0929様には大変勉強になりました。 度重なる質問にもご親切に対応していただいたこと、心より感謝申し上げます。
guest

0

C#

1 var hogebutton = GameObject.Find("Button").GetComponent<Text>();

上記の場合、以下の理由で正しく動作しません。

  • 「Button」という名前のゲームオブジェクトが複数存在する場合、どれが選ばれるかは分からない
  • そもそも、Instantiate()で生成したゲームオブジェクトは「元の名前 (Clone)」という名前であり、取得できるはずがない

この場合、

C#

1 var obj = Instantiate(hogehoge, canvas.transform) as GameObject;

objこそがまさに生成したゲームオブジェクトであり、わざわざGameObject.Find()を使う必要すらありません。

投稿2022/01/02 00:41

fiveHundred

総合スコア10152

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

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

blueyellow

2022/01/02 15:59

ご回答ありがとうございました!確かに、Button(Clone)という名前になっていたので、取得できないはずですよね...。また、objだけでよかったんですね。GameObject.Findを使わないといけないものと思い込んでいました。たくさん教えていただき感謝いたします...!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問