新しい機能を作りたいときによくMonoBehaviorを継承して頻繁に作りますが
初期化方法として
- Awake
- Start
の2つがあると思いますが今回はこれらの違いを見ていきたいと思います。
コンテンツ
AwakeとStartの違い
初期化に使うAwakeとStartの違いは大まかに言えば処理が走るタイミングが異なる点です。
公式ドキュメントによるとこの様になっています。
普通に使用する場合このような順序となっていますね。
一見するとAwakeのほうが最速で処理されるので便利そうに見えまね
Awakeの特徴と欠点
Awake特徴として
- 最速で実行される
- Awakeを実装したスクリプトを設定したオブジェクトが非表示の場合でも実行される
- Awake時に他のオブジェクトの処理がまだされていないので、他のオブジェクトの取得する 最高のタイミングとなる
↓Hobby
Awakeのときに使用回数をセットし遊ぶたびに使用回数が減っていく
public class Hobby : Monobehavior
{
// 使用可能回数
private int m_usage = 0;
void Awake()
{
m_usage = 10;
}
public void GetUsage()
{
return m_usage;
}
public void Play()
{
if(m_usage > 0)
{
m_usage--;
return 1;
}
else
{
return 0;
}
}
}
主なHobbyの使用者としては以下がいるとします。
↓ ジャイアニスト
[RequireComponent(typeof(Hobby))]
public class Jaianist : Monobehavior
{
private Hobby m_myHobby = null;
void Awake()
{
m_myHobby = GetComponent<Hobby>();
}
void Update()
{
if(m_myHobby != null)
{
m_myHobby.Play();
if(m_myHobby.GetUsage() == 0)
{
Destroy(m_myHobby.gameobject);
m_myHobby = null;
}
}
}
}
「ジャイアニスト」スクリプトがWakeで最速にHobbyを取得しUpdateで遊んでますね…
使用回数がになったら捨ててますしね。
[RequireComponent(typeof(Hobby))]
これ使用すること Jaianist を使用するときは同じオブジェクトに Hobby を設定しなければないらないので。
GetComponent<Hobby>() の保証がかなり担保されるので。
GetComponent を使う際は使用することをおすすめします。
これだけでもかなり便利ですが欠点として
- Awakeが複数ある場合どの順番で実行されるか不明
- Awakeを実装したスクリプトを設定したオブジェクトが非表示の場合でも実行される
- Awake時に初期化が終わっていないほかオブジェクトの値を変更すると最悪ゲームが止まる
例えばこんな感じですね
引き続きジャイアにスト
public class Jaianist : Monobehavior
{
private Hobby m_myHobby = null;
void Awake()
{
m_myHobby = GameObject.Find<RichBoy>("RichBoy").Rental();
m_myHobby.Play();
if(m_myHobby.GetUsage() == 0)
{
Destroy(m_myHobby.gameobject);
m_myHobby = null;
}
}
}
Hobbyを持っているRichBoy
[RequireComponent(typeof(Hobby))]
public class RichBoy : Monobehavior
{
private int m_happyCounter = 0;
public Hobby Rental()
{
return transform.Find("Hobby").GetComponent<Hobby>();
}
void Update()
{
Rental.Play();
}
}
今度も Hobbyを取得しUpdateで遊んでますね…
しかし挙動としては
- 「ジャイアニスト」スクリプトがWakeで最速に「RichBoy」からHobbyを取得
- 「ジャイアニスト」スクリプトがAwake時にHobbyを遊び使用できなくなったら破棄
普通はこんなスクリプトは絶対に書いてはいけませんが
この場合 「ジャイアニスト」スクリプト が一度遊んでいますが 「ジャイアニスト」がAwake時に遊んだときの「Hobby」は初期化できているのでしょうか?
答えは不明です。
ですのでできていない場合は 「ジャイアニスト」がAwakeのタイミングで破棄しているため。
「RichBoy」はHobbyを遊ぶことができなくなりますね。
Startの特徴と欠点
Startの特徴としては
- Updateの直前で処理される
- Awakeを実装したスクリプトを設定したオブジェクトが非表示の場合では実行されない
Awakeとは違いオブジェクトが非表示の場合はそのオブジェクトがSetActiveなどで表示状態にならなければ実行されません。
ですので初期化の意味合いとしては若干違う雰囲気がしますね。
更に以下のようなスクリプトの場合
public class Product : MonoBehavior
{
void Start()
{
Debug.Log("Start");
}
public void Init()
{
Debug.Log("Init");
}
}
public class Creater : MonoBehavior
{
private Product m_baseProduct = null;
void Update()
{
var product = Instatiate(m_baseProduct);
product.Init();
}
}
見ての通り「Creater」が毎フレーム「Product」生成してますが
この場合
StartよりもInitが先に処理されます。
そのため、Startは必ず呼ばれるものでもなく最速ではない点に注意しましょう。
まとめ
MonoBehaviorを継承したオブジェクトの方法のStartとAwakeの差を見てみましたが、
同じ最初に呼ばれるものでも大きく挙動が異なることわかります。
ですので初期化の際は以下に注意しましょう。
- AwakeではGetComponentなどオブジェクトの参照取得に使う
- Awakeでは自身の値の初期化に留める
- Awakeでは外部オブジェクトの値の参照や変更は絶対に避ける
- Startは呼ばれない可能性がある
また、どうしても順番を担保して安全に初期化をしたい場合は、このように
自分でInitを呼び手動初期化するのも全然ありかと思います。
public class Creater : MonoBehavior
{
private Product m_baseProduct = null;
void Update()
{
var product = Instatiate(m_baseProduct);
product.Init();
}
}
public class Product : MonoBehavior
{
public void Init()
{
Debug.Log("Init");
}
}
このように、それぞれの適材適所があるのでうまく使い分けていきましょう。
Awake
↓
Start