Guardando Su Trabajo
Input

Instanciando Prefabs en tiempo de ejecución

En este punto usted ya debe entender el concepto de Prefabs en un nivel fundamental. Estos son una colección de GameObjects & Components predefinidos que son re-utilizables a lo largo del juego. Si no sabe qué es un Prefab, le recomendamos leer la página de Prefabs para una introducción más básica.

Los Prefabs son muy útiles en el momento en que usted quiera instanciar GameObjects complejos en tiempo de ejecución. La alternativa para instanciar un Prefab es crear un GameObject desde cero usando código. Instanciar prefabs tiene muchas ventajas frente al método alternativo.

  • Puede instanciar un Prefab desde una linea de código, con una funcionalidad completa. En cambio, para crear GameObjects equivalentes desde código toma un promedio de cinco lineas de código, pero probablemente más.
  • Usted puede configurar, probar, y modificar el Prefab fácil y rápidamente en la escena y el Inspector.
  • Usted puede cambiar el Prefab siendo instanciado sin la necesidad de cambiar el código que lo instancia . Un cohete simple puede volverse en un cohete súper cargado, y no se requeriría de código para hacerlo.

Escenarios Comunes

Para ilustrar la fortaleza de los Prefabs, tomemos en consideración algunas situaciones básicas en dónde pueden ser útiles.

  1. Construyendo una pared de un solo prefab “ladrillo” al crearlo varias veces en diferentes posiciones.
  2. Un lanzacohetes instancia un prefab de un cohete cuando dispara. El Prefab contiene un Mesh, Rigidbody, Collider, y un GameObject hijo que contenga su propio camino/rastro de un particle system.
  3. Un robot explotando en muchas piezas. El robot completo operacional es destruido y remplazado con un prefab de un robot destruido. Este Prefab consistiría en la división del robot en muchas partes, todas configuradas con sus propios Rigidbodies y Particle Systems (sistema de partículas). Esta técnica le permite explotar un robot en muchas partes, con una sola linea de código, remplazando un objeto con un Prefab.

Construyendo una pared

Esta explicación ilustra las ventajas de usar un Prefab vs crear objetos desde el código.

Primero, construyamos una pared de ladrillo desde código:

// 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);
            }
        }
    }
 }
  • Para usar el script de arriba, sencillamente guardamos el script y lo arrastramos a un GameObject vacío.
  • Cree un GameObject vacío con GameObject->Create Empty.

Si usted ejecuta este código, va a ver como una pared completa ladrillo se crea cuando se ingresa al Play mode (modo de juego). Hay dos lineas relevantes a la funcionalidad de cada ladrillo individual: la linea de CreatePrimitive , y la linea de AddComponent . No es malo ahorita, pero, cada uno de los ladrillo no tiene textura. Cada acción adicional que queramos realizarle al ladrillo, como cambiar de textura, de fricción , o de Rigidbody mass, es en una linea extra.

Si usted crea un Prefab y realiza todas las configuraciones antes, usted solo entonces usa una linea de código para crear y configurara cada ladrillo. Esto lo libera del mantenimiento y del cambio de mucho código cuando decida que quiere hacer cambios. Con un Prefab, usted solo hace los cambios y juega. No hay una modificación del código necesario.

Si está utilizando un Prefab para cada ladrillo individual, este es el código que necesita para crear la pared.

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

Esto no solo es muy organizado, pero también re-utilizable. No hay nada que diga que estamos instanciando un cubo o que debe tener un rigidbody. Todo esto está definido en el Prefab y se puede crear de forma rápida en el Editor.

Ahora solo necesitamos crear el Prefab, que se puede hacer en el Editor. He aquí cómo:

  1. Escoja GameObject > 3D Object > Cube
  2. Escoja Component > Physics > Rigidbody
  3. Escoja Assets > Create > Prefab
  4. En el Project View, cambie el nombre de su nuevo Prefab a “Ladrillo”
  5. Arrastre el cubo que creo en la Hierarchy al Prefab del “Ladrillo” en el Project View
  6. Con el Prefab creado, usted puede con seguridad borrar el Cubo de la Jerarquía (Delete en Windows, Command-Backspace en Mac)

Hemos creado nuestro Prefab de Ladrillo, ahora tenemos que adjuntarlo a la variable brick en nuestro script. Cuando seleccione el GameObject vacío que contiene el script, la variable Brick va a aparecer en el inspector.

Ahora arrastre el Prefab del “Ladrillo” de la Vista de Proyecto(Project View) a la variable brick en el Inspector. Oprima el botón de juego (reproducir/play) y verá el muro construido con el Prefab.

Este es un patrón de flujo de trabajo que puede ser utilizado las veces que se quiera en Unity. En el principio usted podrá preguntarse por qué esto es mejor, ya que el script creando el cubo usando código es solo dos lineas más largo.

Pero ya que está utilizando un Prefab, usted puede ajustar el Prefab en segundos. Quiere cambiar la masa de todas esas instancias? Ajuste el RigidBody en el Prefab una sola vez. Quiere usar un Material para todas las instancias? Arrastre el Material al Prefab una sola vez. Quiere cambiar la fricción? Use un Physic Material diferente en el collider del Prefab. Quiere agregar un Particle System a todas esas cajas? Agregue un hijo al Prefab solo una vez.

Instanciando cohetes & explosiones

He aquí cómo los Prefabs tienen lugar en este escenario:

  1. Un lanzacohetes instancia un prefab de un cohete cuando el usuario presiona fuego. El Prefab contiene un mesh, Rigidbody, Collider, y un GameObject hijo que contiene un camino de un particle system.
  2. El cohete impacta e instancia un prefab de una explosión. El prefab de una explosión contiene un Particle System, una luz que se desvanece con el tiempo, y un script que aplica daño a los GameObjects de alado.

Aunque es posible construir un GameObject cohete completamente por código, añadiendo Componentes manualmente y ajustando propiedades; es más fácil instanciar un Prefab. Usted puede instanciar el cohete en una sola linea de código, sin importar qué tan complejo el prefab del cohete es. Después de instanciar el Prefab, también puede modificar cualquier propiedad del objeto instanciado (e.g. puede establecer la velocidad del Rigidbody del cohete).

A parte de ser más fácil de usar, usted puede actualizar el prefab más tarde. Entonces si está construyendo un cohete, inmediatamente no tiene que agregar un camino de partículas a él. Usted puede hacer eso más tarde. Tan pronto como agregue el camino como un GameObject hijo al Prefab, todos los cohetes instanciados tendrán caminos de partícula. Y últimamente, usted puede ajustar rápidamente las propiedades del prefab de su cohete en el Inspector, haciendo más fácil afinar su juego.

Este script muestra cómo lanzar un cohete usando la función 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();
    }
}


Sustituir un personaje con un muñeco de trapo o una ruina

Digamos que usted tiene un personaje enemigo completamente ensamblado y se muere. Usted puede simplemente reproducir una animación de muerte en el personaje y deshabilitar todos los scripts que usualmente se utilizan en la lógica del enemigo. Probablemente tendrá que encargarse de quitar varios scripts, agregando una lógica personalizada para asegurarse de que nadie vaya a continuar de atacar el enemigo, y otras tareas de limpieza.

Un mejor acercamiento es inmediatamente borrando el personaje entero y remplazarlo con un prefab instanciado destruido. Esto le da mucha flexibilidad. Puede utilizar un material diferente para el personaje muerto,adjuntar scripts completamente diferentes, generar un prefab conteniendo el objeto roto en muchos pedazos para simular un enemigo destrozado, o simplemente instanciar un prefab que contiene la versión del personaje.

Cualquiera de estas opciones pueden ser logradas con una sola llamada a Instantiate(), solamente tiene que adjuntarlo en el prefab correcto y está hecho!

La parte importante que recordar es que los restos que usted Instantiate() pueden ser hechos por objetos completamente distintos al original. Por ejemplo, si usted tiene un avión, va a modelar dos versiones. Una en la que el avión consista de un solo GameObject con Mesh Renderer y scripts para la física de avión. Manteniendo el modelo en un solo GameObject, su juego va a correr más rápido ya que va a poder hacer el modelo con menos triángulos, y como tiene menos objetos, va a representarse más rápido que usando partes pequeñas. Ademas, mientras su avión esté felizmente volando por ahí, no hay razón para tenerlo en partes separadas.

Para construir un Prefab de un avión en ruinas, los pasos típicamente son:

  1. Modele su avión con muchas partes diferentes en su modelador favorito
  2. Cree una escena vacía
  3. Arrastre el modelo a la escena vacía
  4. Agregue Rigidbodies a todas las partes, seleccionando todas las partes y escogiendo Component->Physics->Rigidbody
  5. Agregue Box Colliders a todas las partes seleccionando todas las partes y escogiendo Component->Physics->Box Collider
  6. Para un efecto especial extra, agregue un Particle System de humo como un GameObject hijo a cada una de las partes.
  7. Ahora tiene un avión con múltiple partes explotadas, que caen al piso por física y van a dejar un camino de partícula(Particle Trail) gracias al Particle System adjunto. Oprima el botón de jugar (reproducir/play) para obtener una vista previa de cómo reacciona su modelo y hacer cualquier ajuste necesario.
  8. Escoja Assets->Create Prefab
  9. Arrastre el GameObject raíz que contiene todas las partes del avión al Prefab.

El siguiente ejemplo le muestra cómo estos pasos son modelados en código.

// JavaScript

var wreck : GameObject;

// Como ejemplo, convertimos el game object en un naufragio después de 3 segundos automáticamente 
function Start () {
     yield WaitForSeconds(3);
     KillSelf();
 }

// Llama al método de disparo al mantener presionado ctrl o mouse
function KillSelf () {
    // Crea una instancia del game object del naufragio en la misma posición en la que estamos
     var wreckClone = Instantiate(wreck, transform.position, transform.rotation);

    // A veces necesitamos llevar más de algunas variables de este objeto
     // al naufragio 
     wreckClone.GetComponent.<MyScript>().someVariable = GetComponent.<MyScript>().someVariable;

    // Matarnos a nosotros mismos
     Destroy(gameObject);


// C#

public GameObject wreck;

// Como ejemplo, convertimos el game object en un naufragio después de 3 segundos automáticamente
 IEnumerator Start() {
    yield return new WaitForSeconds(3);
    KillSelf();
 }

// Llama al método de disparar al mantener presionado ctrl o mouse 
void KillSelf () {
    // Crea una instancia del objeto del juego de naufragio en la misma posición en la que somos
    GameObject wreckClone = (GameObject) Instantiate(wreck, transform.position, transform.rotation);
    
    // A veces necesitamos llevar más de algunas variables de este objeto
    // al naufragio
    wreckClone.GetComponent<MyScript>().someVariable = GetComponent<MyScript>().someVariable;
    
    // Matarnos a nosotros mismos
    Destroy(gameObject); }
}

Poniendo un montón de objetos en un patrón específico

Digamos que quiere colocar un montón de objetos en patrón de una cuadricula o circulo. Tradicionalmente esto hubiera sido hecho por ambos:

  1. Construyendo un objeto completamente por código. Esto es tedioso! Escribiendo valores de un script es a la vez lento, poco intuitivo y no vale la pena la molestia.
  2. Hacer el objeto completamente ensamblado, duplicarlo y ponerlo muchas veces en la escena. Esto es tedioso, y colocar objetos con precisión en una cuadrícula es difícil.

Entonces use mejor Instantiate() con un Prefab! Nosotros pensamos que usted tendrá idea del por qué los Prefab son tan útiles en estos escenarios. He aquí el código necesario para estos escenarios:

// JavaScript

// Instancia un prefab en un círculo

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#
 // Instancia un prefab en un círculo

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

// Instancia un prefab en una grilla

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#

// Instancia un prefab en una grilla

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


Guardando Su Trabajo
Input