次のようなヒエラルキー上の子オブジェクトをスクリプトからすべて取得したい場合はどうすればいいの?
Transformのプロパティやメソッドを使えばできるわ。
ある特定のゲームオブジェクトの子オブジェクトを全て取得(列挙)する方法の解説記事です。
結論を述べると、スクリプトからTransformコンポーネントのAPIを用いれば可能です。
子オブジェクトの列挙には、直下の子のみ取得する方法、孫含めて再帰的に取得する方法があります。
ただし、コードの書き方によっては非アクティブなオブジェクトを取得できなくなる恐れがあり、注意が必要です。
本記事では、特定のゲームオブジェクトの子オブジェクトを全て取得する方法について、具体的なサンプルコードを示しながら解説していきます。また、実装における注意点のほか、拡張メソッドやLinqを活用して楽する方法も紹介します。
この作品はユニティちゃんライセンス条項の元に提供されています
- Unity 2021.1.21f1
目次 非表示
直下の子オブジェクトを列挙する
孫を含まない、指定されたオブジェクト直下の子オブジェクトを列挙する方法です。
for文を使う方法とforeach文を使う方法の2種類があります。必要に応じて使いやすいほうを選択すれば良いでしょう。
for文を使って列挙する
あるゲームオブジェクトの子オブジェクトは、Transform.GetChildメソッドで取得できます。
引数には取得する子要素のインデックスを指定します。インデックスは0始まりです。
参考:Transform-GetChild – Unity スクリプトリファレンス
子オブジェクトの総数は、Transform.childCountプロパティから取得できます。
これらのメソッドとプロパティを用いて子オブジェクトを取得するコードは次のようになります。
foreach文を使って列挙する
Transformコンポーネントは、foreachステートメントに対応しています。
参考:Transform – Unity スクリプトリファレンス
foreach文を用いると、子要素を先頭から順番に取得できます。
なお、内部的にはTransform.GetChildメソッドを呼び出して子要素を取得しているようです。
Transform.GetEnumeratorメソッドでEnumeratorクラスのインスタンスを生成しているため、処理コストはTransform.GetChildメソッド呼び出しよりも僅かに高くなります。
ただ、処理コストに大差は無いため、状況に合わせて使いやすい方法を選択する形で問題ないでしょう。
サンプルスクリプト
以下、for文を使って指定されたオブジェクト(Transform)直下の子オブジェクトを全て取得し、ログ出力する例です。
上記をGetAllChildrenExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチします。インスペクターよりParent項目に子オブジェクトを取得したい対象を指定すると機能します。
実行結果
指定したオブジェクトの直下の階層のオブジェクトがすべて取得できていることが確認できます。
また、非アクティブになっているオブジェクトに対しても取得できます。
孫オブジェクトも含めて再帰的に列挙する
孫オブジェクトも含めて列挙したい場合は、GetComponentsInChildrenメソッドを用いると簡単です。
上記のコードの場合、親オブジェクトも含んだ子オブジェクトが再帰的に得られます。引数にtrueを指定しないと非アクティブオブジェクトが結果から除外されるのでご注意ください。
参考:Component-GetComponentsInChildren – Unity スクリプトリファレンス
親オブジェクトを含まない子オブジェクトの配列を得たい場合は、次のように配列を新規作成する必要があります。
配列を2重に確保しなければいけないため、処理コストは高くなります。
このように、GetComponentsInChildrenメソッドを通じて子オブジェクトを取得できる理由は、Transformオブジェクトがゲームオブジェクトに対して必ず1つ存在しているためです。
Transform以外のコンポーネントを上記の方法で取得した場合、子オブジェクトを正しく取得できない状況が発生します。理由は、必ず1つ存在しているとは限らないためです。
これにより、結果が重複したり、逆に欠けたりする不都合が発生してしまいます。
サンプルスクリプト
指定されたゲームオブジェクト配下の子オブジェクトを再帰的に取得する例です。
上記をGetAllChildrenRecursiveExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターより必要な設定を行います。
Parentに親オブジェクトを指定してください。また、Include Parent項目のチェックの有無で親オブジェクトを結果に含めるかも指定できるようにしています。
実行結果
孫以下含め再帰的に取得できていることが確認できます。
Include Parent項目のチェックを入れた(親を結果に含める)場合は、先頭要素が親オブジェクトになります。
スクリプトの説明
親オブジェクトを含む場合は、次の処理でGetComponentsInChildrenメソッドの結果をそのまま返しています。
親オブジェクトを含まない場合は、以下処理で新しく配列を確保して結果をコピーしています。
Transformでなくゲームオブジェクトとして扱いたい場合
例えば親オブジェクトをゲームオブジェクトとし、ここから子オブジェクトをゲームオブジェクトとして取得したいケースもあるでしょう。
この場合、ゲームオブジェクトからTransformを取得し、その後前述の取得方法を用いれば良いです。
サンプルスクリプト
前述の例をゲームオブジェクトとして取得するようにした例です。
直下の子オブジェクト、および孫含めて再帰的に取得する処理両方が含まれます。
上記をGameObjectExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターより設定してください。
使い方は先の例と一緒です。
実行結果
先の例と同様に子オブジェクトが取得できていることを確認できました。
スクリプトの説明
直下の子オブジェクトを取得する処理は、親のTransformを取得してからGetChildメソッドなどでループで取得し、得られた子のTransformからゲームオブジェクトを取得する流れで実装しています。
孫含めた再帰的な取得も同様です。
ゲームオブジェクトの配列として取得する場合は、親含む含まないに関わらずゲームオブジェクト配列を作成する必要があります。
Linqを用いた子オブジェクトの列挙
ここまで紹介した方法は、すべて配列で結果を返していました。
インデックスによるランダムアクセスが必要ない場合、Linqを用いてコレクションとしてリターンするようにすれば、簡潔に実装できるだけでなく、親要素を除く操作などを配列を新規作成せずに実現できます。
直下の子オブジェクトをLinqで列挙する
次のようなコードになります。
TransformクラスはIEnumerable継承クラスですが、これをEnumerable.OfTypeメソッドによりIEnumerable<Transform>型に変換しています。
孫含む子オブジェクトをLinqで再帰的に列挙する
次のようなコードになります。
GetComponentsInChildrenで親を含む孫オブジェクトを取得し、Skip(1)で先頭要素(=親オブジェクト)のみを除外しています。
Linqを使えば凄く簡単に書けてとっても便利だね。
ただ、気を付けないと思わぬところで重たい処理をやりかねないので注意が必要だわ。
Linqでコレクションを扱うときの注意点
コレクションを扱う際、次のようなコードを実行すると、無駄なメモリ領域を確保する処理が走ってしまいます。
IEnumerable.ToArrayを実行すると、新しい配列を確保し、この配列に列挙した要素を格納する処理が走ります。
CPU、メモリどちらのリソースも消費してしまうため、必要最小限の使用に留めておくべきでしょう。
サンプルスクリプト
以下、Linqを使って子オブジェクトを列挙するサンプルスクリプトです。再帰的に列挙する処理も入っています。
使い方および実行結果は先の例と一緒のため、割愛させていただきます。
スクリプトの説明
子を再帰的に列挙するメソッドでは、親を含む場合はGetComponentsInChildrenの結果をそのまま返し、含まない場合はSkipメソッドで親(=先頭要素)をスキップしています。
配列を用いずに子オブジェクトに対して処理する
ここまで解説した方法では、Transformなどの配列を確保して子オブジェクトを取得していましたが、配列を用いずに実現することも可能です。
直下の子オブジェクトを処理する場合
次のようなコードでになります。
孫以下含めて再帰的に処理する場合
次のようにメソッドの再帰呼び出しを使えば実現可能です。
サンプルスクリプト
以下、実際に実装して動くようにした例です。
使用方法および実行結果は先の例と一緒のため割愛します。
拡張メソッドで扱いを簡単にする
ここまで解説した子オブジェクトの取得処理は、拡張メソッド化すると楽でしょう。
これにより、次のようにTransformから直でメソッドを呼び出すような要領で子オブジェクトを全て取得できます。
サンプルスクリプト
本記事で紹介した例を拡張メソッドとして実装した例です。
上記をUnityプロジェクトに保存すると、スクリプトから拡張メソッドが使えるようになります。
以下、使う側の例です。
使い方および実行結果は先の例と一緒のため割愛します。
さいごに
子オブジェクトをTransformとして列挙する方法を紹介しました。
取得自体は簡単ですが、子オブジェクトの数が多くなるとパフォーマンスに影響が出る点には注意する必要があります。
頻繁な呼び出しはなるべく避け、結果を使いまわすなどの工夫をして使うとよいでしょう。