プレハブから生成したインスタンスの子オブジェクトを取得したかったのだけど、そのときハマっちゃったので備忘録。
###きみはインスタンスなんだろ、きみの子オブジェクトのことだけを教えてよ!
ゲームのキャラステータス画面を動的に生成する方法を考えていた。
(※キャラ絵は「ゆるドラ」公式の配布物を使用)
上図のようなパネルをまるごとプレハブ化したらいつでもインスタンス生成できるやん! と喜び勇んでたときのこと。
「さて、インスタンスの女の子に名前をつけるぞ。エマちゃんがいいかな」と思ったものの、プレハブ内のGameObject(上図の場合はパネルの子name)を変更できない。インスタンス自身の子オブジェクトにアクセスする方法がさっぱり分からないのだ。天気の子ではない。
いちおう、プレハブにスクリプトをアタッチして、プレハブのインスタンスが生成されたときにGameObject.Findしてきたらいいことはなんとなく分かったんだけど……。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class sample01 : MonoBehaviour
{
GameObject UI_Text;
void Start()
{
UI_Text = Gameobject.Find("name");
UI_Text.GetComponent<Text>().text = "エマちゃん";
}
}
一見、上記sample01.csでなんとかなりそうだけど、これは問題がある。なんと、プレハブのインスタンスが作成されるたびにすべての"name"と名のつくGameObjectを取得してしまうっぽい。
インスタンスを1個しか生成しないのならそれでもいいかもしれないけれど、複数個インスタンスを生成したときは悲惨だ。全然関係ない別のキャラが「エマちゃん」に改名されてしまう(要はすでに生成されているインスタンスの"name"GameObjectを取得してしまうので)。困る。
**「他のオブジェクトのことなんてどうでもいいんだ、きみの子オブジェクトのことだけ教えてよ!」**というお話。
###君の名は、this
インスタンス自身を指す時はthisをつけるといい、ということはなんとなく知っていた。thisさえつければ、インスタンス自身が持つGameObjectをFindできるのではないか。sample01.csのFind部分を以下のように書き換えてみる。
UI_Text = this.gameObject.Find("name");
thisは「プレハブにアタッチされたスクリプトのインスタンス」を指す。呪文か。ただ、これだとエラーが出る。gameObject.Findはできない。どうやら他の方法で子オブジェクトを取得するしかないらしい。ここで結構ハマってしまった。
###this.transformできみを追う子ども
調べていくうちに、GameObjectを取得する方法がGameObject.Find以外にもあることにたどり着いた。transform.FindからGameObjectを取得する方法があるらしい。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class sample02 : MonoBehaviour
{
GameObject UI_Text;
void Start()
{
UI_Text = this.transform.Find("name").gameObject;
}
}
上コードでは、thisでインスタンス自身を指定し、インスタンス自身が持つ"name"GameObjectのtransformコンポーネントを取得している。
transform.Find()の返り値はtransform型なので、GameObject型で取得するため末尾に.gameObjectがついている。これでインスタンス自身の"name"GameObjectを取得できるので、あとはGetComponent<>してやればよい。
やっとインスタンスの子を知れたよ。
※注記
GameObject.Findは重たい処理らしく、あまり用いないほうがよいらしい(特にUpdate内に書くとめちゃ重くなる)。
「ならtransform.Findだけでいいじゃん!」となりそうだが、双方にはいろいろ違いがあるようだ。
参考リンク:【Unity】Findメソッドの説明・使い方をお伝えする【オブジェクトを取得できる】
今回ハマったのは、参考にしていた入門書や入門サイトでGameObject.Findが頻出していたからだろう。なぜ、重い処理のほうを薦めるんだろう。UnityプログラミングがGameObjectで始まりGameObjectで終わるから、わかりやすさ重視のためかな。
やさしいかわいい女教師が「んじゃあとりあえず、GameObjectってモノに慣れとこっか!」なんて軽いノリだと思うと悪くない気もした。