0からスタート!ゲーム開発ブログ

ゲーム開発に関する様々な記事を更新します

Unityの重要機能!Instantiate メソッドの活用方法

 Unity で最も利用される処理の1つに Instantiate(インスタンシエイト) メソッドがあります。 このメソッドは多機能であるため、処理の内容をきちんと理解することで、適切な処理を組み立てることが出来るようになります。

 この記事では2回に分けて、Instantiate メソッドの各引数のオーバーロードごとの活用事例を、ケースに合わせて紹介します。


Instantiateメソッドの引数の書式


 メソッドの引数には、オーバーロードという機能が用意されています。

 オーバーロードとは、同じ名前のメソッドを引数の型、個数、順序が異なる形で複数定義することを指します。 これにより、同じメソッドでも異なる引数を受け取り、その結果として異なる動作をすることが可能になります。

 UnityのInstantiateメソッドは5つのオーバーロードがあります。

 これらはUnity の公式スクリプト・リファレンスにも記載されています。

docs.unity3d.com


 それでは実際に、どういうケースを想定してオーバーロードを変えていくか、具体的に説明していきます。



Instantiateメソッドのユースケース


1.ヒエラルキーにあるゲームオブジェクトを複製する


 第1引数のみを指定する書式を活用することで、引数に指定したゲームオブジェクトを複製することが出来ます。

 例えば、ヒエラルキーにあるゲームオブジェクトを指定すれば、同じ位置と回転情報を使って複製することが出来ます。

 以下は、特定のボタンが押された時にゲームオブジェクトを複製する場合のサンプルコードです。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CopyObject : MonoBehaviour
{
    public GameObject objectToCopy;  // ヒエラルキーにあるゲームオブジェクトをアサインしておきます

    void Update()
    {
        // スペースキーが押されたときに
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // ゲームオブジェクトを複製
            GameObject copy = Instantiate(objectToCopy);

            // 必要に応じて座標の調整
            copy.transform.position = new Vector3(0, 10, 0);
        }
    }
}


 objectToCopy 変数にプレハブをアサインすれば、プレハブの座標と回転情報を利用して複製できます。




2.指定したオブジェクトに子オブジェクトを追加する


 Instantiateメソッドの第1引数に複製したいオブジェクトやプレハブ、第2引数に Transform 型を指定すると、 生成されるオブジェクトはその第2引数に指定した Transform の位置と回転情報に加えて、そのゲームオブジェクトを親として持ちます。 これを利用すると、例えば、キャラクターが武器や装備品を装備するといったシナリオを実装することができます。

 以下にサンプルコードを提示します。


using UnityEngine;

public class Character : MonoBehaviour
{
    public GameObject swordPrefab;  // ソードのプレハブ
    public Transform handTransform;  // キャラクターの手の位置を示すTransform

    void EquipSword()
    {
        // ソードを生成し、キャラクターの手の位置に配置する
        GameObject sword = Instantiate(swordPrefab, handTransform);

        // この時点で、swordの親はhandTransformとなるので、
        // キャラクターが動いたり回転したりすると、ソードもそれに従って動く

    }
}


 この引数のオーバーロードを利用した場合、親子関係が成立することになりますので、 事前にゲームオブジェクト間の親子関係が判明している場合には、生成と同時に親子関係が構築できるこの記法が便利です。



3.エフェクトなどのパーティクルの回転情報にプレハブの情報を利用する


 最も利用されるパターンである、第1引数に複製するゲームオブジェクトやプレハブ、 第2引数に Vector3 型での座標指定第3引数に Quaternion 型での回転指定オーバーロードの使用例です。

 ここでは第3引数の使い方について、便利な方法を紹介します。




 例えば、敵キャラクターを倒したときに爆発エフェクトをその場所で生成したいといった場合、 エフェクトのプレハブは通常、その視覚的な表現に最適な特定の向きを持っています(例えば、爆発エフェクトは上向きに爆発します)。 そのため、新たにインスタンス化するときには、そのプレハブの回転を維持したい、というケースが多いです。

 エフェクトやパーティクルシステムなどは通常、それ自体で一連の動作(例えば爆発や火花)を行い、 それらの動作はエフェクトが持つローカル座標系に依存しているためです。

 そのような時には、第3引数に、プレハブの持つ回転情報を指定する方法があります。

 以下にサンプルコードを提示します。


using UnityEngine;

public class Enemy : MonoBehaviour
{
    public GameObject explosionEffectPrefab;  // 爆発エフェクトのプレハブ

    void DestroyEnemy()
    {
        // この敵が倒されたときに爆発エフェクトを生成する
        Instantiate(explosionEffectPrefab, transform.position, explosionEffectPrefab.transform.rotation);

        // エネミーオブジェクトを削除する
        Destroy(gameObject);
    }
}


 エフェクトの回転はプレハブ自体の回転(explosionEffectPrefab.transform.rotation)に設定されます。 これにより、エフェクトはプレハブと同じ向きで生成され、この例であれば、その場で爆発します。 想定している回転でのエフェクトを生成する際に役立ちますね。

 毎回 Quaternion で詳細に回転を指定したり、Quaternion.identity を利用しなくても、 生成する際の情報には、プレハブの持つ座標や回転情報も指定できるということを覚えておきましょう。

4.Canvas 内に UI 用のプレハブを生成する


 Instantiate メソッドの引数の書式は、第2引数に Transform 型第3引数に bool 型を取るオーバーロードもあります。

 第2引数の Transfrom 型は、【2】で紹介した場合と同じで、座標と回転の情報に加えて、生成されるゲームオブジェクトの親となる Transform オブジェクトです。

 第3引数の bool 型は、生成されるゲームオブジェクトの位置がワールド座標系で指定されるべきか(true の場合)、 それとも親のローカル座標系で指定されるべきか(false の場合)を指定するものです。

 これは主に Canvas 内に UI 用のプレハブを生成する際に役立ちます

 以下にサンプルコードを提示します。


using UnityEngine;
using UnityEngine.UI;

public class InstantiateExample : MonoBehaviour
{
    public Button buttonPrefab;  // 生成するボタンのプレハブ
    public Transform canvasTransform;  // CanvasのTransform

    void Start()
    {
        // ボタンをCanvasの子として生成し、その位置をCanvasのローカル座標系で設定する
        Button newButton = Instantiate(buttonPrefab, canvasTransform, false);

        // ボタンの位置をCanvas内で調整
        newButton.transform.localPosition = new Vector3(100, 200, 0);
    }
}


 第2引数に Canvas のゲームオブジェクトを指定することで、生成されるオブジェクトは Canvas の位置と回転情報の他に Canvas の子オブジェクトとして生成されます。これは UI の配置にとって最適な位置です。 なぜなら、ヒエラルキーにある UI 用のゲームオブジェクトは、必ず Canvas の子オブジェクトでなければ表示されない、というルールがあるためです。

 また、第3引数の指定を false にすることによって、生成されたゲームオブジェクトの座標は Canvas ゲームオブジェクトから見た位置(ローカル座標)として指定されます。

 このように Canvas や UI の特性を考えると、この書式を使った Instantiate メソッドがとても効果的な処理になります。

 この処理はさらに改良もできます。 例えば、ボタンの生成処理を for 文内で行うことで、複数のボタンを異なる位置にずらして並べる、という処理も作成できますね。 ブロック崩しのブロックを並べる処理の応用です。




Instantiateメソッドの第1引数における型の指定方法と比較


 UnityにおけるInstantiateメソッドは、ゲームオブジェクトやプレハブの複製を作成するために使用されます。 以下に、Instantiateメソッドを用いた2つのケースのサンプルコードを提供し、それぞれの利点と欠点について詳述します。



1.GameObject型を引数に取る


 このケースでは、Instantiateメソッドの引数にGameObject型を指定します。 その後、生成したゲームオブジェクトから特定のコンポーネントを取得するためにGetComponentを使用します。


public GameObject bulletPrefab;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        GameObject bulletObject = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
        Bullet bullet = bulletObject.GetComponent<Bullet>();
        bullet.Launch();
    }
}


 この方法の利点は、生成したゲームオブジェクトから任意のコンポーネントを取得できる柔軟性にあります。 つまり、1つのゲームオブジェクトにアタッチされている複数のコンポーネントを取得し操作することが可能です。

 欠点は、GetComponentが実行時に一定のパフォーマンスコストを伴うことです。 そのため、大量のゲームオブジェクトを生成し、それぞれからコンポーネントを取得する必要がある場合、パフォーマンスに影響を及ぼす可能性があります。



2. プレハブにアタッチされているクラスの型を指定


 このケースでは、Instantiateメソッドの引数にプレハブにアタッチされているクラスの型を指定します。 この方法を使用すると、GetComponentを使用せずに直接クラス内のメソッドにアクセスできます。

 以下の例は、弾のプレハブがあり、Bullet スクリプトがアタッチされているケースです。 先ほどのケースとは異なり、bulletPrefab 変数の型が Bullet 型で宣言されています。


public Bullet bulletPrefab;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Bullet bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
        bullet.Launch();
    }
}


 Instantiate メソッドを利用する際の、左辺の型が GameObject 型ではなく、Bullet 型になっています。 これは Instantiate メソッドの戻り値の型が、第1引数の型と同じになるためです。

 この方法の利点は、GetComponentを使わずに直接インスタンス化したオブジェクトのメソッドにアクセスできる点です。 これによりパフォーマンスが向上し、コードもすっきりします。よって、インスタンス時にアクセスしたいクラスが確定している場合には、こちらの手法が便利です。

 これらの型による生成の違いについては、このサイトの最初に提示した Unity の公式スクリプト・リファレンスにも記載されています。



まとめ


 いかがでしたか?

 何気なく使っている Instantiate メソッドですが、とても奥が深い、そして機能を理解することで応用が利く処理になっています。

 いま書いている処理にもきっと Instantiate メソッドは出てきていると思いますので、 どの引数のオーバーロードの書式を使っているのか、新しい視点でプログラムを見ていくようにしてみてください。