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

【Unity】Prefab

はじめに

この記事はカヤックUnityアドベントカレンダー2016の5日目の記事になります。
今日の内容はPrefabについてです。基本的な使い方などを中心に説明いたします。

ソーシャルゲーム事業部所属、Unityエンジニアの荒井と申します。
現在は社内で運用と開発が行われているソーシャルゲームアプリぼくらの甲子園!ポケットに携わっています。

Prefabとは

PrefabはGameObjectをAsset化して再利用可能にしたものです。 構成が同じなGameObjectを複数個配置する場合に利用されます。PrefabはProjectビューに保存されます。

Prefabの使い方

あるGameObjectをPrefabにするには、そのオブジェクトをHierarchyビューからProjectビューにドラック&ドロップすることで行えます。

f:id:drknss_kyc:20161205211219g:plain

PrefabはProjectビューに保存しておき、シーンに配置することで 使用することができます。シーンに配置されたものはPrefabのインスタンスと呼ばれ、Prefabのインスタンスを作成することをPrefabのインスタンス化とよく呼ばれます。

Prefabで重要な性質としてPrefabの内容を変更するとインスタンスも同じように変更が反映され、逆にインスタンスの変更をPrefabに反映できることがあげられます。下記はその一例を示しています。

f:id:drknss_kyc:20161205211158g:plain

まず、GameObjectをインスタンス化しています。その後にPrefabにBox Colliderをアタッチしています。 すると、インスタンスにもBox Colliderがアタッチされていることがわかります。 次にPrefab側でBox ColliderのCenterのXの値を200に変更すると、インスタンスのBox ColliderのXの値も200に変更されていることが確認できます。 また、インスタンスでColliderのYの値を300にしてInspectorビュー上部にある【Apply】を選択することで、Prefab側にその値が反映されていることが確認できます。

スクリプトからPrefabをインスタンス化するには

処理を実行中にPrefabのインスタンス化を行うには、Instantiate()関数を使います。
引数にはPrefabのオブジェクトを指定します。 下記の例はMキーが押される度にPrefabのインスタンスを生成するものです。

[SerializeField]
private GameObject _prefab;

// Update is called once per frame
void Update()
{
  if (Input.GetKeyDown(KeyCode.M))
  {
    Instantiate(_prefab);
  }
}

簡単にインスタンスを作成することができることがわかります。 しかし、Prefabのインスタンスを作成するにもある程度コストがかかり、大量に作成するとパフォーマンスが落ちる場合があります。 そこで、コストが大きい場合には作成する数を抑えて、インスタンスを都度作成するのではなく、使いまわすなどの方法を検討する場合もあります。また、Destory()関数を使用することで、作成したインスタンスを削除することができます。

Prefabとインスタンスの関連付けを切るには

Prefabとそのインスタンスには、変更が反映される特徴があることを説明しました。 しかし、この関連付け切りたい場合が出てきます。このような時は別途作成する必要はなく、関連付けを切る機能が備わっています。 関連付けを切るインスタンスを選択し、【GameObjectメニュー】にある、【Break Prefab Instance】を選択することで関連付けを切ることができます。

f:id:drknss_kyc:20161205211119g:plain

また、変更により関連付けが切れてしまう場合があります。インタンスにおいてPrefabにも存在しているGameObjectを削除すると、下記のウィンドウが出てきます。 【Continue】を選択すると【Break Prefab Instance】を選択したときのように、関連付けが切られます。

f:id:drknss_kyc:20161202234135p:plain

インスタンスでのオーバーライド時の挙動に注意!

便利なPrefabですが使用する際に注意しなければならない性質があります。そのひとつがオーバーライド時の挙動についてです。まずはその挙動についてご覧ください。

f:id:drknss_kyc:20161205211140g:plain

上記の例では、はじめにPrefabの方でBox Colliderの値を変更し、Prefabのほうで値が反映されていることを確認しています。 次にインスタンスの方でBox Colliderの値を変更していますが、【Apply】を選択していないのでPrefabには変更は反映されています。 ここで、Prefabのほうで同じ箇所の変更を行った場合どうなるのでしょうか。 Prefabの値が反映されるかと思われますが、反映されないことが上記の例でわかります。

上記のことから、インスタンス側で変更がオーバーライド(上書き)された場合、Prefabでのその値を変更してもその箇所は反映されなくなる性質があります。 そのため、インスタンス側でのオーバーライドを多用すると、Prefabの利点である反映が活かせずに管理も大変になるため、あまり多用しない方がいいと思います。 また、オーバーライドされたプロパティ(上記の例ではCenter)は太文字で表記されるようになり、オーバーライドされている箇所を見分けることができます。

Prefabの中のPrefabを配置する場合は注意!

Prefabの中に別のPrefabを配置し、【Apply】を実行したときの挙動には注意が必要です。その挙動についてご覧ください。

f:id:drknss_kyc:20161205211055g:plain

上記では、まずPrefabAの子にPrefabBをインスタンス化しています。この状態ではPrefabAとPrefabBをPrefab側で変更すると、インスタンスにも変更が反映されていることが確認できます。次に【Apply】を実行しています。これによりPrefabAの子にPrefabBが配置された変更がPrefabに反映されます。ここで先程の同様にPrefabAとPrefabBをPrefab側で変更してみます。値を確認すると、インスタンスのPrefabAには変更が反映されていますが、PrefabAの中にあるPrefabBには反映されているないことがわかります。

このように、Prefabの中にPrefabを配置して【Apply】を実行すると、Prefabの中に配置されたPrefabのインスタンスは元のPrefabとの関連付けが切られてしまいます。 このことを忘れてしまうと【Apply】されても反映されていないことに躓いてしまうので、忘れないでおいた方がいいと思います。 また、このように新しい構成となる場合は別に新しいPrefabを作成することなどを行った方がいい場合もあります。

おわりに

この記事では、Prefabについて作成方法から扱う際の注意点を説明しました。

明日はScriptableObjectについての記事になります。
担当はp_chinになります。 お楽しみに。