【Unity】さよならエラー!『Find関数』を深く理解する!

本日のテーマ

GameObjectの検索&取得に使われる
「Find関数」について

Unityにおいて、オブジェクトを取得・検索するために使われる「Find」関数。

間違った使い方をすると、エラーが発生するだけでなく、ゲームが重くなる原因にもなります。

「Find」関数を正しく使い、快適なUnityライフを目指しましょう。

 

 

もくじ

1.『GameObject.Find』について
  1.『GameObject.Find』とは
  2.『GameObject.Find』の使用例
  3.『GameObject.Find』の使用上の注意
2.『Transform.Find』について
  1.『Transform.Find』とは
  2.『Transform.Find』の使用例
  3.『Transform.Find』の使用上の注意
3.『GameObject.Find』と『Transform.Find』の比較
4.具体例で理解を深める
5.Find関数のまとめ

 

 

GameObject.Findについて

 

GameObject.Findとは

GameObject.Find」は、Hierarchyから指定した「GameObject」を取得する関数です。

 

使用例

・GameObject.Find(“example”);

・GameObject.Find(“Parent_1/Parent_2/example”);

 

使用上の注意

「GameObject.Find」は、Hierarchy内の全オブジェクトを検索して

指定したGameObjectを見つけ出すため、時間もかかり重くなる原因になります。

完成品のゲームでの使用は極力控えた方が良いみたいです。

 

代わりとして「GameObject.FindWithTag」を使うか、

子オブジェクトの検索の場合には「Transform.Find」を使うようにしましょう。

どうしても使用したいのであれば、Start関数Awake関数の中だけにすることです。

 

GameObject.Find」では、

指定したGameObjectが非アクティブ(チェックが入っていない)だった場合には、

取得することができない(nullが返される)ということにも注意が必要です。

 

非オブジェクトの取得に関しては例外があるみたいです。

 

GameObject.Find(“example”);

このように直接オブジェクトを指定して取得する場合には、

非アクティブでの取得は絶対にできないのですが、

 

GameObject.Find(“Parent_1/Parent_2/Parent_3/example”);

このようにスラッシュ(/)を使ったパス付きで指定して取得する場合には、

非アクティブであっても「一部」取得できるみたいです(Unityのバグの可能性も?)。

1階層目(Parent_1)と2階層目(Parent_2)が非アクティブでさえなければ

3階層目以降の非アクティブなオブジェクトは取得することができます

 

例えば、

「Parent_1 > Parent_2 > Parent_3 > example」 という構成になっている場合、

Parent_1とParent_2がアクティブでありさえすれば、

Parent_3やexampleが非アクティブであっても

GameObject.Find(“Parent_1/Parent_2/Parent_3/example”);

これでexampleを取得することができます。

 

逆にいえば、Parent_1かParent_2が非アクティブであったなら

exampleがアクティブであったとしてもnullが返ってきて取得することはできません。

不思議な話です・・・。

 

非アクティブなGameObjectを取得したい場合には、

無難に「Transform.Find」を使用するほうが良いですね。

 

 

Transform.Findについて

 

Transform.Findとは

Transform.Find」は、子オブジェクトから指定した「Transform」を取得する関数です。

 

使用例

・this.transform.Find(“example”);

・this.transform.Find(“child_1/child_2/example”);

 

使用上の注意

取得されるのが「GameObject」ではなく「Transform」であることに注意が必要です。

Transform.Find」では、子オブジェクトが検索の対象となっています。

Transform.Find」では、アクティブ・非アクティブに関係なく取得することできます。

 

 

2つの比較

 

GameObject.Find

・「GameObject」を検索&取得する

・Hierarchy内すべてが検索対象となる

・基本的には非アクティブなGameObjectは取得できない

 

Transform.Find

・「Transform」を検索&取得する

・子オブジェクトが検索対象となる

・非アクティブなGameObjectのTransformも取得できる

 

 

「GameObject.Find」と「Transform.Find」の説明が終わりましたので、

具体例をみて、さらに理解を深めていきましょう。

 

 

具体例で理解を深める

以下ようなファイル構成になっている場合のGameObjectの取得方法を見ていきましょう。

スクリプトは「Parent_1」にアタッチしているものと考えてください。

 

基礎1. 「target_1」を取得してみましょう

 

GameObject.Findを使った場合

例1)GameObject target = GameObject.Find(“target_1”); ※取得できません

例2)GameObject target = GameObject.Find(“Parent_1/Parent_2/target_1”);

target_1が非アクティブなので、基本は取得できませんが、

例2の場合には取得することができます。ただしこの方法は用いないほうが良いですね。

 

Transform.Findを使った場合

例1)Transform target = this.transform.Find(“Parent_2/target_1”);

例2)GameObject target = this.transform.Find(“Parent_2/target_1”).gameObject;

Transform.Findを使えば、target_1が非アクティブでも取得可能ですね。

注意が必要なのは、Transform.Findを使ったときは「Transform」で取得されることです。

例2では「.gameObject」として、TransformからGameObjectを取得しています。

 

 

基礎2. 「target_2」を取得してみましょう

 

GameObject.Findを使った場合

今回の場合、GameObject.Findの使用ではtarget_2を取得することはできません。

 

Transform.Findを使った場合

例1)Transform target = this.transform.Find(“Parent_3/target_2”);

例2)GameObject target = this.transform.Find(“Parent_3/target_2”).gameObject;

と、非アクティブでも問題なく取得することができます。

 

 

応用 .「target」を取得してみましょう

少し複雑なパターンも練習しておきましょう。

以下ような構成になっていて、スクリプトが”Script”にアタッチされている場合です。

 

“Script”にスクリプトがアタッチされているため、

this.transform.Find(“Parent_2/target”);

このように直接”子オブジェクト”を探す書き方ができません。

 

一旦、targetの親オブジェクトで「アクティブ」になっているParent_2を取得し、

そのあとでtargetを取得する、という手順を踏みます。

1.GameObject temp = GameObject.Find(“Parent_1/Parent_2”);

2.GameObject target = temp.transform.Find(“target”).gameObject;

この手順を踏めば、非アクティブなtargetも取得することができます。

 

上記を1つにまとめて書くこともできます。

GameObject target = GameObject.Find(“Parent_1/Parent_2”).transform.Find(“target”).gameObject;

こんな感じになります。

 

どうでしたでしょうか。

子オブジェクトの検索しかできないですが、非アクティブでも取得できる「Transform.Find」は便利ですね。

 

ちなみに、以前は子オブジェクトを取得するために、Transform.FindChildとい関数が使われていましたが(現在も使うことはできますが)、古い型なので使用しないように注意しましょう。

 

 

Find関数のまとめ

GameObject.Find」はゲームを重くする原因になるので、極力使用は避けるべきです。

代わりとして「GameObject.FindWithTag」や「Transform.Find」を利用する。

どうしても使いたいなら、Start()Awake()だけでの使用に限る。

非アクティブのGameObject取得には「Transform.Find」を利用してください。