Version: 2022.1
言語: 日本語
変数とインスペクター
イベント関数の実行順序

ランタイムのプレハブのインスタンス化

プレハブ は、複雑なゲームオブジェクトや一群のゲームオブジェクトをランタイムにインスタンス化したい場合に便利です。コードを使用して 0 からゲームオブジェクトを作成するのと比べ、コードを使ったプレハブのインスタンス化は以下を含む多くの長所があります。

  • 1 行のコードでプレハブをインスタンス化できます。同等のゲームオブジェクトを 0 から作成するには、さらに多くのコードが必要です。

  • シーンビュー、Hierarchy、Inspector 上で早く簡単にプレハブを設定、テスト、修正できます。

  • コードを変更せずにインスタンス化するプレハブを変更できます。コードを変更することなく、シンプルなロケットをスーパーチャージロケットにすることができます 。

ノート: このページのすべてのサンプルを含む Unity プロジェクトは、こちらからダウンロードできます。

InstantiatingPrefabsExamples.zip

プレハブのインスタンス化の基本

ランタイムにプレハブをインスタンス化するには、コードにそのプレハブへの参照を加える必要があります。コードで public 変数を作成してプレハブ参照を加えることで、この参照を作成できます。コードのパブリック変数は、Inspector で割り当て可能なフィールドとして表示されます。使用したいプレハブを Inspector で割り当てることができます。

以下のスクリプトの例には、プレハブへの参照である 1 つのパブリック変数 “myPrefab” があります。Start() メソッドでそのプレハブのインスタンスを作成します。

using UnityEngine;
public class InstantiationExample : MonoBehaviour 
{
    // プレハブへの参照。Inspector のこのフィールドにプレハブをドラッグします。
    public GameObject myPrefab;

    //このスクリプトは、 ゲームが始まった時に簡単にプレハブをインスタンス化します。
    void Start()
    {
        // 位置 (0, 0, 0) でインスタンス化して回転しません。
        Instantiate(myPrefab, new Vector3(0, 0, 0), Quaternion.identity);
    }
}

このサンプルを使用するには、以下を行います。

  • プロジェクトに新しい C# スクリプトを作成し、“InstantiationExample” という名前を付けます。

  • 上記のスクリプト例をコピーして新しいスクリプトに貼り付け、保存します。

  • メニューの GameObject > Create Empty を使用して、空のゲームオブジェクトを作成します。

  • 新しい空のゲームオブジェクトにスクリプトをドラッグして、コンポーネントとしてゲームオブジェクトに追加します 。

  • 任意のプレハブを作成 し、Project ウィンドウ からスクリプトコンポーネントの My Prefab フィールドにドラッグします。

Project ウィンドウからスクリプトコンポーネントの My Prefab フィールドにプレハブをドラッグ
Project ウィンドウからスクリプトコンポーネントの My Prefab フィールドにプレハブをドラッグ

再生モードを開始すると、シーンの位置 (0, 0, 0) にプレハブがインスタンス化されているのが確認できます。

別のプレハブを Inspector の My Prefab フィールドにドラッグすると、スクリプトを変更せずにインスタンス化するプレハブを変更できます。

この最初の例は非常に簡単なので、プレハブを自身でシーンに配置するのと比べ、特に利点はないようです。ただし、コードを使用してプレハブをインスタンス化できることは、以下の例に示すように、ゲームやアプリケーションの実行中にゲームオブジェクトの複雑な設定を動的に作成する強力な機能を提供します。

よくあるシナリオ

実行時にプレハブをインスタンス化する利点を説明するために、プレハブが役立ついくつかの基本的な状況を説明します。

  • 異なる位置でプレハブを複数回複製することによる、1 つのプレハブからの構造の構築。例えば、グリッドや円形状など。

  • ランチャーからの砲弾プレハブの発射。砲弾プレハブは、メッシュリジッドボディコライダーオーディオソース動的ライト、トレイル パーティクルシステム をもつ子ゲームオブジェクトを含む複雑な設定です 。

  • 車両、建物、キャラクター。例えば、こなごなに壊れるロボット。このシナリオでは、スクリプト例によって、完全に動作するロボットのプレハブを削除し、壊れたロボットのプレハブに置き換えます。この破壊されたプレハブは、ロボットの個々の壊れた部分で構成されており、それぞれのリジッドボディとパーティクルシステムで設定されています。このテクニックを使用すると、1 行のコードでロボットを多くの部分に破壊でき、元のゲームオブジェクトが壊れたプレハブに置き換えられます。

以下のセクションでは、これらのシナリオの実装方法を説明します。

構造体の作成

コードを使用すると、特定の設定で多くのプレハブのコピーをほとんど瞬時に作成できます。このように構造をコードで生成することを手続き型生成といいます。以下の例では、ブロックインスタンスの壁を作成します。

この例を試すには、以下のスクリプトを作成し Wall という名前を付け、シーンの空のゲームオブジェクトに配置します。

using UnityEngine;
public class Wall : MonoBehaviour
{
   public GameObject block;
   public int width = 10;
   public int height = 4;
  
   void Start()
   {
       for (int y=0; y<height; ++y)
       {
           for (int x=0; x<width; ++x)
           {
               Instantiate(block, new Vector3(x,y,0), Quaternion.identity);
           }
       }       
   }
}

これを完了すると、Block 変数が Inspector に表示され、フィールドに None と表示されます。“None” は、この変数にまだプレハブが割り当てられていないことを意味します。

プレハブがまだ割り当てられていない Block 変数
プレハブがまだ割り当てられていない Block 変数

上記のスクリプト例は、プレハブを Block 変数に割り当てるまで機能しません。簡単なブロックプレハブを作成するには、以下の手順を行います。

  1. GameObject > 3D Object > Cube を選択します。

  2. Hierarchy ウィンドウのキューブを Project ウィンドウの Assets フォルダーへドラッグします。 このようにして、プレハブアセットを作成します。

  3. プレハブの名前を “Block” に変更します。

  4. Block プレハブがアセットになったので、Hierarchy からキューブを安全に削除できます。

Block プレハブを作成したので、それを Block 変数に割り当てます。元のゲームオブジェクト (“Wall” スクリプトがアタッチされているゲームオブジェクト) を選択します。次に、“Block” プレハブを Project ウィンドウ から “Block” 変数スロット (“None”と表示) にドラッグします。

Block プレハブが割り当てられた Block 変数
Block プレハブが割り当てられた Block 変数

この設定が完了したら、Play をクリックすると、プレハブを使用した壁が作成されます。

上の例で生成された、4 x 10 で作られた壁。
上の例で生成された、4 x 10 で作られた壁。

これは、Unity で何度も使用できる柔軟なワークフローパターンです。このスクリプトではプレハブを使用しているため、プレハブを簡単に置き換えたり編集したりして、スクリプトを変えることなく壁のレンガのプロパティを変更することができます。また、異なるプレハブが割り当てられたシーン内の他のゲームオブジェクトに Wall スクリプトを使用して、さまざまなタイプのプレハブで作られたさまざまな壁を作成することもできます。

コードを使用すると、作成するゲームやアプリケーションに合わせて、ゲームオブジェクトをグリッド、円状、ランダムに散らばったもの、その他の考えうる配置に置くことがます。インスタンスを円状に配置する別の例を次に示します。

using UnityEngine;
public class CircleFormation : MonoBehaviour
{
   // 円状にプレハブをインスタンス化
   public GameObject prefab;
   public int numberOfObjects = 20;
   public float radius = 5f;
   void Start()
   {
       for (int i = 0; i < numberOfObjects; i++)
       {
           float angle = i * Mathf.PI * 2 / numberOfObjects;
           float x = Mathf.Cos(angle) * radius;
           float z = Mathf.Sin(angle) * radius;
           Vector3 pos = transform.position + new Vector3(x, 0, z);
           float angleDegrees = -angle*Mathf.Rad2Deg;
           Quaternion rot = Quaternion.Euler(0, angleDegrees, 0);
           Instantiate(prefab, pos, rot);
       }
   }
}
上記の例で生成されたブロックの円状配置
上記の例で生成されたブロックの円状配置

砲弾と爆発のインスタンス化

このシナリオでは、以下を行います

  1. プレイヤーが発射ボタンを押すと、“Launcher” ゲームオブジェクトは砲弾プレハブをインスタンス化します。プレハブにはメッシュ、リジッドボディ、コライダーが含まれているため、空中を飛んで衝突を検知することができます。

  2. 砲弾が何かと衝突し、爆発プレハブをインスタンス化します。爆発プレハブにはパーティクルシステムエフェクトと周囲のゲームオブジェクトに力を加えるスクリプトが含まれています。

上の Block プレハブと同じように、砲弾プレハブがどんなに複雑であっても、砲弾を 1 行のコードでインスタンス化できます。プレハブをインスタンス化した後、インスタンス化されたゲームオブジェクトのプロパティを変更することもできます。例えば、砲弾のリジッドボディの速度を設定できます。

使いやすいだけでなく、コードに手を加えることなくプレハブを後で変更することができます。したがって、砲弾がロケットの場合、後でパーティクルシステムを追加して、雲の航跡を残すことができます。これを行うと、インスタンス化したすべてのロケットにパーティクルの航跡ができます。

次のスクリプトは Instantiate() 関数を使用して砲弾を発射する方法を示しています。

using UnityEngine;
public class FireProjectile : MonoBehaviour
{
    public Rigidbody projectile;
    public float speed = 4;
    void Update()
    {
        if (Input.GetButtonDown("Fire1"))
        {
            Rigidbody p = Instantiate(projectile, transform.position, transform.rotation);
            p.velocity = transform.forward * speed;
        }
    }
}

コードでは、プレハブ変数の型は Rigidbody であり、GameObject ではありません。これには 2 つの便利な効果があります。

  1. この変数に割り当てることができるのは、Rigidbody コンポーネントを持つゲームオブジェクトだけです。このことは、正しいゲームオブジェクトを変数に割り当てるのに役立ちます。

  2. Instantiate メソッドは、新しいインスタンスの Rigidbody コンポーネントへの参照を返します。これは、インスタンス化した直後にリジッドボディの速度を簡単に設定できるので便利です。

public のプレハブ変数を作成する場合、変数の型は GameObject にすることも、任意の有効なコンポーネント型 (Unity ビルトインコンポーネントか、独自の MonoBehaviour スクリプトの 1つ) にすることも可能です。

GameObject 型の変数の場合、任意のゲームオブジェクトを変数に割り当てることができ、Instantiate 関数は新しいゲームオブジェクトインスタンスへの参照を返します。

コンポーネント型の変数 (Rigidbody、Collider、Light など) の場合、そのコンポーネント型のゲームオブジェクトのみを変数に割り当てることができ、Instantiate 関数は新しいゲームオブジェクトインスタンスの特定のコンポーネントへの参照を返します。

以下のスクリプト (砲弾プレハブに設定) は、砲弾の現在位置で爆発をインスタンス化し、砲弾が何かと衝突したときに砲弾ゲームオブジェクトを削除するアクションを実行します。

using UnityEngine;
public class Projectile : MonoBehaviour
{
   public GameObject explosion;
   void OnCollisionEnter()
   {
       Instantiate(explosion,transform.position,transform.rotation);
       Destroy(gameObject);
   }
}
砲弾のプレハブがインスタンス化され、衝突すると爆発プレハブに置き換えられる例
砲弾のプレハブがインスタンス化され、衝突すると爆発プレハブに置き換えられる例

上の図の再生モードで実行されているスクリプトを見ると、インスタンス化されたゲームオブジェクトが Hierarchy に現れ、名前に “(Clone)” が加えられます。

キャラクターをラグドールまたは破壊物で置き換える

ゲームでは、キャラクター、乗り物、建物、その他のアセットを “無傷の” 状態から “破壊された” 状態に切り替えることがよくあります。ゲームオブジェクトの無傷のバージョンを変更 (スクリプトの削除や Rigidbody コンポーネントの追加など) しようとするよりも、無傷のゲームオブジェクト全体を削除し、インスタンス化され破壊されたプレハブに置き換える方がはるかに効率的で効果的です。これにより、作業を大幅に柔軟にします。破壊されたバージョンに異なるマテリアルを使用したり、完全に異なるスクリプトを添付したり、ばらばらに破壊されたプレハブをインスタンス化して、元のゲームオブジェクトの破壊されたバージョンをシミュレートすることができます。これらのオプションはどれも Instantiate() を 1 回呼び出すことで実行でき、破棄されたバージョンをシーンに加え、元のバージョンを削除します。

最も重要なことは、元のオブジェクトとは完全に異なるゲームオブジェクを使って Instantiate() を呼び出すことによって、破壊されたバージョンを作成できるということです。例えば、破壊されるロボットを作成するには、2 つのバージョンのモデルを作ります。1 つは、メッシュレンダラーを備えた 1 つのゲームオブジェクトとロボットの動きを制御するスクリプトで作成します。

もう 1 つは、物理演算によって個別に制御できるいくつかの骨格パーツで作成します。ゲームは、ゲームオブジェクト 1 つだけのモデルを使うほうが、より速く実行できます。なぜなら、モデルに含まれる三角形が少なく、多くの小さいパーツがあるロボットよりも速くレンダリングできるためです。また、ロボットが (壊れていないで) 楽しく歩き回っているときに、個々のパーツに分けて動かすことはありません。

壊れたロボットのプレハブをビルドする作成するには 、以下の手順を行います。

  1. お気に入りの 3D モデリングソフトウェアで、多くのさまざまな骨格パーツを使ってロボットをモデリングし、Unity プロジェクトの Assets フォルダーにエクスポートします。

  2. Unity エディターで空 (何も入っていない) のシーンを作成します。

  3. モデルを Project ウィンドウから空のシーンにドラッグします。

  4. すべてのパーツを選択し、Component > Physics > Rigidbody を選んで、リジッドボディをすべてのパーツに加えます。

  5. すべてのパーツを選択し、Component > Physics > Mesh Collider を選択して、すべてのパーツにコライダーを加えます (より高速なパフォーマンスのためには Convex オプションを有効にします)。

  6. 壊れたロボットのすべてのパーツが 1 つのルートゲームオブジェクトの子になるようにしてください。

  7. さらに特殊な効果を得るには、煙のパーティクルシステムを子ゲームオブジェクトとして各パーツに加えします。

  8. これで、複数の爆発可能なパーツを持つロボットができました。パーツは物理演算によって制御されているため、地面に落下する可能性があります。各パーツは、アタッチされたパーティクルシステムによってパーティクルトレイルを作成します。

  9. 再生をクリックしてモデルの反応をプレビューし、必要な調整を行います。

  10. ルートのゲームオブジェクトを Project ウィンドウの Assets フォルダーにドラッグして新しいプレハブを作成します。

次の例は、これらの手順をコードでモデル化する方法を示しています。

using UnityEngine;
public class WreckOnCollision : MonoBehaviour
{
   public GameObject wreckedVersion;
   // Update は各フレームで 1 回呼び出されます
   void OnCollisionEnter()
   {
       Destroy(gameObject);
       Instantiate(wreckedVersion,transform.position,transform.rotation);
   }
}
ロボットのプレハブが砲弾に当たったときに、壊れたプレハブと交換される例
ロボットのプレハブが砲弾に当たったときに、壊れたプレハブと交換される例

これらすべてのサンプルを含むプロジェクトをこちらからダウンロードできます。 InstantiatingPrefabsExamples.zip

変数とインスペクター
イベント関数の実行順序