U
18 / 22 · 9 分
FAQ

Instantiate:Prefab の生成と再利用

短い答え

コードでオブジェクトを生成するには?

対象を Prefab にし、public フィールドに参照を持ち、Instantiate(prefab, position, rotation) を呼びます。Project パネルから Prefab をドラッグすること——Hierarchy のインスタンスを拾うのがスポナー bug の半分の原因です。

オブジェクト生成は Instantiate という関数一つと、うまく動くかを決める習慣一つの話です——生成するものは Project パネルにある prefab でなければならず、シーン内に置かれたコピーであってはなりません。

まず prefab を作る

  1. 1

    シーンの中で、生成したいものそのものになるまでオブジェクトを組み立てます——mesh、material、collider、スクリプト、全部揃えます。

  2. 2

    Hierarchy から Assets/Prefabs へドラッグすると名前が青になります——これで prefab、ディスク上に存在するものになりました。

  3. 3

    シーンに残った方は削除します。prefab こそが正本で、シーンの方は起動時にそこに敵が余計に一匹立っているだけのものです。

Instantiate

Spawner.cs — drag the PREFAB into the slot, not the scene object
using UnityEngine;

public class Spawner : MonoBehaviour
{
    public GameObject prefab;
    public Transform parent;        // optional, keeps the Hierarchy tidy
    public float interval = 1.5f;

    float next;

    void Update()
    {
        if (Time.time < next) return;
        next = Time.time + interval;

        Vector3 where = transform.position + Random.insideUnitSphere * 3f;
        where.y = transform.position.y;

        GameObject spawned = Instantiate(prefab, where, Quaternion.identity, parent);

        // Instantiate returns the new copy. Configure that, never the prefab —
        // writing to prefab.something edits the asset on disk.
        spawned.name = "Enemy " + Time.frameCount;
    }
}

人がよく忘れる 3 つ

  • Instantiate はスケールも子オブジェクトも含め、すべてをコピーします。生成物のサイズが間違っていたら、prefab のサイズが間違っているのです。

  • parent を渡せば Hierarchy は読みやすくなりますが、オブジェクトは親のスケールを継承します。スケール 1 の親に付けるか、親なしで生成してから SetParent(parent, true) を呼んでください。

  • Quaternion.identity は回転なしを意味します。代わりに transform.rotation を渡すと、生成物は毎回 spawner と同じ向きを向きます——本当にやりたかったのはおそらくこちらです。

Destroy と、spawner がフレームレートを落とす理由

Instantiate は毎回メモリを確保し、Destroy は毎回ガベージコレクタ用のゴミを残します。毎フレーム動く spawner は、スマホなら 1 分とかからずカクつき始めます。弾やエフェクト——数百回登場するもの——は、作り直すのではなく使い回してください。

The smallest pool that is worth having
using System.Collections.Generic;
using UnityEngine;

public class SimplePool : MonoBehaviour
{
    public GameObject prefab;
    readonly Queue<GameObject> idle = new();

    public GameObject Get(Vector3 at)
    {
        GameObject go = idle.Count > 0 ? idle.Dequeue() : Instantiate(prefab);
        go.transform.position = at;
        go.SetActive(true);
        return go;
    }

    // Call this instead of Destroy.
    public void Release(GameObject go)
    {
        go.SetActive(false);
        idle.Enqueue(go);
    }
}

これを作るレッスン

コードでブロックの並びを作る

この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — 基礎 のレッスン 08。