Diferencia entre revisiones de «Unity Scripts»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 94: Línea 94:
 
: Imaginar un juego en el que quieres que al protagonista (el cubo) siempre lo siga sin fiel robot (la esfera) a una cierta distancia...
 
: Imaginar un juego en el que quieres que al protagonista (el cubo) siempre lo siga sin fiel robot (la esfera) a una cierta distancia...
  
* Podríamos modificar el script para que al pulsar la tecla espaciador, la esfera se dirigiera a la posición del cubo a una velocidad determinada...
+
* Podríamos modificar el script para que la esfera se dirija a la posición del cubo a una velocidad determinada...
 
<syntaxhighlight lang="java" line enclose="div" highlight="6,11">
 
<syntaxhighlight lang="java" line enclose="div" highlight="6,11">
 
public class ScriptCubo : MonoBehaviour {
 
public class ScriptCubo : MonoBehaviour {

Revisión del 10:38 9 mar 2019

Introducción

  • Ahora explicaré unos conceptos básicos de programación para los que no sepan programar y quieran aprender operaciones básicas para mover-interaccionar con las figuras 3D.
Es resto de alumnos, puede ir a las secciones siguientes.



Programación básica

Nombre de los scripts

  • Cambiar el nombre => cambiar clase



Referencias dentro de los Scripts

  • Los scripts son la base del desarrollo de videojuegos.
A pesar de las herramientas que nos ofrece Unity para 'crear' una escena, el movimiento-detección de colisiones-acciones deben ser programadas para dar sentido a nuestro videojuego.
  • Los scripts son asociados a un GameObject de la escena.
Para 'asociar' un script a un GameObject debemos añadir un componente de tipo script y escoger el script creado.
También se puede 'arrastrar' el script a la ventana Inspector, estado el GameObject seleccionado.


  • Nota para programadores:
Los que sepan de programación orientada a objectos, todos los GameObjects vienen a ser Instancias de una clase en el momento el que se ejecuta el juego.
Los scripts también son clases que se instancian cuando se inicia la ejecución.
  • Dentro del script, podemos acceder a la instancia de la clase Transform y que guarda toda la información de rotación-escalado-posición del GameObject asociado.
La forma de acceder es mediante la orden: transform (con minúscula) o gameObject.tranform.
Fijarse que 'gameObject' lleva la primera letra con minúscula, indicando que hace referencia a la instancia 'actual' del gameobject asociado al script.
Por lo tanto, todas las propiedades que aparecen cuando estamos en un script hacen referencia al 'gameObject' asociado y es lo mismo poner gameObject.propiedad que propiedad.


  • Dentro de un script podemos definir propiedades que hagan referencia a otros gamebjects o componentes.
La forma de hacerlo es la siguiente:
 1 using UnityEngine;
 2 
 3 public class ScriptCubo : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private Transform esfera;
 7 
 8 	// Use this for initialization
 9 	void Start () {
10         Debug.Log(gameObject.transform.position);
11 	}
12 	
13 	// Update is called once per frame
14 	void Update () {
15 		
16 	}
17 }
  • Como vemos estamos definiendo una propiedad con el modificador [SerializedField].
Al hacerlo, dicha variable aparece reflejada en la ventana Inspector cuando asociemos el script a un GameObject.
Unity3d scripts 16.jpg
En la imagen hemos 'arrastrado' el script al gameobject Cubo (o lo podemos añadir pulsando el botón add component y buscando el script).
Como vemos, gráficamente aparece la propiedad Transform que hemos definido en el script.


Ahora solo tenemos que arrastrar un GameObject de la escena a dicha propiedad.
Unity3d scripts 17.jpg
Lo que hemos hecho es hacer que la propiedad esfera definida en el script (public Transform esfera) pase a ser el transform de la esfera.
Por lo tanto, ahora desde el script 'ScriptCubo' el cual está asociado al gameobject Cubo vamos a poder realizar cualquier operación sobre el transform del objeto esfera.


  • Por ejemplo, vamos a hacer que el cubo se sitúe en la posición de la esfera.
Para ello modificamos el script y añadimos las siguientes líneas al método update:
1 void Update () {
2 
3         esfera.position = transform.position + Vector3.up*2;
4 }
Lo que estamos indicando con esta línea es situar a la esfera 2 unidades por encima (en el eje Y) de donde se encuentre el cubo.
  • Se ejecutamos el juego y movemos el cubo en ejecución podemos comprobar como se mueve la esfera.
Unity3d scripts 18.jpg


Las posibilidades que nos brinda esto son muchas.
Imaginar un juego en el que quieres que al protagonista (el cubo) siempre lo siga sin fiel robot (la esfera) a una cierta distancia...
  • Podríamos modificar el script para que la esfera se dirija a la posición del cubo a una velocidad determinada...
 1 public class ScriptCubo : MonoBehaviour {
 2 
 3     [SerializeField]
 4     private Transform esfera;
 5     [SerializeField]
 6     private float velocidad;
 7 
 8 .....
 9 void Update () {
10 
11         esfera.position = Vector3.Lerp(esfera.position, transform.position, velocidad * Time.deltaTime);
12 }
Recordar darle una velocidad en la ventana Inspector y al ejecutar el juego, podéis comprobar como si movemos el cubo (en la ventana Scene) la esfera lo sigue.
Unity3d scripts 19.jpg



  • Al igual que hemos hecho una 'referencia' a la propiedad 'Transform' de un GameObject, podemos pasar referencias de cualquier componente de un gameobject e incluso de un GameObject completo.
Vamos a modificar el script para que al pulsar la tecla espaciador, la esfera se destruya.
Para destruir un GameObject necesitamos hacer uso del método Destroy(Object obj, float t = 0.0F).
A dicho método se le debe pasar como parámetro el objeto que queremos destruir y opcionalmente podemos indicar el tiempo que debe pasar desde que indicamos que queremos destruir el objeto hasta que se destruye realmente.
Nota: Destruir quiere decir que el GameObject se elimina de la escena (y también se libera la memoria asociada al mismo).


  • El script modificado es el siguiente:
 1 using UnityEngine;
 2 
 3 public class ScriptCubo : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private GameObject esfera;
 7 
 8 	// Use this for initialization
 9 	void Start () {
10 
11     }
12 	
13 	// Update is called once per frame
14 	void Update () {
15 
16         if (Input.GetKeyDown(KeyCode.Space))
17         {
18             Destroy(esfera, 2);
19         }
20 
21     }
22 }
  • Línea 6: Ahora la referencia va a ser un GameObject (con todos sus componentes)
  • Línea 16: Comprobamos si pulsamos la tecla barra espaciador.
  • Línea 17: Eliminamos de la escena a la esfera después de 2 segundos.



  • Con el anterior ejemplo debéis de tener claro que en Unity todo son clases (todos los componentes de un GameObject, el GameObject en si mismo,...) por lo que vais a poder obtener las referencias a dichas clases y cambiar por programación cualquier propiedad de las mismas o llamar a métodos que estén definidos en las mismas.


Podéis crear referencias a luces, a cámaras, a animaciones,...a cualquier GameObject o componente que podáis añadir a un GameObject.




Obtener una referencia de cualquier componente

  • Vimos en el punto anterior como obtener una referencia 'de forma gráfica' de un componente o GameObject.
Pero en el desarrollo del juego vamos a necesitar tener referencias a esos mismos componentes directamente en el código.


  • Para obtener una referencia a un componente de un GameObject necesitamos utilizar el método getComponent.
Un ejemplo concreto para obtener el componente Transform de un objeto sería:
1  Transform transform = MiGameObject.getComponent<Transform>();
Entre los < Tipo > indicamos el tipo de componente que queremos recuperar.


  • Aún no vimos como gestionar las colisiones entre GameObjects, pero uno de los componentes que se utilizan son los Collider.
En el ejemplo anterior, podemos ver como el cubo tiene un 'Box Collider' como uno de sus componentes.
Si quisiéramos obtener una referencia a este componente dentro del código de un script podríamos hacerlo de la siguiente forma:
1 BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
Nota:Recordar que dentro de un script podemos poner 'gameObject.' de forma optativa.
La línea anterior sería igual a poner: BoxCollider boxCollider = GetComponent<BoxCollider>();


  • Veamos un pequeño ejemplo en el que vamos a modificar el tamaño del BoxCollider dinámicamente al pulsar la tecla C.
El código del script (siguiendo el ejemplo de la sección anterior) sería el siguiente:
 1 using UnityEngine;
 2 
 3 public class ScriptCubo : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private GameObject esfera;
 7 
 8     private BoxCollider boxCollider;
 9 
10 	// Use this for initialization
11 	void Start () {
12         boxCollider = gameObject.GetComponent<BoxCollider>();
13     }
14 	
15 	// Update is called once per frame
16 	void Update () {
17 
18         if (Input.GetKeyDown(KeyCode.C)) {
19             boxCollider.size = new Vector3(3, 3, 3);
20         }
21 
22         if (Input.GetKeyDown(KeyCode.Space))
23         {
24             Destroy(esfera, 2);
25         }
26 
27     }
28 }
  • Línea 8: Declaramos la propiedad boxCollider de tipo BoxCollider que es privada por lo que no aparece en la ventana Inspector.
  • Línea 12: Cuando se inicie el script, obtenemos la referencia al boxCollider del GameObject al que está asociado el script (en el ejemplo es la esfera).
  • Línea 19: La propiedad boxCollider ahora tiene todos los métodos y propiedades del componente BoxCollider que aparece en la ventana Inspector.
Entre ellos está la propiedad 'size' que espera recibir un Vector3.


  • Si ejecutamos el código:



  • Este mismo proceso lo podemos realizar sobre los componentes de cualquier GameObject al que podamos acceder.
Por ejemplo, en el caso anterior, vamos a hacer que también se modifique el Collider de la esfera.
Si pulsamos sobre la esfera podemos comprobar que el tipo de collider de la esfera es una SphereCollider y que la propiedad para cambiar su tamaño es 'radius':
Unity3d scripts 25.jpg



Como dentro del script tenemos una referencia al GameObject Sphere podemos obtener su componente de la forma:
 1 	// Update is called once per frame
 2 	void Update () {
 3 
 4         if (Input.GetKeyDown(KeyCode.C)) {
 5             boxCollider.size = new Vector3(3, 3, 3);
 6             SphereCollider sphereCollider = esfera.GetComponent<SphereCollider>();
 7             sphereCollider.radius = 3.5f;
 8         }
 9 
10         if (Input.GetKeyDown(KeyCode.Space))
11         {
12             Destroy(esfera, 2);
13         }
14 
15     }
Nota: Se pone 3.5f para indicar que el valor es de tipo 'float', es decir, un número con decimales. Si es entero no hace falta poner el punto y Unity realiza la conversión internamente (de forma implícita).


  • Si ejecutamos el código:


  • Durante la programación de un vídeojuego os daréis cuenta de la necesidad de contar con un mecanismo que nos permite acceder a los componentes de un GameObject.
Por ejemplo, podrías tener unos soldados que al pasar a una zona abierta, fueran capaces de detectar al protagonista a una distancia mayor (por lo tanto su collider sería más grande, como hicimos en el ejemplo).



Obtener una referencia a cualquier GameObject

  • Como comento en esta wiki con el uso de Prefabs, vamos a poder necesitar, dentro de nuestro script, tener una referencia a un GameObject, que en el momento de diseño no disponemos de el, por que puede ser creado dinámicamente.


  • Para obtener una referencia podemos hacer uso de los siguientes mecanismos:
  • Instanciar los Prefabs dentro de un GameObject vacío y por tanto vamos a poder tener referencia a los componentes de este Empty GameObject.
Un ejemplo de uso es el de hacer que los enemigos surjan de un punto (un Empty GameObject) y dichos enemigos puedan tomar como punto de partida la posición del Empty GameObject.
Esta forma consume muchos recursos, por lo que si se tiene que utilizar, hacerlo siempre en el método start() y nunca en el método update().
Ya comenté en esta Wiki como asociar un Tag a un GameObject.
  • Existen otras alternativas como FindObjectOfType(Type type) o FindObjectsOfType(Type type) que devuelve un array de Objects del tipo indicado, pero que no se recomiendan utilizar por su alto consumo de recursos (si es necesario, utilizarlas, siempre fuera del método Update].


  • Otro mecanismo que se suele utilizar es hacer uso de una clase Static que tenga los GameObjects a los cuales queremos referenciar.
Dicha clase hará uso del patrón Singleton.


  • Veamos un ejemplo de uso de cada tipo.








Métodos de la clase MonoBehaviour

  • Un Script va a estar asociado a un GameObject y pasará por distintos métodos al derivar de la clase MonoBehaviour.



Awake/Start

  • Awake es un método que se ejecuta una vez al cargarse la escena, esté o no el GameObject activado.
  • Start es un método que se ejecuta sólo una vez justo después del método Awake.
Si el Script está desactivado, al activarlo se ejecutará el método Start, pero si lo activamos/desactivamos en ejecución no se volverá a ejecutar.
  • Normalmente se emplea el método Awake para obtener las referencias a otros GameObjects/Componentes que va a necesitar el script para su funcionamento.
Se suele usar el método Start para inicializar variables y cargar recursos que va a necesitar el script para funcionar, pero que mientras no esté 'activo' no es necesario cargar en memoria.
Iniciar una animación debería ir en Start ya que no tiene sentido activarla hasta que no se active el Script (por ejemplo).





Reset

  • En este método se suelen escribir las inicializaciones por defecto de las variables/referencia a componentes del propio GameoObject que tengamos en el script.
Gráficamente al pulsar sobre el botón 'reset' del script se cargarán los valores indicadas en este método.



Orden de ejecución de los scripts / Componentes




-- Ángel D. Fernández González -- (2018).