【Unity】子オブジェクトを全て取得(列挙)する

こじゃらこじゃら

次のようなヒエラルキー上の子オブジェクトをスクリプトからすべて取得したい場合はどうすればいいの?

このはこのは

Transformのプロパティやメソッドを使えばできるわ。

ある特定のゲームオブジェクトの子オブジェクトを全て取得(列挙)する方法の解説記事です。

結論を述べると、スクリプトからTransformコンポーネントのAPIを用いれば可能です。
子オブジェクトの列挙には、直下の子のみ取得する方法孫含めて再帰的に取得する方法があります。

ただし、コードの書き方によっては非アクティブなオブジェクトを取得できなくなる恐れがあり、注意が必要です。

本記事では、特定のゲームオブジェクトの子オブジェクトを全て取得する方法について、具体的なサンプルコードを示しながら解説していきます。また、実装における注意点のほか、拡張メソッドLinqを活用して楽する方法も紹介します。

この作品はユニティちゃんライセンス条項の元に提供されています

動作環境
  • Unity 2021.1.21f1

スポンサーリンク

直下の子オブジェクトを列挙する

孫を含まない、指定されたオブジェクト直下の子オブジェクトを列挙する方法です。

for文を使う方法foreach文を使う方法の2種類があります。必要に応じて使いやすいほうを選択すれば良いでしょう。

for文を使って列挙する

あるゲームオブジェクトの子オブジェクトは、Transform.GetChildメソッドで取得できます。

public Transform GetChild(int index);

引数には取得する子要素のインデックスを指定します。インデックスは0始まりです。

子オブジェクトの総数は、Transform.childCountプロパティから取得できます。

これらのメソッドとプロパティを用いて子オブジェクトを取得するコードは次のようになります。

// 親オブジェクト
Transform parent;

・・・(中略)・・・

// 子オブジェクトを格納する配列作成
var children = new Transform[parent.childCount];

// 0~個数-1までの子を順番に配列に格納
for (var i = 0; i < children.Length; ++i)
{
    children[i] = parent.GetChild(i);
}

foreach文を使って列挙する

Transformコンポーネントは、foreachステートメントに対応しています。

foreach文を用いると、子要素を先頭から順番に取得できます。

// 親オブジェクト
Transform parent;

・・・(中略)・・・

// 子オブジェクトを格納する配列作成
var children = new Transform[parent.childCount];
var childIndex = 0;

// 子オブジェクトを順番に配列に格納
foreach (Transform child in parent)
{
    children[childIndex++] = child;
}

なお、内部的にはTransform.GetChildメソッドを呼び出して子要素を取得しているようです。

Transform.GetEnumeratorメソッドでEnumeratorクラスのインスタンスを生成しているため、処理コストはTransform.GetChildメソッド呼び出しよりも僅かに高くなります。 [1]

ただ、処理コストに大差は無いため、状況に合わせて使いやすい方法を選択する形で問題ないでしょう。

サンプルスクリプト

以下、for文を使って指定されたオブジェクト(Transform)直下の子オブジェクトを全て取得し、ログ出力する例です。

GetAllChildrenExample.cs
using UnityEngine;

public class GetAllChildrenExample : MonoBehaviour
{
    // 親オブジェクト
    [SerializeField] private Transform _parent;

    private void Start()
    {
        // 子オブジェクトを取得する
        var children = GetChildren(_parent);

        // 取得した子オブジェクト名をログ出力
        for (var i = 0; i < children.Length; i++)
        {
            Debug.Log(children[i].name);
        }
    }

    // parent直下の子オブジェクトをforループで取得する
    private static Transform[] GetChildren(Transform parent)
    {
        // 子オブジェクトを格納する配列作成
        var children = new Transform[parent.childCount];

        // 0~個数-1までの子を順番に配列に格納
        for (var i = 0; i < children.Length; ++i)
        {
            children[i] = parent.GetChild(i);
        }

        // 子オブジェクトが格納された配列
        return children;
    }
}

上記をGetAllChildrenExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチします。インスペクターよりParent項目に子オブジェクトを取得したい対象を指定すると機能します。

実行結果

指定したオブジェクトの直下の階層のオブジェクトがすべて取得できていることが確認できます。

また、非アクティブになっているオブジェクトに対しても取得できます。

孫オブジェクトも含めて再帰的に列挙する

孫オブジェクトも含めて列挙したい場合は、GetComponentsInChildrenメソッドを用いると簡単です。

Transform parent;

・・・(中略)・・・

// 親を含む子オブジェクトを再帰的に取得
// trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
var parentAndChildren = parent.GetComponentsInChildren<Transform>(true);

上記のコードの場合、親オブジェクトも含んだ子オブジェクトが再帰的に得られます。引数にtrueを指定しないと非アクティブオブジェクトが結果から除外されるのでご注意ください。

親オブジェクトを含まない子オブジェクトの配列を得たい場合は、次のように配列を新規作成する必要があります。

Transform parent;

・・・(中略)・・・

// 親を含む子オブジェクトを再帰的に取得
// trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
var parentAndChildren = parent.GetComponentsInChildren<Transform>(true);

// 子オブジェクトの格納用配列作成
var children = new Transform[parentAndChildren.Length - 1];

// 親を除く子オブジェクトを結果にコピー
Array.Copy(parentAndChildren, 1, children, 0, children.Length);

// 配列childrenに子オブジェクトが再帰的に格納される

配列を2重に確保しなければいけないため、処理コストは高くなります。

このように、GetComponentsInChildrenメソッドを通じて子オブジェクトを取得できる理由は、Transformオブジェクトがゲームオブジェクトに対して必ず1つ存在しているためです。

注意

Transform以外のコンポーネントを上記の方法で取得した場合、子オブジェクトを正しく取得できない状況が発生します。理由は、必ず1つ存在しているとは限らないためです。 [2]

これにより、結果が重複したり、逆に欠けたりする不都合が発生してしまいます。

サンプルスクリプト

指定されたゲームオブジェクト配下の子オブジェクトを再帰的に取得する例です。

GetAllChildrenRecursiveExample.cs
using System;
using UnityEngine;

public class GetAllChildrenRecursiveExample : MonoBehaviour
{
    // 親オブジェクト
    [SerializeField] private Transform _parent;

    // 親含めて子オブジェクトを再帰的に取得するかどうか
    [SerializeField] private bool _includeParent = true;

    private void Start()
    {
        // 子オブジェクトを取得する
        var children = GetChildrenRecursive(_parent, _includeParent);

        // 取得した子オブジェクト名をログ出力
        for (var i = 0; i < children.Length; i++)
        {
            Debug.Log(children[i].name);
        }
    }

    // parent直下の子オブジェクトを再帰的に取得する
    private static Transform[] GetChildrenRecursive(Transform parent, bool includeParent = true)
    {
        // 親を含む子オブジェクトを再帰的に取得
        // trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
        var parentAndChildren = parent.GetComponentsInChildren<Transform>(true);

        if (includeParent)
        {
            // 親を含む場合はそのまま返す
            return parentAndChildren;
        }

        // 子オブジェクトの格納用配列作成
        var children = new Transform[parentAndChildren.Length - 1];

        // 親を除く子オブジェクトを結果にコピー
        Array.Copy(parentAndChildren, 1, children, 0, children.Length);

        // 子オブジェクトが再帰的に格納された配列
        return children;
    }
}

上記をGetAllChildrenRecursiveExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターより必要な設定を行います。

Parentに親オブジェクトを指定してください。また、Include Parent項目のチェックの有無で親オブジェクトを結果に含めるかも指定できるようにしています。

実行結果

孫以下含め再帰的に取得できていることが確認できます。

Include Parent項目のチェックを入れた(親を結果に含める)場合は、先頭要素が親オブジェクトになります。

スクリプトの説明

親オブジェクトを含む場合は、次の処理でGetComponentsInChildrenメソッドの結果をそのまま返しています。

// 親を含む子オブジェクトを再帰的に取得
// trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
var parentAndChildren = parent.GetComponentsInChildren<Transform>(true);

if (includeParent)
{
    // 親を含む場合はそのまま返す
    return parentAndChildren;
}

親オブジェクトを含まない場合は、以下処理で新しく配列を確保して結果をコピーしています。

// 子オブジェクトの格納用配列作成
var children = new Transform[parentAndChildren.Length - 1];

// 親を除く子オブジェクトを結果にコピー
Array.Copy(parentAndChildren, 1, children, 0, children.Length);

// 子オブジェクトが再帰的に格納された配列
return children;

Transformでなくゲームオブジェクトとして扱いたい場合

例えば親オブジェクトをゲームオブジェクトとし、ここから子オブジェクトをゲームオブジェクトとして取得したいケースもあるでしょう。

この場合、ゲームオブジェクトからTransformを取得し、その後前述の取得方法を用いれば良いです。

GameObject parent;

・・・(中略)・・・

// 親オブジェクトのTransformを取得
var parentTransform = parent.transform;

// 子オブジェクトを格納する配列作成
var children = new GameObject[parentTransform.childCount];

// 0~個数-1までの子を順番に配列に格納
for (var i = 0; i < children.Length; ++i)
{
    // Transformからゲームオブジェクトを取得して格納
    children[i] = parentTransform.GetChild(i).gameObject;
}

サンプルスクリプト

前述の例をゲームオブジェクトとして取得するようにした例です。

直下の子オブジェクト、および孫含めて再帰的に取得する処理両方が含まれます。

GameObjectExample.cs
using UnityEngine;

public class GameObjectExample : MonoBehaviour
{
    // 親オブジェクト
    [SerializeField] private GameObject _parent;

    // 親含めて子オブジェクトを再帰的に取得するかどうか
    [SerializeField] private bool _includeParent = true;

    private void Start()
    {
        // 子オブジェクトを取得する
        var children = GetChildren(_parent);

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== children ===== ");
        for (var i = 0; i < children.Length; i++)
        {
            Debug.Log(children[i].name);
        }

        // 親オブジェクトを含めて子オブジェクトを再帰的に取得する
        var childrenRecursive = GetChildrenRecursive(_parent, _includeParent);

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== childrenRecursive ===== ");
        for (var i = 0; i < childrenRecursive.Length; i++)
        {
            Debug.Log(childrenRecursive[i].name);
        }
    }

    // parent直下の子オブジェクトをforループで取得する
    private static GameObject[] GetChildren(GameObject parent)
    {
        // 親オブジェクトのTransformを取得
        var parentTransform = parent.transform;

        // 子オブジェクトを格納する配列作成
        var children = new GameObject[parentTransform.childCount];

        // 0~個数-1までの子を順番に配列に格納
        for (var i = 0; i < children.Length; ++i)
        {
            // Transformからゲームオブジェクトを取得して格納
            children[i] = parentTransform.GetChild(i).gameObject;
        }

        // 子オブジェクトが格納された配列
        return children;
    }

    // parent直下の子オブジェクトを再帰的に取得する
    private static GameObject[] GetChildrenRecursive(GameObject parent, bool includeParent = true)
    {
        // 親オブジェクトのTransformを取得
        var parentTransform = parent.transform;

        // 親を含む子オブジェクトを再帰的に取得
        // trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
        var parentAndChildrenTransform = parentTransform.GetComponentsInChildren<Transform>(true);

        // 子オブジェクトの格納用配列作成
        var children = new GameObject[includeParent
            ? parentAndChildrenTransform.Length
            : parentAndChildrenTransform.Length - 1
        ];
        // 親を含む場合は0から、含まない場合は1からコピーする
        var offset = includeParent ? 0 : 1;

        // 結果を格納
        for (var i = 0; i < children.Length; i++)
        {
            // Transformからゲームオブジェクトを取得して格納
            children[i] = parentAndChildrenTransform[i + offset].gameObject;
        }

        // 子オブジェクトが再帰的に格納された配列
        return children;
    }
}

上記をGameObjectExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターより設定してください。

使い方は先の例と一緒です。

実行結果

先の例と同様に子オブジェクトが取得できていることを確認できました。

スクリプトの説明

直下の子オブジェクトを取得する処理は、親のTransformを取得してからGetChildメソッドなどでループで取得し、得られた子のTransformからゲームオブジェクトを取得する流れで実装しています。

// parent直下の子オブジェクトをforループで取得する
private static GameObject[] GetChildren(GameObject parent)
{
    // 親オブジェクトのTransformを取得
    var parentTransform = parent.transform;

    // 子オブジェクトを格納する配列作成
    var children = new GameObject[parentTransform.childCount];

    // 0~個数-1までの子を順番に配列に格納
    for (var i = 0; i < children.Length; ++i)
    {
        // Transformからゲームオブジェクトを取得して格納
        children[i] = parentTransform.GetChild(i).gameObject;
    }

    // 子オブジェクトが格納された配列
    return children;
}

孫含めた再帰的な取得も同様です。

// parent直下の子オブジェクトを再帰的に取得する
private static GameObject[] GetChildrenRecursive(GameObject parent, bool includeParent = true)
{
    // 親オブジェクトのTransformを取得
    var parentTransform = parent.transform;

    // 親を含む子オブジェクトを再帰的に取得
    // trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
    var parentAndChildrenTransform = parentTransform.GetComponentsInChildren<Transform>(true);

    // 子オブジェクトの格納用配列作成
    var children = new GameObject[includeParent
        ? parentAndChildrenTransform.Length
        : parentAndChildrenTransform.Length - 1
    ];
    // 親を含む場合は0から、含まない場合は1からコピーする
    var offset = includeParent ? 0 : 1;

    // 結果を格納
    for (var i = 0; i < children.Length; i++)
    {
        // Transformからゲームオブジェクトを取得して格納
        children[i] = parentAndChildrenTransform[i + offset].gameObject;
    }

    // 子オブジェクトが再帰的に格納された配列
    return children;
}

ゲームオブジェクトの配列として取得する場合は、親含む含まないに関わらずゲームオブジェクト配列を作成する必要があります。

Linqを用いた子オブジェクトの列挙

ここまで紹介した方法は、すべて配列で結果を返していました。

インデックスによるランダムアクセスが必要ない場合、Linqを用いてコレクションとしてリターンするようにすれば、簡潔に実装できるだけでなく、親要素を除く操作などを配列を新規作成せずに実現できます。 [3]

直下の子オブジェクトをLinqで列挙する

次のようなコードになります。

// parent直下の子オブジェクトLinqで列挙する
private static IEnumerable<Transform> EnumChildren(Transform parent)
{
    return parent.OfType<Transform>();
}

TransformクラスはIEnumerable継承クラスですが、これをEnumerable.OfTypeメソッドによりIEnumerable<Transform>型に変換しています。

孫含む子オブジェクトをLinqで再帰的に列挙する

次のようなコードになります。

// parent直下の子オブジェクトLinqで再帰的に列挙する
private static IEnumerable<Transform> EnumChildrenRecursive(Transform parent)
{
    // 親を含まない場合は親をスキップする
    return parent
        .GetComponentsInChildren<Transform>(true) // 親を含む子を再帰的に取得
        .Skip(1); // 親をスキップする
}

GetComponentsInChildrenで親を含む孫オブジェクトを取得し、Skip(1)で先頭要素(=親オブジェクト)のみを除外しています。

こじゃらこじゃら

Linqを使えば凄く簡単に書けてとっても便利だね。

このはこのは

ただ、気を付けないと思わぬところで重たい処理をやりかねないので注意が必要だわ。

Linqでコレクションを扱うときの注意点

コレクションを扱う際、次のようなコードを実行すると、無駄なメモリ領域を確保する処理が走ってしまいます。

// コレクションで子を取得
IEnumerable<Transform> children = parent.OfType<Transform>(true);
// 配列に変換
Transform[] childrenArray = children.ToArray();

IEnumerable.ToArrayを実行すると、新しい配列を確保し、この配列に列挙した要素を格納する処理が走ります。

CPU、メモリどちらのリソースも消費してしまうため、必要最小限の使用に留めておくべきでしょう。

サンプルスクリプト

以下、Linqを使って子オブジェクトを列挙するサンプルスクリプトです。再帰的に列挙する処理も入っています。

LinqExample.cs
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class LinqExample : MonoBehaviour
{
    // 親オブジェクト
    [SerializeField] private Transform _parent;

    // 親含めて子オブジェクトを再帰的に取得するかどうか
    [SerializeField] private bool _includeParent = true;

    private void Start()
    {
        // 子オブジェクトを取得する
        var children = EnumChildren(_parent);

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== children ===== ");
        foreach (var child in children)
        {
            Debug.Log(child.name);
        }

        // 親オブジェクトを含めて子オブジェクトを再帰的に取得する
        var childrenRecursive = EnumChildrenRecursive(_parent, _includeParent);

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== childrenRecursive ===== ");
        foreach (var child in childrenRecursive)
        {
            Debug.Log(child.name);
        }
    }

    // parent直下の子オブジェクトLinqで列挙する
    private static IEnumerable<Transform> EnumChildren(Transform parent)
    {
        return parent.OfType<Transform>();
    }

    // parent直下の子オブジェクトLinqで再帰的に列挙する
    private static IEnumerable<Transform> EnumChildrenRecursive(Transform parent, bool includeParent = true)
    {
        if (includeParent)
        {
            // 親を含む場合はそのまま返す
            return parent.GetComponentsInChildren<Transform>(true);
        }

        // 親を含まない場合は親をスキップする
        return parent
            .GetComponentsInChildren<Transform>(true) // 親を含む子を再帰的に取得
            .Skip(1); // 親をスキップする
    }
}

使い方および実行結果は先の例と一緒のため、割愛させていただきます。

スクリプトの説明

子を再帰的に列挙するメソッドでは、親を含む場合はGetComponentsInChildrenの結果をそのまま返し、含まない場合はSkipメソッドで親(=先頭要素)をスキップしています。

// parent直下の子オブジェクトLinqで再帰的に列挙する
private static IEnumerable<Transform> EnumChildrenRecursive(Transform parent, bool includeParent = true)
{
    if (includeParent)
    {
        // 親を含む場合はそのまま返す
        return parent.GetComponentsInChildren<Transform>(true);
    }

    // 親を含まない場合は親をスキップする
    return parent
        .GetComponentsInChildren<Transform>(true) // 親を含む子を再帰的に取得
        .Skip(1); // 親をスキップする
}

配列を用いずに子オブジェクトに対して処理する

ここまで解説した方法では、Transformなどの配列を確保して子オブジェクトを取得していましたが、配列を用いずに実現することも可能です。

直下の子オブジェクトを処理する場合

次のようなコードでになります。

Transform parent;

・・・(中略)・・・

var childCount = parent.childCount;
for (var i = 0; i < childCount; i++)
{
    // 子オブジェクトchildに対する処理
}

孫以下含めて再帰的に処理する場合

次のようにメソッドの再帰呼び出しを使えば実現可能です。

// 直下の子オブジェクトを再帰的に取得する
private static void ForEachChildrenRecursive(Transform parent, Action<Transform> action, bool includeParent = true)
{
    // 親を含む場合は親も結果として返す
    if (includeParent)
        action(parent);

    // 子オブジェクトを再帰的に処理
    var childCount = parent.childCount;
    for (var i = 0; i < childCount; i++)
    {
        ForEachChildrenRecursive(parent.GetChild(i), action);
    }
}

サンプルスクリプト

以下、実際に実装して動くようにした例です。

ForEachExample.cs
using System;
using UnityEngine;

public class ForEachExample : MonoBehaviour
{
    [SerializeField] private Transform _parent;

    // 親含めて子オブジェクトを再帰的に取得するかどうか
    [SerializeField] private bool _includeParent = true;

    private void Start()
    {
        // 直下の子オブジェクトをループで取得してログ出力
        Debug.Log(" ===== children ===== ");
        ForEachChildren(_parent, child => Debug.Log(child.name));

        // 子オブジェクトを再帰的に取得してログ出力
        Debug.Log(" ===== childrenRecursive ===== ");
        ForEachChildrenRecursive(_parent, child => Debug.Log(child.name), _includeParent);
    }

    // 直下の子オブジェクトをループで取得する
    private static void ForEachChildren(Transform parent, Action<Transform> action)
    {
        var childCount = parent.childCount;
        for (var i = 0; i < childCount; i++)
        {
            action(parent.GetChild(i));
        }
    }

    // 直下の子オブジェクトを再帰的に取得する
    private static void ForEachChildrenRecursive(Transform parent, Action<Transform> action, bool includeParent = true)
    {
        // 親を含む場合は親も結果として返す
        if (includeParent)
            action(parent);

        // 子オブジェクトを再帰的に処理
        var childCount = parent.childCount;
        for (var i = 0; i < childCount; i++)
        {
            ForEachChildrenRecursive(parent.GetChild(i), action);
        }
    }
}

使用方法および実行結果は先の例と一緒のため割愛します。

拡張メソッドで扱いを簡単にする

ここまで解説した子オブジェクトの取得処理は、拡張メソッド化すると楽でしょう。

これにより、次のようにTransformから直でメソッドを呼び出すような要領で子オブジェクトを全て取得できます。

Transform parent;

・・・(中略)・・・

// 拡張メソッドで直下の子オブジェクトを取得
var children = parent.GetChildren();

サンプルスクリプト

本記事で紹介した例を拡張メソッドとして実装した例です。

ChildrenExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public static class ChildrenExtensions
{
    #region Transform配列版

    // parent直下の子オブジェクトをforループで取得する
    public static Transform[] GetChildren(this Transform parent)
    {
        // 子オブジェクトを格納する配列作成
        var children = new Transform[parent.childCount];

        // 0~個数-1までの子を順番に配列に格納
        for (var i = 0; i < children.Length; ++i)
        {
            children[i] = parent.GetChild(i);
        }

        // 子オブジェクトが格納された配列
        return children;
    }

    // parent直下の子オブジェクトを再帰的に取得する
    public static Transform[] GetChildrenRecursive(this Transform parent, bool includeParent = true)
    {
        // 親を含む子オブジェクトを再帰的に取得
        // trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
        var parentAndChildren = parent.GetComponentsInChildren<Transform>(true);

        if (includeParent)
        {
            // 親を含む場合はそのまま返す
            return parentAndChildren;
        }

        // 子オブジェクトの格納用配列作成
        var children = new Transform[parentAndChildren.Length - 1];

        // 親を除く子オブジェクトを結果にコピー
        Array.Copy(parentAndChildren, 1, children, 0, children.Length);

        // 子オブジェクトが再帰的に格納された配列
        return children;
    }

    #endregion

    #region ゲームオブジェクト配列版

    // parent直下の子オブジェクトをforループで取得する
    public static GameObject[] GetChildren(this GameObject parent)
    {
        // 親オブジェクトのTransformを取得
        var parentTransform = parent.transform;

        // 子オブジェクトを格納する配列作成
        var children = new GameObject[parentTransform.childCount];

        // 0~個数-1までの子を順番に配列に格納
        for (var i = 0; i < children.Length; ++i)
        {
            // Transformからゲームオブジェクトを取得して格納
            children[i] = parentTransform.GetChild(i).gameObject;
        }

        // 子オブジェクトが格納された配列
        return children;
    }

    // parent直下の子オブジェクトを再帰的に取得する
    public static GameObject[] GetChildrenRecursive(this GameObject parent, bool includeParent = true)
    {
        // 親オブジェクトのTransformを取得
        var parentTransform = parent.transform;

        // 親を含む子オブジェクトを再帰的に取得
        // trueを指定しないと非アクティブなオブジェクトを取得できないことに注意
        var parentAndChildrenTransform = parentTransform.GetComponentsInChildren<Transform>(true);

        // 子オブジェクトの格納用配列作成
        var children = new GameObject[includeParent
            ? parentAndChildrenTransform.Length
            : parentAndChildrenTransform.Length - 1
        ];
        // 親を含む場合は0から、含まない場合は1からコピーする
        var offset = includeParent ? 0 : 1;

        // 結果を格納
        for (var i = 0; i < children.Length; i++)
        {
            // Transformからゲームオブジェクトを取得して格納
            children[i] = parentAndChildrenTransform[i + offset].gameObject;
        }

        // 子オブジェクトが再帰的に格納された配列
        return children;
    }

    #endregion

    #region IEnumerable<Transform>版

    // parent直下の子オブジェクトLinqで列挙する
    public static IEnumerable<Transform> EnumChildren(this Transform parent)
    {
        return parent.OfType<Transform>();
    }

    // parent直下の子オブジェクトLinqで再帰的に列挙する
    public static IEnumerable<Transform> EnumChildrenRecursive(this Transform parent, bool includeParent = true)
    {
        if (includeParent)
        {
            // 親を含む場合はそのまま返す
            return parent.GetComponentsInChildren<Transform>(true);
        }

        // 親を含まない場合は親をスキップする
        return parent
            .GetComponentsInChildren<Transform>(true) // 親を含む子を再帰的に取得
            .Skip(1); // 親をスキップする
    }

    #endregion

    #region ループ版

    // 直下の子オブジェクトをループで取得する
    private static void ForEachChildren(this Transform parent, Action<Transform> action)
    {
        var childCount = parent.childCount;
        for (var i = 0; i < childCount; i++)
        {
            action(parent.GetChild(i));
        }
    }

    // 直下の子オブジェクトを再帰的に取得する
    private static void ForEachChildrenRecursive(
        this Transform parent,
        Action<Transform> action,
        bool includeParent = true)
    {
        // 親を含む場合は親も結果として返す
        if (includeParent)
            action(parent);

        // 子オブジェクトを再帰的に処理
        var childCount = parent.childCount;
        for (var i = 0; i < childCount; i++)
        {
            ForEachChildrenRecursive(parent.GetChild(i), action);
        }
    }

    #endregion
}

上記をUnityプロジェクトに保存すると、スクリプトから拡張メソッドが使えるようになります。

以下、使う側の例です。

UseExtensionExample.cs
using UnityEngine;

public class UseExtensionExample : MonoBehaviour
{
    // 親Transform
    [SerializeField] private Transform _parent;

    // 親含めて子オブジェクトを再帰的に取得するかどうか
    [SerializeField] private bool _includeParent = true;

    private void Start()
    {
        // 拡張メソッドで直下の子オブジェクトを取得
        var children = _parent.GetChildren();

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== children ===== ");
        foreach (var child in children)
        {
            Debug.Log(child.name);
        }

        // 親オブジェクトを含めて子オブジェクトを再帰的に取得する
        var childrenRecursive = _parent.EnumChildrenRecursive(_includeParent);

        // 取得した子オブジェクト名をログ出力
        Debug.Log(" ===== childrenRecursive ===== ");
        foreach (var child in childrenRecursive)
        {
            Debug.Log(child.name);
        }
    }
}

使い方および実行結果は先の例と一緒のため割愛します。

さいごに

子オブジェクトをTransformとして列挙する方法を紹介しました。

取得自体は簡単ですが、子オブジェクトの数が多くなるとパフォーマンスに影響が出る点には注意する必要があります。

頻繁な呼び出しはなるべく避け、結果を使いまわすなどの工夫をして使うとよいでしょう。

参考サイト

スポンサーリンク