ゲームプレイの作成
入力

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

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

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

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

基本的なシナリオ

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

  1. ひとつの “レンガ” から壁を作成するために,複数回異なる場所に作成する
  2. ロケットランチャーが発射時に空飛ぶロケット プレハブ をインスタンス化します。プレハブはメッシュ, Rigidbody, Collider, および子ゲームオブジェクトと軌跡に Particle System を含みます。
  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 で作成します。

もしコードを実行すると, Play Mode に入ったときにレンガの壁全体が見られます。個別のレンガの機能に関連する箇所が二行あります: 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 > Create General > Cube を選択
  2. Component > Physics > Rigidbody を選択
  3. Assets > Create > Prefab を選択
  4. Project View でプレハブの名前を “Brick” に変更します。
  5. Hierarchy で作成したキューブを Project View の “Brick” プレハブにドラッグ&ドロップします。
  6. 作成したプレハブで,階層ビューか ら安全にキューブを削除出来ます。(Windows では Delete ,Macでは Command-Backspace

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

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

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

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

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

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

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

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

使用することが簡単であることとは別に,後からプレハブを変更することが出来ます。このためロケットを作成している場合,すぐにトレイル パーティクルを加える必要がありません。それは後から出来ます。トレイルを子ゲームオブジェクトとしてプレハブに加えると,全てのインスタンス化されたロケットはトレイルのパーティクルが出来ます。そして最後に,速やかにインスペクタ上でロケットプレハブのプロパティを微調整できるため,ゲームの調整が遥かに容易になります。

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

// JavaScript

// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
var rocket : Rigidbody;
var speed = 10.0;

function FireRocket () {
    var rocketClone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    // You can also acccess other components / scripts of the clone
    rocketClone.GetComponent(MyRocketScript).DoSomething();
}

// Calls the fire method when holding down ctrl or mouse
function Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


// C#

// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
public Rigidbody rocket;
public float speed = 10f;

void FireRocket () {
    Rigidbody rocketClone = (Rigidbody) Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    
    // You can also acccess other components / scripts of the clone
    rocketClone.GetComponent<MyRocketScript>().DoSomething();
}

// Calls the fire method when holding down ctrl or mouse
void Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


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

完全にリギングされた敵キャラクターがいて,死亡したとします。キャラクター上で単に死亡したアニメーションを再生して,全ての敵ロジックをハンドリングしていた全てのスクリプトを無効化します。おそらくいくつかのスクリプトを取り除くことが必要となり,追加のカスタムロジックにより誰も死亡した敵に対する攻撃を行わない,などその他クリーンアップのタスクが必要です。

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

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

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

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

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

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

// JavaScript

var wreck : GameObject;

// As an example, we turn the game object into a wreck after 3 seconds automatically
function Start () {
    yield WaitForSeconds(3);
    KillSelf();
}

// Calls the fire method when holding down ctrl or mouse
function KillSelf () {
    // Instantiate the wreck game object at the same position we are at
    var wreckClone = Instantiate(wreck, transform.position, transform.rotation);

    // Sometimes we need to carry over some variables from this object
    // to the wreck
    wreckClone.GetComponent(MyScript).someVariable = GetComponent(MyScript).someVariable;

    // Kill ourselves
    Destroy(gameObject);


// C#

public GameObject wreck;

// As an example, we turn the game object into a wreck after 3 seconds automatically
IEnumerator Start() {
    yield return new WaitForSeconds(3);
    KillSelf();
}

// Calls the fire method when holding down ctrl or mouse
void KillSelf () {
    // Instantiate the wreck game object at the same position we are at
    GameObject wreckClone = (GameObject) Instantiate(wreck, transform.position, transform.rotation);
    
    // Sometimes we need to carry over some variables from this object
    // to the wreck
    wreckClone.GetComponent<MyScript>().someVariable = GetComponent<MyScript>().someVariable;
    
    // Kill ourselves
    Destroy(gameObject);
}

}

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

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

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

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

// JavaScript

// Instantiates a prefab in a circle

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#
// Instantiates a prefab in a circle

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

// Instantiates a prefab in a grid

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#

// Instantiates a prefab in a grid

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);
        }
    }
} 


ゲームプレイの作成
入力