【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」を利用してください。

 

【Unity】NullReferenceExceptionのエラーと対策
本日のテーマ

「NullReferenceException」の原因と対策

 

NullReferenceExceptionとは

NullReferenceException』とは、

オブジェクトの参照が切れているときに発生するエラーのことです。

 

もっと噛み砕いた言い方をするなら、

「そんなオブジェクト見つからないよ?」

「どのオブジェクトのことをいってるの?」

と、このよう状況のときに発生するエラーです。

 

エラーメッセージの内容

UnityエディターのConsole(コンソール)画面に、

このようなエラーメッセージが表示されます。

エラーメッセージ2行目に、

エラーが発生した関数(Example.Start())と、

エラーが出たファイルとその行数(Assets/Scripts/Example.cs:12)

が表示されています。

 

エラーが起こった場所の特定に利用できます。

このエラーメッセージをダブルクリックすることで、

エラー箇所に飛ぶ(そのスクリプトを開く)こともできるので非常に便利です。

 

3つの原因と対策

では、どういった場面で「NullReferenceException」のエラーが起こるのか。

ポイントは3つです。

 

スクリプトで定義した変数の初期化を行っていない

このスクリプトでは、

“player”というGameObjectを定義しているが、初期化が行われずに使用されています。

 

[原因]

スクリプトで定義したGameObjectを、初期化することなく使用した。

 

[対策]

Find関数を使ってシーン内(Hierarchy)からGameObjectを取得したり、

Prefabを使ってGameObjectを生成するなどして、

しっかりと初期化を行ってください。

 

こちらが、Find関数を使った正しいスクリプトになります。

 

 

続いては、

上記のようにFind関数を使って、しっかりと初期化したにもかかわらず、

NullReferenceExceptionのエラーが出る場合です。

 

Find関数でGameObjectの取得に失敗している

スクリプトには問題ないが、NullReferenceExceptionのエラーが出る場合があります。

これはFind関数を使ってGameObjectを取得している場合に発生して、

原因は2つあります。

 

[原因1]

そもそも指定したGameObjectがScene内に存在していない

存在していないのだから取得できなくて当然ですよね。

もちろん、指定したGameObject名のスペルミスの場合も同様です。

 

[対策]

Scene内(Hierarchy)にGameObjectが間違いなく存在していること、

指定したGameObject名に間違いがないことを確認してください。

 

 

[原因2]

Find関数で取得するGameObjectが非アクティブになっている。画像2image2

見落としがちですが、

GameObjectが非アクティブ、すなわち「チェックが外れた状態」になっていると、

GameObject.Findでは、GameObjectを取得することができずにnullが返されます。

結果、NullReferenceExceptionのエラーが発生します。

 

[対策]

GameObjectをアクティブに設定することです。

どうしても非アクティブのままにしたい場合には、

GameObject.Find()ではなく、Transform.Find()を使用するようにしてください。

説明すると長くなってしまうので、当記事では割愛させていただきますが、

詳しくは「さよならエラー!『Find関数』を深く理解する!」をご覧ください。

 

 

インスペクターでGameObjectの初期化を行っていない

UnityエディタのInspector(インスペクター)で、

初期化しなければならないGameObjectの初期化を忘れている場合にも、

NullReferenceExceptionのエラーが発生します。


[原因]

インスペクターでGameObjectの初期化を行っていない。

 

[対策]

エラーメッセージを見て、

エラーが出ているスクリプトがアタッチされているGameObjectのインスペクターで、

GameObjectを正しく設定してください。

 

まとめ

NullReferenceExceptionのエラーが発生したときのチェックポイントです。

1.スクリプトで定義したGameObjectの初期化し忘れがないか

2.Find関数を使っている場合、GameObjectの指定を間違っていないか

3.GameObject.Findを使っている場合、GameObjectが非アクティブになっていないか

4.インスペクター上で、GameObjectの初期化し忘れがないか

 

これで、NullReferenceExceptionのエラーと対策の解説を終わります。

 

【Unity】エラー発生!?非アクティブ設定の落とし穴!

 

Unityでゲームを作っていると、予期せぬエラーが発生してしまうことがある。

そんなエラーを解決する一助になればと思い記事にしておきます。

本日のテーマ

アタッチされたスクリプトの
『非アクティブ設定』で起こるエラー

 

こんな経験ありませんか?

 

アタッチされているスクリプトを、

「機能を一時的に止めるために、非アクティブに設定する」

あるいは、

不要になったスクリプトだけど、後から使うかもしれないからと、

非アクティブに設定して、アタッチしたままにしておく」

 

このようなことを経験したことはありませんか?

 

『非アクティブに設定する』とは

 

非アクティブに設定する』とは、

アタッチされたスクリプトの“チェックをはずす”ということです。

こんな感じですね。

 

『非アクティブ設定』の落とし穴

 

スクリプトを非アクティブに設定する場合には、

注意しなければいけないポイントが1つあります。

 

それは、

Point

スクリプトを『非アクティブ』に設定したからといって、そのスクリプトの機能が完全に停止したわけではない

ということです。

 

どういうことかというと、

 

スクリプトを『非アクティブ』に設定したとしても、

・Awake関数

・OnCollision系関数

・OnTrigger系関数

少なくとも上記3種のイベント関数は“実行される”のです。

 

ちなみに、Start関数、Update系関数、OnGUI関数の3種のイベント関数は、

非アクティブ設定で呼び出されることはありません。

 

ですので、

非アクティブに設定するスクリプトに、

OnCollision系やOnTrigger系のイベント関数が記述されている場合には、

十分注意してください。

 

まとめ

 

まとめ

Awake、OnCollision系、OnTrigger系のイベント関数は、スクリプトを非アクティブに設定しても “実行される” ので注意が必要

スクリプトを非アクティブに設定する場合は、

上記のことをしっかり意識して行い、不要なスクリプトは、

きちんとRemove Componentするよう心がけてください。

 

以上で、エラー発生!?非アクティブ設定の落とし穴!の解説を終わります。