読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】ゲームの起動後 Awakeより前にメソッドを実行する

Unity C#

この記事は、アプリ起動時に指定のメソッドを呼ぶ「RuntimeInitializeOnLoadMethod」に可能性を感じたが錯覚だった気が で絶望した戦士たちが、よく見たらそんなこと無かったと気づくストーリーである。

要するに書き直し。

RuntimeInitializeOnLoadMethod

RuntimeInitializeOnLoadMethodはAttribute(属性)である。彼は、ゲーム開始時に設定したメソッドを呼び出す為に存在するのだ。

例えば下のように記述した場合、ゲーム開始時にGameObjectをシーンに一つ作成してくれます。

gist.github.com

f:id:tsubaki_t1:20160728202300j:plain

さらにRuntimeInitializeOnLoadMethodの強力な所は、シーンにオブジェクトを配置しなくともメソッドが呼び出される事です。そのため、ゲーム起動時に必ず呼び出す必要のある処理は、このメソッドに定義しておくことでコンポーネントを設定せずとも呼び出すことが可能という訳です。

さらに、RuntimeInitializeOnLoadMethodはGameObjectを生成してみせたように、UnityのAPIを呼び出すことが可能です。スタティックコンストラクタやコンストラクタだとこの辺りは呼び出せないので、その辺りも嬉しい所です。

Awakeの前にRuntimeInitializeOnLoadMethodを呼び出す

RuntimeInitializeOnLoadMethodはそのままで呼ぶと、OnEnableの後に呼ばれます。つまりRuntimeInitializeOnLoadMethodが設定した何らかのパラメータにアクセス出来るようになるのは、少なくともAwakeでは無理でOnEnableまで待つ必要がある訳でした。

しかし何時の間にか付いたのか最初からついていたのか、実は引数にRuntimeInitializeLoadType.BeforeSceneLoadを設定することで、Awakeより前に呼び出す事が可能だったみたいです。

gist.github.com

f:id:tsubaki_t1:20160728204015j:plain

RuntimeInitializeLoadType.BeforeSceneLoadを設定したメソッドはAwakeより先に、1回のみ呼びだされます。

RuntimeInitializeLoadType.AfterSceneLoadを設定したメソッドは、OnEnableの後に呼び出されます。

例:ゲームに必要な処理を生成する

一つの例です。

例えばゲームの進行において必ず必要となるオブジェクト(重要OBJ)があったとします。これを他のコンポーネントが必要とする前に設定する方法は、概ね3つです。

  • 重要OBJをシーンに事前に配置しておく
  • コンポーネントが参照する際に重要OBJが無ければ生成する
  • 重要OBJがコンポーネント群を生成する、あるいはシーンをロードする

で、RuntimeInitializeOnLoadMethodをうまく活用すると4つ目

  • 重要OBJをゲーム開始時に生成する

が出来る訳です。
例えば下のような感じでオブジェクトを生成すれば、全Awakeが安定してオブジェクトを取得出来ます。(二周目は知りません)

gist.github.com

f:id:tsubaki_t1:20160728210943j:plain

ただし、コレをsingletonに使うのは少々問題かもしれません。というのも、下のような問題があるからです。

呼ばれなくても呼ばれるRuntimeInitializeOnLoadMethod

色々と便利そうではあるRuntimeInitializeOnLoadMethodですが、一つ使用するに辺り確実に注意すべき点があります。それは、定義したら場合呼びたくない状況でも呼ばれるという事です。

例えば下のようにログを非表示にするメソッドをRuntimeInitializeOnLoadMethodで定義すると、ログは表示されなくなります。

gist.github.comこの処理はプロジェクトの中にコードを置くだけで発動してしまうので、非常に追いにくい問題となります。

特に、ログといった分かりやすいパラメータなら兎も角、staticなフィールドをnullで埋めるみたいな処理を書かれ、即座にnull reference exceptionが発生しないような状況の場合、これは非常に追いにくい問題になりそうです。

呼出順の設定は出来ない

もう一つの問題は、RuntimeInitializeOnLoadMethod自体の呼出順を設定することが出来ない事です。

コンポーネントのコールバックのいくつかの呼出順はExecute Orderやオブジェクト内のコンポーネント順で設定出来ますが、RuntimeInitializeOnLoadMethodは制御出来ません。(まぁ、多分名前順かクラスID順ですが)

なので、相互的に関係するのであれば、複数定義するのではなく、単一の機能に処理を詰め込んだ方が良さそうです。

便利な機能だが使う際には注意を

Awakeより前に呼べると分かったので、割と可能性を感じるレベルになりましたが、やはり問答無用で呼ばれてしまうのが不安な所です。

記述した処理はシンボルで囲ってON/OFFを楽に設定できるようにした方が良いかもしれません。

kan-kikuchi.hatenablog.co

関連

tsubakit1.hateblo.jp