久々にInstantiate使ったらSetParent以外で生成したオブジェクトの親を設定できることを知った話
Instantiateでオブジェクトを複製したけどなんかでかい!
こんな感じのScrollViewの中にボタンをいっぱい生成して選択できるようにしたいです。そんなときはInstantiate。Prefabから生成できるように以下のようにコードを書きました。
(ScrollViewのContentにはHorizontalLayoutGroupコンポーネントを設定しているのであとは追加していくだけで横にずらーっと並んでくれる想定です。)
// インスペクタで生成したい数だけ設定する
[SerializeField] private Color[] _color;
// Prefabから生成するオブジェクト
[SerializeField] private GameObject _object;
private void Start()
{
var buttonsParent = this.gameObject.transform.Find("Scroll View/Viewport/Content");
for (int i = 0; i < this._color.Length; ++i)
{
var button = Instantiate(this._object);
button.transform.parent = buttonsParent;
button.GetComponent<Image>().color = this._color[i];
}
}
以下実行結果
生成はされましたがインスペクタを見てみると生成されたオブジェクトのScaleが大きいです。
1(Prefabの正しいScale)に戻すと正しく表示されますが、
理由もわからないまま
button.transform.localScale = Vector3.one;
なんてしないようにしてください。 (1敗)
なぜ勝手にScaleが変わってしまうのか
その原因はワールド座標、ローカル座標にあります。
ワールド座標、ローカル座標についての説明は以下のサイトでとても詳しく説明されていらっしゃるので、ぜひご覧ください。
var button = Instantiate(this._object);
button.transform.parent = buttonsParent;
先ほどのコードだと、生成した際のthis._objectは何処に生成されるでしょうか。
そう、ここでは生成されたオブジェクトはrootにワールド空間で生成されます。
そのあとにparentを指定しているのでワールド空間のオブジェクトがcanvas内に入り、このときScaleが変わってしまっていました。
対応①
var button = Instantiate(this._object, buttonsParent, false);
生成する際に親の設定を行う。(falseは規定値なので記載しなくても動作は変わらないです)
このように記載することで
生成するタイミングでtransformを親に対して相対的に設定できます。
表示も正しく行えました!
ちなみに
Instantiate(this._object, buttonsParent, true);
このように記載すればワールド空間に生成できます。
その他の詳しい説明などはリファレンスをどうぞ
対応②
いままで自分が使っていた方法です。
var button = Instantiate(CustomManager.Instance._PrefabButton);
button.transform.SetParent(buttonsParent.transform, false);
生成した後にSetParentで親を設定していました。
こちらの方法だと2行使ってしまい見栄えも悪いです。
まとめ
Instantiateで生成する際は
Instantiate(複製するオブジェクト, 親オブジェクト.transform, false);
で生成すると冒頭にあった問題は回避できます。