プレハブとは?

Unityでゲームを作っていると、同じ構成のGameObjectを何度も使い回したい場面が出てきます。

例えば敵キャラ、アイテム、UIのボタンなど、同じものを複数配置するケースは日常的にあるはずです。

プレハブは、GameObjectの構成をアセットとして保存し、テンプレートのように再利用できる仕組みです。

プレハブを作ると、Projectウィンドウにアセットとして保存されます。

このアセットが「オリジナル」で、シーン上に配置したものが「インスタンス」です。

インスタンスはオリジナルと紐付いており、オリジナルを変更するとすべてのインスタンスに反映されます。

逆にインスタンス側で個別に変更を加えることもでき、その変更はOverrideとして管理されます。

この仕組みによって、共通部分はオリジナルで一括管理しつつ、個別の調整もできるようになっています。

プレハブのメリット・デメリット

メリット

プレハブを使う最大のメリットは、一括変更ができることです。

オリジナルのプレハブを修正すれば、シーン上のすべてのインスタンスに変更が反映されます。

例えば敵キャラのColliderのサイズを調整したい場合、プレハブを1つ修正するだけで全体に適用されます。

また、プレハブはアセットとしてプロジェクト内に保存されるため、複数のシーンで同じプレハブを使い回せます。

チーム開発でも、プレハブ単位でバージョン管理できるため、作業の分担がしやすくなります。

デメリット

便利な反面、プレハブのネストが深くなりすぎると管理が難しくなります。

特にOverrideが多くなると、どのインスタンスでどんな変更をしたのか把握しにくくなります。

意図せずApplyしてしまい、全インスタンスに想定外の変更が伝播するといった事故も起こり得ます。

プレハブ化の判断基準

すべてのGameObjectをプレハブにする必要はありません。

以下に当てはまるなら、プレハブ化を検討してみてください。

  • シーン内やシーンをまたいで、同じ構成のオブジェクトを使い回す
  • Instantiateで動的に生成する(この場合はプレハブが必須)
  • チームで共通のUIパーツや敵キャラを統一して管理したい

逆に、シーンに1つしか置かないオブジェクトや、構成がころころ変わるものは無理にプレハブ化しなくて大丈夫です。

プレハブを作る

Hierarchyからプレハブを作成する

作り方はシンプルで、HierarchyウィンドウにあるGameObjectをProjectウィンドウへドラッグ&ドロップするだけ。

プレハブが作成されると、Hierarchy上のGameObjectの名前が青色に変わります。

これがプレハブのインスタンスである目印です。

Prefab Modeでの編集

プレハブを編集するには、Prefab Modeを使います。

Projectウィンドウのプレハブアセットをダブルクリックするか、

Hierarchy上のインスタンスの横にある矢印アイコンをクリックするとPrefab Modeに入れます。

Prefab Modeではオリジナルを直接編集でき、変更はすべてのインスタンスに反映されます。

編集が終わったら、Hierarchyの左上にある「<」ボタンで元のシーンに戻れます。

Overrideの仕組み(Apply / Revert)

シーン上のインスタンスに対して個別に変更を加えると、その変更はOverrideとして記録されます。

Inspectorでは、Overrideされたプロパティが太字で表示されるので見分けがつきます。

Overrideに対しては、2つの操作ができます。

  • Apply … インスタンスの変更をオリジナルのプレハブに反映する
  • Revert … インスタンスの変更を取り消して、オリジナルの状態に戻す

InspectorのOverridesドロップダウンから、変更内容の一覧を確認し、個別または一括でApply/Revertできます。

ただしApplyには注意が必要です。意図しない変更まで全インスタンスに適用されてしまうことがあります。

Apply前に、変更内容の一覧をしっかり確認しておくのがおすすめです。

Nested Prefab

Nested Prefabとは?

Nested Prefabは、プレハブの中に別のプレハブを配置する機能です。

例えば「部屋」のプレハブの中に「椅子」「テーブル」「照明」といった家具のプレハブを配置するような使い方ができます。

家具のプレハブを修正すれば、それを使用しているすべての部屋プレハブにも変更が反映されます。

部品単位でプレハブを管理できるため、大規模なプロジェクトでの再利用性が高まります。

使いどころ

Nested Prefabは色々な場面で使えます。

  • ボタンやヘッダーなどのUIパーツをプレハブにして、画面プレハブに組み込む
  • 壁、床、ドアなどの部品をプレハブにして、部屋プレハブを組み立てる
  • 武器や防具をプレハブにして、キャラクタープレハブに持たせる

Override管理の注意点

Nested Prefabでは、親プレハブのOverrideと子プレハブのOverrideが混在します。

例えば、部屋プレハブ内の椅子プレハブの位置を変えた場合、それは部屋プレハブに対するOverrideとなります。

椅子プレハブ自体のマテリアルを変えたいのか、この部屋での椅子の配置だけを変えたいのか。ここを意識してApplyとRevertを使い分ける必要があります。

Overridesドロップダウンには、どのプレハブに対するOverrideなのかが表示されるので、Apply先の確認を忘れずに。

Prefab Variant

Prefab Variantとは?

Prefab Variantは、既存のプレハブ(ベースプレハブ)を元にして、一部だけ変更した派生プレハブを作る機能です。

オブジェクト指向でいうところの「継承」に近い概念です。

ベースプレハブを変更すると、Prefab Variantにも変更が反映されます。

ただし、Prefab Variant側でOverrideしている部分はそのまま維持されます。

Prefab Variantを作成するには、Projectウィンドウでプレハブを右クリックし、Create > Prefab Variantを選択します。

Nested Prefabとの違い・使い分け

Nested PrefabとPrefab Variantは混同しやすいですが、目的が異なります。

Nested PrefabPrefab Variant
概要プレハブの中に別のプレハブを配置ベースプレハブの派生を作成
関係性親子(包含)基底と派生(継承)
使いどころ部品を組み合わせて構成する同じ構造でバリエーションを作る

ざっくり言えば、Nested Prefabは「部品の組み合わせ」、Prefab Variantは「バリエーション展開」。目的が違います。

使いどころ

Prefab Variantは、構造は同じだけど見た目やパラメータだけ違う、というオブジェクトに向いています。

  • 基本構造は同じで、HPや攻撃力、見た目が異なる敵キャラ
  • 同じ武器タイプだけど、レアリティやダメージが違うもの
  • 同じレイアウトで色やフォントだけ変えたUI

例えば敵キャラの場合、共通のコンポーネント構成をベースプレハブで定義し、Prefab VariantでHP、速度、マテリアルなどを個別に設定できます。

// 敵キャラの基底クラス
public abstract class EnemyBase : MonoBehaviour
{
    [SerializeField] private int _hp;
    [SerializeField] private float _speed;

    public int Hp => _hp;
    public float Speed => _speed;

    // 共通の処理
    public virtual void TakeDamage(int damage)
    {
        _hp -= damage;
        if (_hp <= 0)
        {
            OnDeath();
        }
    }

    protected abstract void OnDeath();
}

ベースプレハブにこのスクリプトをアタッチし、Prefab VariantごとにInspectorで_hp_speedの値を変えるだけで、異なる強さの敵を作成できます。

Prefabを差し替える(Replace)

HierarchyでのReplace機能

Unity 2022.1以降では、Hierarchy上でGameObjectのプレハブを簡単に差し替えられます。

差し替えたいGameObjectを右クリックし、Prefab > Replace Prefabを選択します。

表示されるダイアログから差し替え先のプレハブを選択すると、Transformの値を保持したまま差し替えが行われます。

スクリプトからの差し替え

スクリプトから動的にプレハブを差し替えたい場合は、古いオブジェクトを削除して新しいプレハブをInstantiateする方法が基本です。

using UnityEngine;

public class PrefabReplacer : MonoBehaviour
{
    [SerializeField] private GameObject _replacementPrefab;

    /// <summary>
    /// 対象のGameObjectを別のプレハブに差し替える
    /// </summary>
    public GameObject Replace(GameObject target)
    {
        // 差し替え前の情報を保存
        var position = target.transform.position;
        var rotation = target.transform.rotation;
        var parent = target.transform.parent;

        // 古いオブジェクトを削除
        Destroy(target);

        // 新しいプレハブを同じ位置・回転で生成
        var newObject = Instantiate(_replacementPrefab, position, rotation, parent);

        return newObject;
    }
}

差し替え時にコンポーネントの値を保持するコツ

差し替え時に注意したいのは、元のオブジェクトが持っていたコンポーネントの値が失われる点です。

Transformの値は上記のように手動で引き継げますが、その他のコンポーネントの値も保持したい場合は、差し替え前に必要な情報を退避させる必要があります。

using UnityEngine;

public class PrefabReplacerWithData : MonoBehaviour
{
    [SerializeField] private GameObject _replacementPrefab;

    /// <summary>
    /// HPなどのデータを引き継いで差し替える
    /// </summary>
    public GameObject ReplaceWithData(GameObject target)
    {
        var position = target.transform.position;
        var rotation = target.transform.rotation;
        var parent = target.transform.parent;

        // 差し替え前のデータを退避
        var oldEnemy = target.GetComponent<EnemyBase>();
        var currentHp = oldEnemy != null ? oldEnemy.Hp : 0;

        Destroy(target);

        var newObject = Instantiate(_replacementPrefab, position, rotation, parent);

        // 差し替え後のオブジェクトにデータを引き継ぐ
        var newEnemy = newObject.GetComponent<EnemyBase>();
        if (newEnemy != null)
        {
            newEnemy.SetHp(currentHp);
        }

        return newObject;
    }
}

引き継ぎたいデータが多い場合は、ScriptableObjectやデータクラスにまとめておくと扱いやすくなります。

汎用的なコンポーネントコピーの仕組みを作ることもできますが、複雑になりがちです。

必要なデータだけ明示的に引き継ぐほうがトラブルは少ないでしょう。

まとめ

今回はプレハブの基本から、Nested Prefab、Prefab Variant、Overrideの管理まで一通り紹介しました。

プレハブは地味な機能に見えますが、プロジェクトが大きくなるほど効いてきます。

特にOverrideの扱いに慣れておくと、「一括で直したい」と「ここだけ変えたい」の両方に対応できるようになります。

使い分け早見表

やりたいこと使う機能
同じオブジェクトを何度も使い回したいプレハブ
プレハブの中に別のプレハブを組み込みたいNested Prefab
同じ構造でパラメータや見た目だけ変えたいPrefab Variant
インスタンスだけ個別に調整したいOverride(Apply / Revert)
シーン上のプレハブを別のものに入れ替えたいReplace Prefab

Nested PrefabとVariantの使い分けは最初ちょっと迷いますが、上の早見表を参考にしてみてください。