Unityの関数AwakeとStartの違いと現状のプログラミングの問題点

今回はゲームの機能を作成するのではなく、AwakeとStartの違いと現状のプログラミングの仕方の問題点を見ていきます。

元々準備されている関数でAwakeとStartというものがあります。
この二つは同じタイミングで実行されるような気がしますが、実は違います。

わたしの場合あまり気にせずすべてStart関数で変数の初期化、ゲームオブジェクトの取得を行っていました。

なぜそうしているかと言うと、購入した本の中で他に設定しているゲームオブジェクトを取得する際は、Awakeだと初期化処理がまだ行われておらず取得出来ない可能性があるので、Start関数内でやると良いと書いてあったからです。

変数とゲームオブジェクトを同じ扱いにしていました・・・・・(^_^;)

スポンサーリンク

AwakeとStartの違いをマニュアルで調べる

Unityのマニュアルを見てみると、

・AwakeはStart関数の前およびプレハブのインスタンス化直後(ゲームオブジェクトが有効である事)
・Startは最初のUpdateフレームが呼び出される前

となっています。

Awakeの方が先に呼び出されるんですね、なら変数の初期化はAwakeの方が良さそうです。
ゲームオブジェクトの取得はStart内が良さそうです。

AwakeとStartの違いをテストスクリプトを作成し検証する

Unityのチュートリアルでもありますが、AwakeとStartの実行順序確認の為、以下のスクリプトを用意して、試してみます。

awake1

ちゃんとAwakeが先に表示されます。
このAwakeなんですが、実はスクリプトが非アクティブでも実行されます。

awake2

上のように設定したスクリプトのチェックを外し非アクティブ(enabledがfalseの状態)にしておきます。
Unityの実行ボタンを押して確認してみます。

awake3

上のようにスクリプトを非アクティブにしていてもAwake関数は呼ばれます。

awake4

ただ上のようにスクリプトを設定しているゲームオブジェクトのチェックを外し、非アクティブ(activeSelfがfalse)にしていた場合ゲームオブジェクトがアクティブにならないとAwake関数は呼ばれません。

ゲーム実行中にゲームオブジェクトをアクティブにするとスクリプトのアクティブの状態にかかわらずAwake関数が呼ばれます。

現状のプログラミングの問題点

--おそらく今は大丈夫、原因は最後に追記します(2017年04月20日)--

ここで現状のわたくしのプログラミングの問題点について考える必要が出てきました。
Start関数内でゲームオブジェクトの取得はいいとして、他オブジェクトに設定されたコンポーネントの取得も同じようにしていいんだろうか?という事です。

なぜこのように思ったかというと、時々Start関数内で他オブジェクトのコンポーネントを取得している個所でエラーが発生する為です。

エラーの内容はゲームオブジェクトやコンポーネントの取得が出来ていないという事でした。

Unityを一旦終了し、再起動するとエラーが消えたので、Unityのバグかな?とも思っていたんですが、ゲームオブジェクトやコンポーネントの取得の方法が原因のような気がしてきました。

他ゲームオブジェクトに設定しているスクリプトを取得するスクリプトを別のゲームオブジェクトのStart関数内に記述しているとしたら、他ゲームオブジェクトのスクリプトのStart関数内での初期化→取得するスクリプトのStart関数で取得の順番で実行されなければいけませんが、もし逆の順番でStart関数が実行されたらデータの初期化が完了していない状態になるんでしょうか?

でもそれであればデータが入ってないとしてもコンポーネントの取得は出来そうな気もしますが・・・。
Start関数が実行されるタイミングで他のゲームオブジェクトのロードが終わっていない事もあるんでしょうか?

ここら辺がよくわかりません。

Start関数内で普通に他オブジェクトのスクリプトは取得するはずですし・・・(^_^;)

他ゲームオブジェクトのスクリプトを取得する際に、GameObject.FindWithTagでタグの名前でゲームオブジェクトを検索し、そのゲームオブジェクトに設定しているスクリプトを取得している個所でたまにエラーが発生したりします。

いつも出るエラーではないので、対処が難しく再起動で対処してましたが、
別の方法を模索してみます。

FindWithTagで探すとダメなのかと思い探すゲームオブジェクトをpublicで指定してそこからスクリプトを取得する。
これで対処出来るなら大丈夫かも。

エラーがいつも出るわけではないので、対処出来たのかどうかもわからずじまいです。

コンポーネントを取得出来ていない原因で考えられるのがもう一つ、JavaScriptが動的型付けだからかも、Unityで使うJavaScriptは型を指定しますが、通常のJavaScriptは型を臨機応変に変更していきます。

その型変換がうまくいかずに取得出来なかった可能性もあります。
それに対応する為にはコンポーネントの取得の記述方法を変えます。

status = GameObject.FindWithTag(“Player”).GetComponent(MyStatus); // 今までの取得方法

status = GameObject.FindWithTag(“Player”).GetComponent.<MyStatus>(); // 型を指定した取得方法

以前の記事

Unityのコンソールに表示されるスクリプトの警告文の内容を見て修正する
Unityのコンソールタブに表示される警告の意味を理解し、警告表示をなくしていきます

でGetComponentで取得する型はComponent型だと言いましたが、その型変換がうまくいっていなかったのかもしれません。

つまり現状の対処としては

・GetComponent(取得するコンポーネント) as 型
・GetComponent.<取得するコンポーネント>();

の二つの対処方法がいいと思われます。
エラーの原因が型の問題だった時の対処法になりますけどね・・・。
またエラーが出たら何か考えます・・・・・(+_+)

その後、同様のエラーが出ました。結局原因が解らずじまいです。
原因がわからないとモヤモヤしますね・・・・・(__)

--エラーが発生していた原因(2017年04月20日追記)--

だいぶUnityを使ってきたのでこの当時に発生していたエラーの原因がわかったような気がします。

原因は型の指定がされていないとかではなかったようです。

スクリプトを取りつけたゲームオブジェクトがあり、そのスクリプトのStart関数内でコンポーネントの取得をしているとします。

しかし、UIなど最初から表示したくないゲームオブジェクトはインスペクタでチェックを外して配置している事もあります。

そんな時に外部のスクリプトからそのUIをアクティブにし、そのスクリプトの関数内でStart関数内で取得するはずのコンポーネントにアクセスしようとするとエラーになっていたのかもしれません。

試しにMainCameraにTest2というスクリプトを作り取りつけます。

マウスの左ボタンを押したらインスペクタで設定したゲームオブジェクトをアクティブにし、そのゲームオブジェクトに設定されているGetComponentTestメソッドを呼びます。

Directional LightゲームオブジェクトにTestスクリプトを作り取りつけます。

Startメソッド内でLightコンポーネントの取得をします。

GetComponentTestメソッドでは取得したコンポーネントの名前を出力しています。

これでサンプルが出来ました。

まずはDirectional Lightをアクティブの状態でUnityを実行しマウスの左ボタンを押します。

DirectinalLightをアクティブにして実行

最初にAwake、次にStartが実行された後に画面内でマウスの左ボタンを押すとライトの名前が表示されます。

次にDirectional Lightを非アクティブにしてUnityを実行します。

DirectionalLightを非アクティブにして実行

Directional Lightが非アクティブの為、最初は何も表示されません。

その後、画面内でマウスの左ボタンを押すとメソッドが実行されますが、Startメソッドの前にGetComponentTestメソッドが呼ばれてしまいlightフィールドにはNullが設定されています。

このようにアクティブにした直後にそのスクリプトのメソッド内でStartメソッドで取得するはずのコンポーネントを使おうと思うと取得出来ていない場合があります。

その為、最初に非アクティブにしたゲームオブジェクトのスクリプトではその中で使うコンポーネントを実行するメソッド内で再度取得するようにした方がいいかもしれません。

-- 追記終了 (2017年04月20日)--

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます