Posted at

Prefab のアタッチメントを効率化させる

More than 5 years have passed since last update.


Instantiate (prefab, transform.position, transform.rotation) as GameObject; って書くのめんどくさい

あるゲームオブジェクトの Prefab をシーン内にアタッチする時に、こういったコードを書くかと思います


Example.cs

public GameObject effectPrefab;

public float effectLifeTime = 3.0f;

void Update () {
// 左クリック
if (Input.GetMouseButtonDown (0)) {
// エフェクトを Prefab からゲームシーンへアタッチ
var effect = Instansiate (
effectPrefab,
transform.position,
transform.rotation
) as GameObject;

// エフェクトの Transform ノードを自分の子要素にする
effect.transform.parent = transform;

// エフェクトをコルーチンで消す
StartCoroutine ("AutoEraseEffect", effect);
}
}

IEnumrator AutoEraseEffect (GameObject effect) {
yield return new WaitForSeconds (effectLifeTime);
Destroy (effect);
}


↑ の例だけだったら別に良いのだけれど、


  • アタッチしたオブジェクトの localPosition を変更したい…

  • アタッチしたオブジェクトの rotation は Prefab で設定したものから変更したくない…

  • アタッチしたオブジェクトにあるはずの Component を取り出して使いたい…

等々、「Instantiate (...) as GameObject って毎回長いの書きたくないけどバリエーションが沢山あってダルい…」と思うようなユースケースが多々出てくるかと思います。


PrefabController

自分の場合 ユーティリティクラスとしてこういうのを最初に書いておきます


PrefabController.cs

using UnityEngine;

using System;

public enum CloneMode {
/// <summary>
/// Clone rotation follow parent.
/// </summary>
FollowParent,
/// <summary>
/// Clone rotation keep prefab's rotation.
/// </summary>
KeepRotate
}

public class PrefabController : MonoBehaviour {

// 引数の mode (デフォルトは parent の rotation に従う)に合わせて Prefab のアタッチメント
// Transform のノードはヒエラルキーのトップ
public static GameObject Clone (GameObject prefab, Transform parent, CloneMode mode = CloneMode.FollowParent) {
Transform rotationOrigin;
switch (mode) {
case CloneMode.KeepRotate:
rotationOrigin = prefab.transform;
break;
default: // Follow Parent Transform
rotationOrigin = parent;
break;
}
return Instantiate (prefab, parent.position, rotationOrigin.rotation) as GameObject;
}

// 引数の parent の子要素としてアタッチ
public static GameObject CloneAsChildren (GameObject prefab, Transform parent, CloneMode mode = CloneMode.FollowParent) {
var instance = Clone (prefab, parent, mode);
instance.transform.parent = parent;
return instance;
}

// 引数の sibling の兄弟要素としてアタッチ
public static GameObject CloneAsSibling (GameObject prefab, Transform sibling, CloneMode mode = CloneMode.FollowParent) {
var instance = Clone (prefab, sibling, mode);
instance.transform.parent = sibling.parent;
return instance;
}
}


つくりたいゲームの仕様次第ですが、たとえば 「シーンロードの際に非同期通信を行い、ゲームのレベルデザイン設定ファイルを受け取ってそこからゲームオブジェクトを生成させる」などのユースケースで効果を発揮します。


おまけ 「エフェクト(パーティクルシステム)」には自分で自分を Destroy する仕組みをもたせた方が管理が楽

有名なアセットの Detonator や Explosion V1 & V2 などはそういうパターンで設計されていますが、アタッチされてから、自分で一定時間演出して、自分で Destroy (gameObject); を呼ぶ仕組みにしておくと便利です。

ただ、外側から GameObject として持っているメンバーは他のオブジェクトからも参照できるので、 NullReferenceException に注意しましょう