LoginSignup
18
19

More than 5 years have passed since last update.

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

Posted at

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 に注意しましょう

18
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
19