FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム » Unity »【Unity】【C#】非アクティブも含めて、Transform (GameObject) をパス名で取得する

【Unity】【C#】非アクティブも含めて、Transform (GameObject) をパス名で取得する  


 今回もこれまでの Transform 系の便利メソッド応用。

 前回GameObject.Find でのパス名の使い方を解説したが、このメソッドは非アクティブなオブジェクトにはヒットしないという欠点がある。

 なので、非アクティブなオブジェクトを取得したいときは、Resources.FindObjectsOfTypeAll() をゴニョゴニョする、みたいな例が多いね。

 それでもいいけど、これまでの応用でも検出はできるので、せっかくなので簡単な再帰ルーチンを用いて、パスで Transform (GameObject) を検索するメソッドを作ってみよう。

 今回のメソッドでは Transform を返しているが、GameObject が欲しいなら、戻値から transform.gameObject を取得すれば良い


(※) Unity 2019.4.14f1 / Windows10(x64) で確認



●ルートからのパス名(絶対パス)を指定して Transform を検出する(非アクティブ, ""(空の名前) も含む)
using UnityEngine;

/// <summary>
/// ルートからのパス名(絶対パス)を指定して Transform を検出する(非アクティブ, ""(空の名前) も含む).
/// 2020/11/09 Fantom (Unity 2019.4)
/// http://fantom1x.blog130.fc2.com/blog-entry-380.html
/// ・同じパス名が複数ある場合、一番最初に見つけたものになる.
/// ・パス名は "/" 区切りで表すので、名前に "/" が入っている場合は上手く検出できない(曖昧になる).
/// ・"nameA/nameB/" → ""(空の名前の Transform) がヒットする(ヒットしないようにするには、最後の '/' を取り除いておく)
/// ・"/name" ではルートが ""(空の名前) で配下が "name" の検出となる.
/// ・GameObject.Find() のようにルート以下のパス名にはヒットできない(絶対パスのみ).
/// ・検索するオブジェクトがアクティブ前提なら、GameObject.Find() の方が速い.
/// ・再帰での全探索のため、負荷が高く遅いので、ランタイム時の使用は注意.
/// ・検索範囲は、現在のシーン:SceneManager.GetActiveScene() 内のみ(GetRootTransforms() に依存).
/// </summary>
/// <param name="path">絶対パス指定: "nameA/nameB" (頭に '/' は付けない)</param>
/// <returns>見つかった Transform / 見つからなかったときは null</returns>
public static Transform FindTransformByPath(string path)
{
if (path == null) //"" (空の名前) は可
return null;

//ルートとそれ以下に分ける
string rootName = path;
string relativePath = null; //null はルートのみのときのフラグとなっている
int idx = path.IndexOf('/');
if (idx >= 0)
{
rootName = path.Substring(0, idx);
relativePath = path.Substring(idx + 1);
}

var roots = GetRootTransforms(); //ルートにある Transform 全て取得(非アクティブも含む)※以前の記事を参照

//全てのルート Transform 以下に再帰で path を検出する
foreach (var tr in roots)
{
if (tr.name == rootName)
{
if (relativePath == null) //ルートのみのとき
return tr;

var result = FindRecursive(tr, path, "");
if (result != null)
return result;
}
}

return null;
}


//再帰処理用
/// <summary>
/// 指定 Transform から、深さ優先探索で Transform を検出する(非アクティブ, ""(空の名前) も含む).
/// 2020/11/09 Fantom (Unity 2019.4)
/// http://fantom1x.blog130.fc2.com/blog-entry-380.html
/// ・同じパス名が複数ある場合、一番最初に見つけたものになる.
/// ・パス名は "/" 区切りで表すので、名前に "/" が入っている場合は上手く検出できない(曖昧になる).
/// ・Transform.Find() に近い結果になるが、最後が '/' の場合、配下の ""(空の名前) として検出する.
/// ・また、Transform.Find() と違い、自身を含める検出となる.
/// (例) "nameA/nameB/nameC" を検出したいとき、transform = nameA, path = "nameA/nameB/nameC" を引数に与える.
/// </summary>
/// <param name="transform">検出開始の Transform</param>
/// <param name="targetPath">検索するパス名</param>
/// <param name="currentPath">再帰での現在のパス名</param>
/// <returns>見つかった Transform / 見つからなかったときは null</returns>
private static Transform FindRecursive(Transform transform, string targetPath, string currentPath)
{
if (transform == null || targetPath == null)
return null;

if (string.IsNullOrEmpty(currentPath))
currentPath = transform.name;

if (targetPath == currentPath)
return transform;

if (transform.childCount == 0 || !targetPath.StartsWith(currentPath))
return null;

for (int i = 0; i < transform.childCount; i++)
{
var child = transform.GetChild(i);
var childPath = currentPath + "/" + child.name; //""(空の名前) でもヒットできる

Transform tr = FindRecursive(child, targetPath, childPath);
if (tr != null)
return tr;
}
return null;
}

●使用例(メインコード等)
using System.Linq;
using UnityEngine;

var transforms = GetComponentsAll<Transform>(); //※以前の記事を参照
foreach (var tr in transforms)
{
var path = tr.GetFullPathName(); //※以前の記事を参照
var go = FindTransformByPath(path).gameObject;
Debug.Log($"{go.name} : path = {path}");
}


 GetRootTransforms(), GetComponentsAll(), GetFullPathName() の3つのメソッドは以前の記事からコピペして欲しい。テストは前回のサンプルとほぼ同様なもので試してみよう。内容的には、全ての Transform を取得 → パス名を取得 → パス名で Transform.gameObject を取得ということをやっている。

 ただ、GameObject.Find() のパス名の書き方とは少し違う点を気をつけて欲しい。なるべくコメントにも書いておいたが、今回のメソッドは絶対パス指定のみとなっているため、途中階層にあるパスのパターンにはヒットしない

 また、空白の名前でもヒットするようになっているため、頭に '/' は付けないパス名となっている。頭に '/' を付けたときは親が ""(空の名前)で子が名前として認識する。また GameObject.Find() では最後の '/' の有無は関係ないが、今回のメソッドの場合、最後のオブジェクトが ""(空の名前) でヒットする。

 もちろん、名前自体に '/' が入っていると上手く検出できない("A/B" は1つの名前とも階層とも取れるので曖昧となる。※これは GameObject.Find でも同じ)。

 FindRecursive() は再帰処理用なので private にしてあるが、普段も利用したければ、

public static Transform FindRecursive(this Transform transform, string targetPath)
{
return FindRecursive(transform, targetPath, "");
}

みたいに public なオーバーロードを定義しても良いだろう。この場合、指定した Transform 以下を検索することになる。Transform.Find() に近い感じになるが、自分自身を起点に検索範囲に含まれる点が違う


 これらメソッドも再帰処理してるので、ランタイムでの利用はほどほどにした方が良いだろう。エディタツールなどで使う分には、ほとんど問題ないパフォーマンスが出る。

 通常、決まったオブジェクトを検索したいのなら、GameObject.FindWithTag() を使った方がパフォーマンスが良い。ただし、事前にタグを作成(設定)しておく必要がある。

 ヒエラルキーでユニークな名前のオブジェクトなら、使いやすいかも知れない。







(関連記事)
【Unity】【C#】シーン(ヒエラルキー)のルートにある Transform を全て取得する(非アクティブも含む)
【Unity】【C#】非アクティブも含めて、全ての GameObject からコンポーネントを取得する
【Unity】【C#】ヒエラルキー(シーン)の全てのオブジェクト(Transform)をスキャンして処理をする
【Unity】【C#】Transfrom (GameObject) のパス名を取得する


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityリファレンス  Transform 
tb: 0   cm: --


トラックバック

トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/380-d5118264
この記事にトラックバックする(FC2ブログユーザー)

プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop