Instantiating Prefabs at runtime
Unity Manual > User Guide > Creating Gameplay > Instantiating Prefabs at runtime

Instantiating Prefabs at runtime

Здесь мы раскроем концепцию префаб на фундаментальном уровне. Префаб — это собрание заранее подготовленных объектов и компонентов для их многократного использования в игре. Информацию о префабах так же можно найти в параграфе Prefabs.

Prefabs come in very handy when you want to instantiate complicated GameObjects at runtime. The alternative to instantiating Prefabs is to create GameObjects from scratch using code. Instantiating Prefabs has many advantages over the alternative approach:

Префабы полезны, когда нужно создать несколько экземпляров сложного объекта во время игры (runtime). Другой способ реализации этого — создание новых объектов с помощью кода. У использования префабов есть определёные преимущества:
* можно вызывать префабы с помощью всего одной строки кода. Создание новых объектов потребует не меньше пяти строк;
* можно очень быстро настраивать, тестировать и редактировать префбаы в Scene View или в Inspector;
* можно изменить экземпляр префаба, не меняя сам префаб.

Common Scenarios

Для иллюстрации силы префабов рассмотрим следующие ситуации:
* Построение стены из одного префаба-«кирпича».
* Ракета из префаба. Префаб содержит меш, Rigidbody, Collider и дочерний объект с Particle System.
* Робот, разлетающийся на части. Робот будет уничтожен и заменён на разрушенного префаб-робота. Префаб содержит робота, разделённого на много частей, каждая из которых снабжена Rigidbodies и Partycle Systems.

Building a wall

Здесь мы проиллюстрируем преимущество использования префабов перед созданием объектов из кода.

Сначала построим кирпичную стену первым способом, с помощью кода:

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

Для использования скрипта мы должны сохранить его и присвоить пустому объекту (GameObject>Create Empty).

При запуске игры (при входе в Play Mode) код будет выполнен и стена будет составлена из кирпичей. К функционалу каждого кирпича относятся две строки: CreatePrimitive() и AddComponent(). Сейчас ничего плохого в этом нет, но все кирпичи нетекстурированы. Каждое дополнительное действие над кирпичами (текстуры, Rigidbody и т. п.) потребует дополнительных строк.

Если создавать префаб и задавать все настройки заранее, то достаточно будет одной линии кода для создания и настройки каждого кирпича. Это уменьшает объём кода, с которым нужно работать.

Если используется префаб для кирпичей, нужно задать следующий код для создания стены:

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

Чисто и пригодно для повторного использования. Всё определяется через префаб и быстро создаётся в редакторе.

Нужно создать префаб:
* GameObject>Create Other>Cube.
* Component>Physics>Rigidbody.
* Assets—Create Prefab.
* В Project View переименовываем префаб в «Brick».
* Перетаскиваем куб из Hierarchy View в Project View на префаб «Brick».
* Куб в Hierarchy View теперь можно удалить.

Мы создали префаб кирпича «Brick», теперь можно прикрепить его к переменной cube в скрипте. Выберем пустой объект, который содержит скрипт. В Inspector отображается переменная cube.


Этой переменной может быть присовен любой объект или префаб.

Перетащим префаб «Brick» из Project View на переменную cube в Inspector. Нажмём Play и посмотрим, как стена будет построена из префабов.

Казалось бы, чем лучше способ с префабами, если создание объектов из кода всего на две строки длиннее?

А лучше он тем, что используя префаб, можно изменить все параметры нашей стены в считанные секунды и клики.
Нужно изменить массу всех кирпичей? Просто измените настройки Rigidbody в префабе.
Нужно использовать материал на всех кирпичах? Просто перетяните материал на префаб.
Нужно добавить/изменить силу трения? Используйте физический материал в коллайдера префаба.
И т. д., и т. п.

Instantiating rockets & explosions

Здесь префабы будут использоваться так:
* Ракета вызывается из префаба, когда пользователь нажимает клавишу огня. Префаб содержит меш, Rigidbody, коллайдер и дочерний объект с системой частиц.
* Ракета получает импульс для движения, затем вызывается префаб взрыва. Префаб взрыва содержит систему частиц, ИС и скрипт нанесения повреждений объектам.

Можно было бы вручную создавать объекты и добавлять компоненты вручную из кода, но, как уже было сказано, мы будем использовать префабы, что позволит настроить и перенастроить всю систему (например, добавить Rigidbody) очень быстро.

Этот скрипт показывает, как используется в запуске ракеты функция Instantiate()

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

Replacing a character with a ragdoll or wreck

Предположим, у нас есть полностью заригенный персонаж противника и он умирает. Можно просто воспроизвести анимацию смерти персонажа и выключить все скрипты, описывающие его логику. Также придётся удалить ещё несколько скриптов и добавить немного логики для того, чтобы никто не продолжал атаковать мёртвого потивника, а также решить ещё некоторые задачи.

Намного проще и быстрее будет реализовать всё это удалением персонажа и заменой его на заранее подготовленный префаб, представляющий из себя мёртвого персонажа. Это предоставляет значительную гибкость. Можно применить разные материалы к мёртвому персонажу, добавить новые скрипты, вызвать префаб содержащий симуляцию разрушения персонажа на множество частей или просто вызывать префаб, содержащий «разрушенную» модель персонажа.

И всё это реализуется через простую функцию Instantiate()

Важно помнить, что мёртвый персонаж, вызываемый Instantiate(), может полностью отличаться от оригинала (живого персонажа). Например, если мы имеем аэроплан, то мы моделируем две его версии. Первая (исправная) версия содержит один объект с компонентом Mesh Renderer и скриптом, описывающим физику. Благодаря хранению модели в одном объекте, мы существенно сэкономим треугольники и производительность.

Чтобы создать префаб разрушенного самолёта, нужно сделать следующее:
* Смоделировать самолёт из большого количества частей в 3D-пакете.
* Создать пустую сцену.
* Перетащить модель в пустую сцену.
* Добавить компонент Rigidbody всем частям (Component>Physics>Rigidbody).
* Добавить Box Colliders всем частям (Component>Physics>Collider).
* Для специальных эффектов добавить систему частиц, имитирующую дым, как дочерний объект каждой части.
* Теперь имеется самолёт из множество частей, который падают на землю, подчиняясь законам физики, и источая частицы.
* Assets>Create Prefab.
* Перетаскиваем главный объет, содержащий все части, в префаб.

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

Урок «First Person Shooter» объясняет, как заменить персонажа версией с регдоллом и синхронизировать всё это дело. Найти урок можно здесь — Tutorials.

Placing a bunch of objects in a specific pattern

Предположим, небходимо разместить связку объектов в виде сетки или круга. Традиционно, это делается так:
* Создаём объекты полностью из кода, что весьма утомительно.
* Создаём полностью риггенный объект, многократно дублируем его и располагаем как надо. Это долго и сложно.

Можно же воспользоваться префабом и функцией Instantiate()
Вот необходимый код:

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