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.
Para ilustrar la fortaleza de los Prefabs, tomemos en consideración algunas situaciones básicas en dónde pueden ser útiles.
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);
}
}
}
}
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:
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.
He aquí cómo los Prefabs tienen lugar en este escenario:
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();
}
}
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:
El siguiente ejemplo le muestra cómo estos pasos son modelados en código.
// 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);
}
}
Digamos que quiere colocar un montón de objetos en patrón de una cuadricula o circulo. Tradicionalmente esto hubiera sido hecho por ambos:
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
// 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);
}
}
}