Version: 5.4
作業内容の保存
入力

実行時のプレハブのインスタンス化

この時点までに基本的なところまで Prefabs の概念について理解できているはずです。これらはゲームを通じて再利用できる、事前に定義済みの GameObject および Components です。もしプレハブが何か分からない場合は、プレハブ でより基本的な説明があるため参照してください。

プレハブは複雑なゲームオブジェクトを実行時にインスタンス化したい場合に便利です。プレハブのインスタンス化に関する代替手段は 0 からコードを使用してゲームオブジェクトを作成することです。プレハブのインスタンス化はこの代替手段に比べて多くの長所があります:

  • プレハブのインスタンス化をコード一行で、完全な機能つきで行うことができます。コードから同等のゲームオブジェクトを作成することは平均で 5 行か、実際にはそれ以上かかります。
  • シーンおよびインスペクター上で早く簡単にプレハブをセットアップ、テスト、修正できます。
  • インスタンス化に使用したコードを変更することなく、インスタンス化されるプレハブを変更することができます。シンプルなロケットを最速のロケットに変更することができてコードの変更が必要ありません。

基本的なシナリオ

プレハブの威力を確認するために、便利に使える基本的な場面を考えて見ます:

  1. ひとつの “レンガ” から壁を作成するために、複数回異なる場所に作成する
  2. ユーザーが発射ボタンを押すと、ロケットランチャーはロケットのプレハブをインスタンス化します。そのプレハブは、メッシュ、Rigidbodyコライダー およびトレイル パーティクルシステム を含む子ゲームオブジェクトから構成されます。
  3. ロボットがバラバラにいくつもの部分に爆発します。完全な、機能するロボットは破壊されて、壊れたロボットプレハブで置き換えられます。このプレハブはいくつもの部分に分割されたロボットを含み、すべて各々の Rigidbody やパーティクルシステムでセットアップされています。このテクニックによりロボットをいくつもの部分に爆発させるのに、一行のコードだけでオブジェクトをプレハブで置き換えることにより実現できます。

壁の作成

このサンプルではプレハブの使用をコードからオブジェクト作成と比較して長所を確認していきます。

最初に、コードからレンガの壁を作成します:

// JavaScript
function Start () {
    for (var y = 0; y < 5; y++) {
        for (var x = 0; x < 5; x++) {
            var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cube.AddComponent.<Rigidbody>();
            cube.transform.position = Vector3 (x, y, 0);
        }
    }
}


// C#
public class Instantiation : MonoBehaviour {

    void Start() {
        for (int y = 0; y < 5; y++) {
            for (int x = 0; x < 5; x++) {
                GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                cube.AddComponent<Rigidbody>();
                cube.transform.position = new Vector3(x, y, 0);
            }
        }
    }
}
  • 上記のスクリプトを使用するには、単にスクリプトを保存して空のゲームオブジェクトにドラッグ&ドロップします。
  • 空のゲームオブジェクトを GameObject->Create Empty で作成します。

もしコードを実行すると、プレーモードに入ったときにレンガの壁全体が見られます。個別のレンガの機能に関連する箇所が 2行あります: CreatePrimitive の行、および AddComponent の行です。ここまで悪くはないですが、各々のレンガはテクスチャがありません。行いたいすべての追加のアクションは一行づつ必要となります、例えばテクスチャ変更、摩擦、Rigidbody の mass (重量)は別の行です。

もしプレハブを作成して、セットアップをあらかじめ行っておくと、一行のコードを使って各レンガの作成とセットアップを行います。これにより、もし変更を行いたい場合に大量のコードの保守と変更が不要となります。プレハブがあれば、変更を行って実行するのみです。コードの修正は必要ありません。

もし各レンガでプレハブを使用している場合、次のコードで壁が作成できます。

// JavaScript

var brick : Transform;
function Start () {
    for (var y = 0; y < 5; y++) {
        for (var x = 0; x < 5; x++) {
            Instantiate(brick, Vector3 (x, y, 0), Quaternion.identity);
        }
    }
}


// C#
public Transform brick;

void Start() {
    for (int y = 0; y < 5; y++) {
        for (int x = 0; x < 5; x++) {
            Instantiate(brick, new Vector3(x, y, 0), Quaternion.identity);
        }
    }
}

これは綺麗であるだけでなく再利用性も高いものです。キューブをインスタンス化する、または rigidbody を含む必要があると書いているところはありません。このすべてはプレハブに定義されていてエディターで速やかに作成することができます。

次にプレハブを作成して、これをエディターで行います。次はそれを行う方法です:

  1. GameObject > 3D Object > Cube を選択
  2. Component > Physics > Rigidbody を選択
  3. Assets > Create > Prefab を選択
  4. Project ビュー でプレハブの名前を Brick に変更します。
  5. Hierarchy で作成したキューブを Project ビュー の Brick プレハブにドラッグ&ドロップします。
  6. 作成したプレハブで、Hierarchy ビューから安全にキューブを削除できます。( Windows では Delete 、Mac では Command-Backspace

レンガプレハブを作成したので、これでスクリプトの brick 変数にアタッチする必要があります。スクリプトを含む空のゲームオブジェクトを選択します。インスペクター上で “brick” という名前で新しい変数が表示されたことを確認してください。

次にプロジェクトビューから Brick プレハブをインスペクター上の brick 変数にドラッグ&ドロップします。Play をクリックすれば、プレハブで作成された壁が表示されます。

このワークフローパターンは Unity で繰り返し何回も使用できるものです。はじめは、スクリプトから作成すると 2 行長いだけで、なぜこの方法がそこまでよいのか分からないかもしれません。

しかし、今はプレハブを使用しているので、プレハブの調整を数秒でできます。すべてのインスタンスの密度を変更したい場合はプレハブの Rigidbody を 1回調整するだけです。すべてのインスタンスで Material を別のものに変更したい場合はマテリアルをプレハブの上に1回ドラッグ&ドロップするだけです。摩擦を変更したい場合はプレハブのコライダーで Physic Material を変更するだけです。すべてのボックスにパーティクルシステムを追加したい場合はプレハブに子オブジェクトを1回追加するのみです。

ロケットおよび爆発のインスタンス化

プレハブをこのシナリオで使用する方法は次のとおりです:

  1. ユーザーが発射ボタンを押すと、ロケットランチャーはロケットのプレハブをインスタンス化します。そのプレハブは、メッシュ、Rigidbody、コライダーおよびトレイルパーティクルシステムを含む子ゲームオブジェクトから構成されます。
  2. ロケットが命中し、爆発プレハブがインスタンス化されます。爆発プレハブには、パーティクルシステム、時間の経過とともにフェードアウトするライト、および周囲のゲームオブジェクトにダメージを与えるスクリプトが含まれています。

ロケットのゲームオブジェクトを完全にコードから手動でコンポーネントを追加しプロパティーをセットして作成することもできますが、プレハブをインスタンス化したほうがはるかに簡単です。ロケットのインスタンス化を、ロケットのプレハブの複雑さに関わらず、一行のコードで行うことができます。プレハブをインスタンス化した後、さらにインスタンス化されたオブジェクトのプロパティーを変更することができます。(例えば、ロケットの Rigidbody の velocity をセットする、等)

あつかうのが容易である事とは別にして、プレハブは後から変更することができます。そのためロケットの作成時に、急いでトレイルパーティクルを加える必要はありません。後から追加すれば良いのです。トレイルを子ゲームオブジェクトとしてプレハブに追加すると、すべてのインスタンス化されたロケットに、トレイルのパーティクルがつきます。そして最後に、インスペクター上で速やかにロケットプレハブのプロパティーを微調整できるため、ゲームの調整が遥かに容易になります。

次のスクリプトでロケットを Instantiate() 関数を使用して発射する方法を示します。

// JavaScript

//  ロケットをリジッドボディにする必要があります
// この方法では、ユーザーはリジッドボディなしにはプレハブを指定できません。
var rocket : Rigidbody;
var speed = 10.0;

function FireRocket () {
    var rocketClone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    // クローンの他のコンポーネント / スクリプトにもアクセスできます
    rocketClone.GetComponent.<MyRocketScript>().DoSomething();
}

// Cntrl かマウスを押すと、 fire メソッドを呼び出します
function Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


// C#

//  ロケットをリジッドボディにする必要があります
// この方法では、ユーザーはリジッドボディなしにはプレハブを指定できません。
public Rigidbody rocket;
public float speed = 10f;

void FireRocket () {
    Rigidbody rocketClone = (Rigidbody) Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    
    // クローンの他のコンポーネント / スクリプトにもアクセスできます
    rocketClone.GetComponent<MyRocketScript>().DoSomething();
}

// Cntrl かマウスを押すと、 fire メソッドを呼び出します
void Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


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

完全にリギングされた敵キャラクターがいて、死亡したとします。その場合、単にキャラクター上で死亡したアニメーションを再生して、敵ロジックを常時ハンドリングしていたすべてのスクリプトを無効化します。恐らく、いくつかのスクリプトを取り除く必要があるでしょうし、死亡した敵に対して誰も攻撃しない事を確認するためのカスタムロジックの追加や、その他のクリーンアップのタスクが必要になるでしょう。

もっと良い方法は、敵キャラクターを直ちに削除して、インスタンス化された破壊物で置き換えることです。これにより柔軟性が高まります。死亡したキャラクターに対して異なるマテリアルを適用したり、まったく異なるスクリプトをアタッチしたり、いくつもの部分に破壊されたオブジェクトを含むプレハブを生成してバラバラになった敵を再現したり、または単純にキャラクターのあるバージョンを持ったプレハブをインスタンス化することもできます。

いずれのオプションであっても、1回 Instantiate() をコールするだけであり、正しいプレハブに結び付けて、それで完了です。

重要な部分は Instantiate() する破壊物は元とは全く異なるオブジェクトで作成できることです。例えば、飛行機があったとして、2つのバージョンをモデリングします。ひとつは飛行機が、Mesh Renderer および飛行機の物理挙動を実行するスクリプトです。モデルをひとつのオブジェクトだけにすることでゲームは、より少ない三角形で作成され、より少ないオブジェクトで構成されるため、より小さいパーツを多く使用するよりもレンダリングが早く、より高速に実行できます。さらに飛行機が飛び回っている間はわざわざいくつかの部分に分ける必要性がありません。

破壊された飛行機のプレハブを作成するときに典型的なステップは:

  1. 好みのモデリングツールを使用して飛行機をたくさんのパーツから作成します。
  2. 空のシーンを作成
  3. 空のシーンにモデルをドラッグ&ドロップ
  4. Rigidbody をすべてのパーツを選択して Component->Physics->Rigidbody を選択することで、すべてのパーツに追加
  5. すべてのパーツを選択して Component->Physics->Box Collider を選択することで、Box Colliders をすべてのパーツにに追加
  6. より特別なエフェクトのために、煙のようなパーティクルシステムを子ゲームオブジェクトとしてすべてのパーツに追加
  7. これで飛行機には複数の爆発するパーツがあり、地面には物理挙動で落ち、アタッチされたパーティクルシステムによるトレイルパーティクルが作成されます
  8. Assets->Create Prefab を選択
  9. すべての飛行機のパーツを含むルートのゲームオブジェクトをプレハブにドラッグ&ドロップ

次の例は、これらのステップがどのようにコード内でモデル化されているかをお見せします。

// JavaScript

var wreck : GameObject;

// 例として、自動的に3秒後にゲームオブジェクトを破棄します
function Start () {
    yield WaitForSeconds(3);
    KillSelf();
}

// ctrl かマウスを押すと、fire メソッドを呼び出します
function KillSelf () {
    //破棄したゲームオブジェクトを同じ位置にインスタンス化します 
    var wreckClone = Instantiate(wreck, transform.position, transform.rotation);

    // オブジェクトから破棄したものへ、いくつかの変数を引き継ぐ
    // 必要がある場合もあります。
    wreckClone.GetComponent.<MyScript>().someVariable = GetComponent.<MyScript>().someVariable;

    // 破棄します
    Destroy(gameObject);


// C#

public GameObject wreck;

// 例として、自動的に3秒後にゲームオブジェクトを破棄します
IEnumerator Start() {
    yield return new WaitForSeconds(3);
    KillSelf();
}

// ctrl かマウスを押すと、fire メソッドを呼び出します
void KillSelf () {
    // 破棄したゲームオブジェクトを同じ位置にインスタンス化します
    GameObject wreckClone = (GameObject) Instantiate(wreck, transform.position, transform.rotation);
    
    // オブジェクトから破棄したものへ、いくつかの変数を引き継ぐ
    // 必要がある場合もあります。
    wreckClone.GetComponent<MyScript>().someVariable = GetComponent<MyScript>().someVariable;
    
    //  破棄します
    Destroy(gameObject);
}

}

たくさんのオブジェクトを特定パターンで配置する

例えばたくさんのオブジェクトをグリッド状または円状に配置したいとします。伝統的にはこれは次のいずれかの方法をとります:

  1. コードから完全にオブジェクトを作成するとします。これは退屈な作業です。スクリプトから値を入力するのは遅く、直感的でなく、労力に見合いません。
  2. 完全にリギングされたオブジェクトを作成し、複製して、シーン上に複数回配置するとします。これは退屈な作業で、グリッドに性格に配置することが難しい作業です。

このため、代わりにプレハブで Instantiate() を使用してください。なぜゲームオブジェクトがこのシナリオで役に立つのかは理解できてるとおもいます。このシナリオのため必要なコードは次のとおりです。

// JavaScript

// プレハブを円状にインスタンス化

var prefab : GameObject;
var numberOfObjects = 20;
var radius = 5;

function Start () {
    for (var i = 0; i < numberOfObjects; i++) {
        var angle = i * Mathf.PI * 2 / numberOfObjects;
        var pos = Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
        Instantiate(prefab, pos, Quaternion.identity);
    }
}


// C#
// プレハブを円状にインスタンス化

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;
        Vector3 pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
        Instantiate(prefab, pos, Quaternion.identity);
    }
}


// JavaScript

// プレハブをグリッド状にインスタンス化
var prefab : GameObject;
var gridX = 5;
var gridY = 5;
var spacing = 2.0;

function Start () {
    for (var y = 0; y < gridY; y++) {
        for (var x=0;x<gridX;x++) {
            var pos = Vector3 (x, 0, y) * spacing;
            Instantiate(prefab, pos, Quaternion.identity);
        }
    }
}


// C#

// プレハブをグリッド状にインスタンス化

public GameObject prefab;
public float gridX = 5f;
public float gridY = 5f;
public float spacing = 2f;

void Start() {
    for (int y = 0; y < gridY; y++) {
        for (int x = 0; x < gridX; x++) {
            Vector3 pos = new Vector3(x, 0, y) * spacing;
            Instantiate(prefab, pos, Quaternion.identity);
        }
    }
}


作業内容の保存
入力