テラシュールブログ

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

【Unity】動的に増やすGameObjectは別にPrefabでなくとも良い

f:id:tsubaki_t1:20180228232440j:plain

オブジェクトの参照をセットする際の一番Unity的なアプローチは、Inspectorに露出してセットする事です。

ただ動的なオブジェクトとなるとInspectorは使いにくい所があります。この辺り、実際に正しい所もあるのですが、そうじゃない所もあります。

動的に増えるGameObjectですが、これが参照出来ない理由の大抵は「Prefabから元となるオブジェクトを持ってきている」為です。

 

 

PrefabはSceneのオブジェクトを参照できない

大抵のチュートリアルやサンプルでは、動的に増減する項目はResources等に配置したPrefabを取得し、Instantiateしています。

この方法はPrefabはScene内のオブジェクトにアクセス出来ないので、シーン内の参照関係を生成するコード等が担保する事になります。

f:id:tsubaki_t1:20180228234008p:plain

例えば敵キャラクターを生成…みたいな場合、敵キャラクターはプレイヤーの位置やゲーム進行等の情報を得る必要があります。

ただPrefab等から生成する場合にはシーン内のオブジェクトにアクセス出来ないので、生成時にFindしてGameControllerを取得したり、Singletonパターンを使ったり、生成するコードが責任持って敵キャラクターに情報を流し込んだりします。

f:id:tsubaki_t1:20180301004043j:plain

 

Scene内のオブジェクトもInstantiate出来る

ここでの勘違いは「InstantiateするオブジェクトはPrefabでなければならない」という点です。Instantiateは別に同じシーン内のオブジェクトに対しても実行が可能です。

同一シーン上で行うのであればInspectorで設定した参照をそのまま使えます。

f:id:tsubaki_t1:20180228235827p:plain

f:id:tsubaki_t1:20180228235112g:plain

 

ボタンのイベントや参照先は保持される

例えばUIのButtonの場合、「ボタンを押したら○○を呼ぶ」の挙動を維持したままボタンの数を増やせます。
どのボタンが押されたか…については、ボタンの引数にTransformを渡しておけば、Indexが取れるのでソレが使えます。

 

イベントや参照部分はScene上で設定できるので、コードはオブジェクトを生成する所だけ見れば良いです。

f:id:tsubaki_t1:20180301001225g:plain

ここで面白いのが、生成時に「自分と同じオブジェクトもしくは子オブジェクト」への参照は、生成された場合も「自分と同じオブジェクトもしくは子オブジェクト」へ参照する点です。

なので上のGifアニメでは「自分のTransform」を参照すると、生成時に元となるTransformではなく「自分のTransform」を参照しています。

 

プレイヤーに向かって移動する敵を生成する例

例えば下のような、特定のオブジェクト(プレイヤー)に向かって移動する…といった物を考えてみます。当然プレイヤーに向かって移動するためにはプレイヤーの存在を知る必要があります。

f:id:tsubaki_t1:20180301125344g:plain

 

初期段階

最初の段階のコードはこんな感じです。

gist.github.com

ケース1:Prefabを使用した場合

これを動的に生成しようと思います。Prefabを使用した場合、生成側が責任を持ってプレイヤーを登録するか、移動するコードが生成してやる必要があります。

ただし敵の種類は一種類とは限らないため、インターフェースを利用して汎用的に使えるようにする必要があります。

逆に追跡側がプレイヤーを取得する場合、何度もFind等を使うのは望ましくない為、Staticなりに登録する必要が出てきます。

これはPrefabを使用する場合です。

gist.github.com

ケース2:シーン内のオブジェクトを使用した場合

シーン内のオブジェクトを生成する場合、下のようなコードで上と同じように動作します。参照先の情報は既に持っているので、それを使えばOKな為です。

gist.github.com

操作が必要になりますが、Prefabのケースと較べてかなりシンプルになっています。

生成元をテンプレートとして使いたいなら非アクティブなオブジェクト下に置く

GameObjectの生成してないのにAwakeが呼ばれて困る…みたいな場合には、親オブジェクトを非アクティブにしておくと良いです。

これでPrefabと同じような動きになります。

f:id:tsubaki_t1:20180301001627g:plain

 

このアプローチの問題点は?

このアプローチで問題は、沢山のSceneで同じオブジェクトが沢山存在する場合、デシリアライズのコストが嵩むという点です。

ただ実際の開発ではシーン毎にユニークなオブジェクト…が割と普通で、共通部分があったとしてもパラメータをScriptableObjectに逃しておけば特に問題ないので、実はそこまでじゃないという話もあります(PrefabのパラメータもScriptableObjectに逃しておかないとInstantiate時のコストが結構あります)

 

それと、Inspectorの参照関係が壊れる的な話が時々上がりますが、自分は今のところ同一シーン内での参照関係が壊れた所を見た記憶が無いので、まぁ大丈夫かなと
(PrefabやAssetへの参照関係は、GUIDを変更しちゃう等で時々見ます)

 

どんな時に使えるの?

特定のシーンでしか使わないような物(UIや敵等)を複製したい場合には、シーン内の物を編集したほうが楽です。

逆に、複数のシーンで汎用的に使う物に関してはPrefabの方が便利かもしれません。

 

なおシーンはファイルが一つですのでマージの問題が出ますので、複数人で開発する場合はスマートマージを入れておいたほうが良いです。

tsubakit1.hateblo.jp

 

その他

参照関係構築の為の記事を書いてたら補足項目なのに1000文字とか行ってたので分割…

 

Unity 4の時はシーンにPrefabの情報が直接書き込まれていたので、シーン内のPrefabにアクセスするのは少し躊躇われましたが、Unity 5からはシーンにあるのはPrefabへの参照と変化した値のみなので、特に気にせずアクセスしてもOK