Curso

CURSO BÁSICO DE PROGRAMACIÓN
EN UNITY CON JAVASCRIPT


ÍNDICE
  1. Variables
    1.1 Tipos de Variables
  2. Funciones
    2.1 Function Start
    2.2 Function Update
    2.3 Function OnTrigger
    2.4 Function OnGUI
    2.5 Function OnMouseDown
  3. Sintaxis
  4. Nuetro primer código
  5. Vectores
  6. Rayos
  7. GUI
    7.1 Texturas y textos
    7.2 Box y buttons
    7.3 Posicionamiento
    7.4 Montando un menú
  8. Empezemos nuestro juego!
    8.1 Controller simple
    8.2 Vida y energía
    8.3 Peligros!
    8.4 Items
    8.5 Plataformas móbiles
    8.6 Muerte
    8.7 Menús
    8.8 Terminando
  9. Produciendo nuestro juego
    9.1 Build
    9.2 Listo para entregar!


    Presentación:

    Antes de empezar con el curso me gustaría decir que voy a intentar hacer este curso lo más simple y entendible posible, así que este curso es apto incluso para aquellos que no saben nada de programación. Si ya tienes conocimientos previos te aconsejo pasar directamente al apartado 4 (Nuestro primer código), aunque deberías hechar un vistazo al apartado 2: Funciones, para aprender las funciones básicas de Unity.

    Gracias por leerlo y espero que os sea útil este curso.

    ___________________________________________________

    1. Variables

    En ocasiones el concepto de variables puede ser difícil de entender, así que en mi opinión dejemonos de conceptos y explicaciones técnicas y vamos a explicar que son las variables de una forma más sencilla.

    Una variable simplemente es un dato, puede ser un número, como la munición que tienes en un juego de disparos, o puede ser una textura, como la de una pared de ladrillos, puede ser un objeto del juego, como una caja, una casa, etc. Una variable no es más que una cosa del juego que se incluye en un script, puede ser un sonido, un objeto, una textura, un valor...

    Creo que esto ha sido una explicación bastante sencilla pero si no te ha quedado claro ahora explicaré los tipos de variables.

    1.1 Tipos de variables

    1 - Int

    Una variable Int (Integer) es simplemente un valor entero, es decir, número sin decimales.

    Ej: -3, -2, -1, 0, 1, 2, 3, 4

    ¿Para qué se usa? Pues su uso puede ser muy diverso, pero lo más común suele ser para valores del juego como la munición, el dinero, etc.

    ¿Por qué estos valores són numeros sin decimales?

    Bueno, porque no puedes tener 30.74 balas para disparar, ni 21.36 monedas para gastar. Hay valores en el juego que es mejor gastar números enteros, se podrían gastar igualmente números con decimales, pero si no quieres correr el riesgo de que a un jugador le aparezcan 2.3 balas, mejor una int.


    2 - Float

    Bueno, con esta terminaré rápido dado que es lo mismo que la anterior pero con decimales. Ej: -1.243, 0.535, 7.368


    3 - Boolean

    Una boolean es una variable que solo puede tener dos valores, llámalos como quieras: 0 y 1 / verdadero o falso / esto o aquello

    En la programación se escribe que es true o false. (verdadero o falso)

    Se suelen usar para poner condiciones. Ejemplo, en un juego de disparos quieres disparar solo si tienes balas (lógico), si no tienes balas no. Pues creas una variable BOOLEAN que se llame... "poder_disparar".

    Primero: si "poder_disparar" es verdad ------ disparas

    Luego: si "munición" es mayor que 0 -------- "poder_disparar" es verdad
    si "munición" es 0 o menos ------------- "poder_disparar" es mentira

    Te lo dejo en script si no te ha quedado claro:


    4 - Double

    Estas no las gastaremos. Las double son como las int pero aceptan valores más grandes, explico: las int, solo llegan hasta 32767.

    Las int van de -32767 a 32767:


    -32767 ------------------------------ 0 ------------------------------ 32767

    si a una variable int con valor 32767 (valor máximo) le sumas +1 se va al otro extremo, es decir, vuelve a -32767.

    Por eso las variables double sirven para valores muy grandes, pero no creo que las usemos.

    VARIABLES DE UNITY

    A partir de aquí nombraremos algunas de las variables más usadas de la librería de unity (eso quiere decir que estas variables solo sirven si programas en unity)

    1 - GameObject

    Pues esto es simplemente un objeto, cualquier cosa que tengas en la escena: un cubo, una esfera, la cámara, el player, un modelo, un sonido, etc.

    Puede ser un objeto de la escena o bien un prefab que tengas en project.

    Normalmente se asignan en el inspector, pero algunas veces puedes asignarlas desde el script.


    2 – Vector

    De estos ya hablaremos más tarde que son más complicados.

    var posi : Vector3

    3 – Texture2D

    Son como el GameObject pero este es una Textura, no un objeto.


    Y bueno, aunque hay más estas son las que vamos a usar principalmente. Si me dejo alguna ya la explicaré más tarde.


    1. FUNCIONES

    Las funciones son los bloques dentro de los cuales están los comandos. Todo efecto que hace un script (que un objeto se mueva, que te baje la vida, etc) debe estar escrito dentro de un bloque, estos bloques son las funciones.

    Hay varios tipos de funciones, y cada una funciona de una manera distinta, que ahora vamos a explicar.


    2.2 Tipos de funciones



    Function Start:

    La función start se ejecuta cuando empieza el nivel en el que estás. Cada vez que entras en una pantalla se ejecuta el comando (el script) que haya dentro.






    Function Update



    La función Update se ejecuta todo el tiempo, a cada momento del juego. Está es la función más usada, aunque su código suele llevar condiciones, ejemplo:



    solo si pulsas el botón espacio saltas, solo si tienes balas disparas...



    ¿Por qué? Muy sencillo, si en Update escribes un código que sirva para disparar, por ejemplo, y no le pones la condición de que eso pase cuando le das al botón izquierdo del ratón, estarías siempre disparando, sin pulsar nada dispararías siempre.



    Aun así, algunas cosas como las asignaciones de valores se hacen sin condiciones, por ejemplo, si tienes en la esquina inferior izquierda un medidor de vida (100 HP) quieres que ese 100 cambie si te baja la vida, si te hacen daño y tu vida baja a 56, quieres que en pantalla también ponga 56, pues pondrías un código sin condiciones, en el que dices que en cada momento del juego el valor que te pone en pantalla sea igual a la variable vida.



    Para entenderlo mejor te dejo el código:






    El segundo código lo que hace es que un texto que aparece en pantalla represente la vida. Es un texto que si la vida es 100, muestra 100, si es 50, muestra 50. Esto ya lo explicaré en el apartado del GUI (Apartado 7)

    Esto tiene que ser cierto durante todo el juego, es decir, si te baja la vida de 100 a 72, el texto en la pantalla tiene que cambiar de 100 a 72. El texto tiene que ser en todo momento igual a la vida. Para eso debes incluirlo en Update.

    Si lo incluyeras en Start, el valor no cambiaría.

    Creo que esto es fácil de entender ¿no? Quizás me excedo con la explicación pero es que no quiero que luego aparezca el típico que dice "no me he enterado de nada!" ... Continuemos!


    Function OnTrigger

    Esta es una función muy interesante. Esta se ejecuta cuando un objeto entra dentro de otro. Este script lo debes asignar al objeto en el cuál se entra.

    Por ejemplo, ¿sabes cuando en los juegos vas caminando tan tranquilamente, y de golpe te sale un vídeo, o un texto, o un diálogo o lo que sea, al entrar en una habitación, o simplemente caminando? Pues esto, en la mayoría de los casos suele ser un cubo invisible (una caja enorme que no puedes ver) y que cuando entras en ella se ejecuta el script.

    Metes el cubo en la entrada a una sala, lo haces invisible, haces que sea atravesable (en el inspector, en el Box Collider le das a IsTrigger) y luego le metes un script con Function OnTrigger.

    Pero hay varios tipos de OnTrigger:

    OnTriggerEnter: Cunado entras en el objeto.

    OnTriggerStay: Mientras estás en el objeto.

    OnTriggerExit: Cuando sales del objeto.

    Si te fijas el OnTriggerEnter / Exit sería como la función Start, solo se ejecuta una vez, mientras que OnTriggerStay sería como Update, se ejecuta todo el rato (que estés dentro del objeto)

    Un aviso: si quieres que el script se ejecute cuando el jugador entra dentro del cubo, eso debes especificarlo, porque el script se ejecuta cuando detecta que algo entra en el cubo, pero en el cubo puede entrar cualquier cosa: un enemigo o simplemente un trozo de la pared, o el suelo. Por eso dentro de la función debes poner como condición que sea el player el que entra. Te dejo el ejemplo:


    Habrás visto que esta vez entre los paréntesis pone algo (other : Collider), other es el objeto que choca con el que contiene el script. Si te da igual qué objeto choque no hace falta que pongas nada, pero como en este caso quieres especificar que sea el jugador el que entre en el cubo, debes ponerlo.

    Así ves como luego ponemos como condición que el nombre del otro (other.name) sea "First Person Controller", eso en caso de que uses el First Person Controller, sino aquí iría el nombre que le pongas al player.

    IMPORTANTE: para que esta función funcione correctamente, ambos objetos deben tener un collider, y como mínimo uno de los dos debe tener un rigidbody puesto, si no, no detecta la collisión.


    Function OnGUI



    Está función, al igual que Update, se ejecuta durante todo el juego, pero ésta solo sirve para scripts que utilizen comandos del GUI, como GUI.Box (que crea un recuadro en pantalla, o GUI.Button (crea un botón).



    Todo lo que sea GUI debe incluirse dentro de function OnGUI, si lo incluyes en Update no funcionará. Ahí va el ejemplo.






    Function OnMouseDown

    Es una función muy útil. Ésta se ejecuta cuando pulsas encima del objeto que lleve este script. En caso de que sea un objeto (un modelo) debe llevar un Collider, si no lo lleva esta función no hace efecto, pero también se puede dar el caso de que sea un GUI Texture, es decir, que con esta función puedes crear botones, puedes poner en pantalla una imagen que si le das se ejecuta un Script. También existe el OnMouseUp, que es cuando pulsas en algo, al levantar el dedo del click, de la tecla del ratón.



    3.Sintaxis

    Pues bien, dado que ya conoces las variables y las funciones, antes de empezar a escribir un código te explicaré las normas de ortografía que son bastante básicas y puede que ya las conozcas. Empecemos:

    1 - Declarar una variable.

    A la hora de declarar una variable lo primero es escribir la palabra clave var, seguida de el nombre de la variable (el que tu quieras), pero recuerda que no puedes incluir espacios y que en programación la diferencia entre mayúsculas y minúsculas existe. A continuación debes detallar el tipo de variable, con dos puntos seguido del tipo de variable:

    var Nombre : int;

    2 - Comentarios

    En los scripts puedes incluir comentarios, que salen en color verde, puedes ver que he incluido unos cuántos en los ejemplos anteriores de código. Los comentarios son partes omitidas del código que el compilador ignora, en pocas palabras: que no sirven para nada.

    Pero lo comentarios pueden servir muchas veces, si quieres probar como funciona un script sin una parte del código (alomejor porque esa parte te da error, o porque no te gusta...) sería muy molesto tener que borrar esa parte del script, probarlo, y luego volverla a escribir.
    Es más fácil utilizar los comentarios para dejar una parte del script apartada por si la quieres usar luego.

    Hay dos formas de incluir comentarios:

    • Con dos barras // : esto convertirá la línia en la que estas en un comentario, desde las barras hacia deltante.
    • Con /* : todo lo que se encuentre entre /* y */ es un comentario, puedes hacer comentarios de varias línias, o solo un trozo.

    Ejemplo a continuación.


    3 - Puntualización

    Todas las línias que contengan un comando o una declaración deben terminar en punto y coma (;):

    declaración: var TROF : boolean;

    comando: Destroy (this.gameObject);

    Todas las aperturas de función o condición deben abrirse y cerrarse con { }

    if (condición) { 
    }
     function Update() {
    }

    No te olvides cuando empiezas una función de incluir los parentesis de los parámetros: function Update ( ) y tampoco te olvides en la function OnTrigger de incluir en los parametros el Collider:
    function OnTriggerEnter (other:Collider)

    4 - Asignación

    Esto es un fallo muy común cuando empiezas a programar. Si en una función asignas un valor a otro, Ej: municion = 30; solo se emplea un igual, pero dentro de una condición debes emplear dos iguales:

    if ( municion == 30)

    Si solo pones un igual te dará error. También debes tener cuidado de no asignarle, por ejemplo, un valor decimal a una variable int. Si le asignas 23.45 a un integer te dará error, ya que es solo para números enteros.


    5 - Operadores lógicos

    Esta puede ser la parte más importante de este apartado. Los operadores lógicos se usan en las condiciones, ahora verás:

    • Igual == , se utiliza para preguntar si hay igualtad entre A y B if (A == B)
    • No igual != , se utiliza para preguntar si A no es igual que B if (A != B)
    • Mayor que >, se utiliza para preguntar si A es mayor que B if (A > B)
    • Menor que <, se utiliza para preguntar si A es menor que B if (A < B)
    • Mayor o igual >=, se utiliza para preguntar si A es mayor o igual que B if (A >= B)
    • Menor o igual <=, se utliza para preguntar si A es menor o igual que B if (A <= B)
    • Or ( || ), se usa para indiciar que la función se ejecute si se cumple una de las dos condiciones: if ( A == B || A < C) solo uno de los dos tiene que ser cierto, si A = B o A < C se ejecutará el código. Para escribir || es Alt derecho + 1
    • And (&&), SHIFT + 6, se usa para indicar que la función se ejecuta si ambas condiciones se cumplen: if( A==B && A > C) ahora ambas condiciones deben cumplirse, A tiene que ser igual a B, y A tiene que ser mayor que B.

      1. Nuestro primer código

      Creo que después de todo lo explicado ya es hora de que nos dejemos de explicaciones y vayamos un poco a la acción. Vamos a empezar escribiendo un código sencillo. Te voy a enseñar a hacer un script bastante sencillo con el cuál haremos que un objeto desaparezca al pulsar un tecla, la que tu quieras.

      Primero empieza abriendo Unity y abriendo una escena nueva (new scene). Primero de todo crea una luz (GameObject > create other > Directional Light), ahora crea un cubo, luego mueve la cámara (main camera) delante del cubo, para que se vea mientras juegas. Dale al play para comprobar que cuando juegas se ve el cubo delante de ti.

      Si no lo ves bien iluminado rota un poco la directional light para enfocar bien el cubo.

      Ahora en Project, donde quieras, click derecho y "create javascript", luego dale dos veces click izquierdo al nuevo archivo que has creado y te se abrira el editor de scripts. Te saldrá algo como esto:




    Bórralo todo menos la function Update. Ahora que tenemos la función Update pregúntate: ¿Si queremos que el cubo desaparezca al darle a un botón que es lo primero que debemos hacer?

    Por supuesto: la condición, antes de escribir ningún código debemos comprobar qué condiciones queremos que se cumplan, en este caso que le demos al botón.

    Vamos a poner esto a continuación de function Update() {

    if (Input.GetKeyDown (KeyCode.Space)){

    Ahora te lo tengo que explicar. Input.GetKey es el comando que significa pulsar la tecla. Hay 3 tipos o modos:

    -Input.GetKeyDown: se ejecuta cuando pulsas la tecla.

    -Input.GetKey: se ejecuta todo el tiempo que tienes la tecla pulsada.

    -Input.GetKeyUp: se ejecuta cuando levantas el dedo de la tecla.

    El GetKey se utiliza cuando queremos que un comando se ejecute contínuamente mientras pulsamos la tecla, como caminar, que si dejas pulsado W avanzas hacia delante, pero en este caso queremos que se destruya el cubo, eso no es algo contínuo, si no algo que pasa solo una vez, por eso utilizamos GetKeyDown.

    Luego (KeyCode.Space) , KeyCode sirve para indicar que estas mencionando una tecla, simplemente ponlo y listo, seguido de punto y el nombre de la tecla.
    Aquí yo he puesto "Space" (Espacio), si quieres cambiar la tecla solo pon la que quieras. Ej: letra T ---> (KeyCode.T), flecha arriba ----> (KeyCode.UpArrow)

    Si quieres ver una lista de teclas vete a google y escribe: "Unity Script Reference", esta página te será muy útil, ahí tienes información sobre toda la programación en unity. Escribe KeyCode en la barra de buscar y te saldrá una lista de teclas.

    Fácil, ¿no? Bien, continuemos. Ya tenemos la condición, ahora la acción. Queremos que el cubo se destruya, pues a continuación de la condición escribimos:

    Destroy (this.gameObject);

    Explico: Destroy es fácil, destruir, destruye el objeto que pongas entre paréntesis, y entre paréntesis tenemos "this.gameObject", esto significa simplemente (traduciendo a lo simple) "este objeto".

    Destroy (this.gameObject); = Destruir este objeto.

    Ahora para terminar cierra la condición y la función con }


    Ahora selecciona el cubo, luego arrastra el script hasta él. Primero guarda, acuérdate siempre de guardar el script, luego dale a Play y empieza el juego. Dale a tu tecla (en mi caso espacio) y BOOM! El cubo desaparece!

    5.Vectores

    Los vectores son muy importantes en la programación sobre todo en un entorno 3D. ¿No sabes qué es un vector? Muy sencillo, un vector (de 3 dimensiones) consta de 3 valores: x,y, z. En caso de que fuera un vector de 2 dimensiones tendría dos valores: x,y. Para hacerte más fácil el aprendizaje de los vectores yo he decido dividirlos en dos tipos: Vectores de posición y vectores de dirección.

    -Vectores de posición: a veces tendrás que utilizar los vectores para indicar una posición en el juego, un sitio de tu escena, un punto. Para eso deberás utilizar un vector con la posición x, la posición y, la posición z.

    -Vectores de dirección: estos ya son más difíciles de entender. Estos se usan cuando quieres indicar la dirección en la que se tiene que mover un objeto. Si tu quieres que un objeto se mueva en el juego debes indicar con un vector hacia que dirección. Pues no son coordenadas lo que tienes que poner. En el vector de dirección solo se pone 1 o 0.

    Explico: Vector (1,0,0) x = 1, y = 0, z = 0 , esto significa que se mueve en dirección a la X. Dado que la dirección en vertical (Y) y en profundidad (Z) es nula, se dirige a (X=1). Si no lo entiendes observa la siguiente tabla:


    En esta imagen he omitido el eje Y porque es todo el rato cero.

    Como puedes ver, en el primer eje, solo X = 1, y la dirección es X.

    En el segundo Z = 1, y la dirección es Z.

    Y en el tercero que X = 1 y Z = 1, la dirección es entre ellos dos.

    Si aun así no lo has entendido ahora hablaré del uso de vecores.

    USO DE VECTORES:

    Para explicarlo usaré el transform.position y el transform.Translate.

    Transform.position es la posición del objeto que contiene el script. Si nosotros declaramos una variable tipo Vector: var posi : Vector3;

    y luego le asignamos una posición en el espacio a posi:
    posi = Vector3(34.64, 54.32, 78.98);

    Ahora ponemos en un script que la posición del objeto es posi,

    transform.position = posi;

    El objeto se irá a esa posición, a las coordenadas que le hemos puesto.

    Pero ahora en cambio usaremos transform.Translate, este sirve para mover un objeto, pero no a una posición, sino en una dirección. Este comando mueve un objeto hacia delante, atrás. Lados, arriba, abajo o la dirección que tu le des.

    En este caso declaramos la variable: 

                                              var direcc : Vector3;

    Asignamos: direcc = Vector3(0,1,0);

    Y luego escribimos: transform.Translate (direcc);

    Esta vez el objeto estará contínuamente moviéndose en la dirección que le hemos puesto (0,1,0), ¿Sabes que dirección es? ¿Hacia el lado? ¿Hacia arriba? ¿Hacia atrás? Lo vas a comprobar porque vamos a hacer el script.

    Vamos a hacer un script para una plataforma móbil, es decir, un objeto que se mueve en una dirección. Si tienes el cubo de antes en la escena quitale el script de destruir (si esa parte te la has saltado y no sabes de qué te hablo crea una escena, con una luz y un cubo y pon la main camera enfrente del cubo para que se vea al jugar) y crea un nuevo script. Vamos a escribir.

    Empieza poniendo la function Update. Dentro de esta vamos a poner la condición de que el objeto solo se mueva si nosotros pulsamos el botón. En este caso usaremos los botones de flecha arriba y abajo.

    Escribe esto:

    if (Input.GetKey (KeyCode.UpArrow)){

    A continuación escribes lo que hemos nombrado antes:

    transform.Translate ( Vector3(0,1,0) );

    Ahora cierra solo la condición, no el Update, con } , y pones a continuación otra condición:

    if ( Input.GetKey (KeyCode.DownArrow)){
    transform.Translate ( -Vector3(0,1,0) )
    }

    Ves que delante de Vector3 he puesto un negativo (-), eso es para que al pulsar la flecha abajo se meuva en la dirección contraria a si pulsas la flecha arriba.

    Y ya cierras todo, cierras la función, te quedaría algo así:


    Prueba a meterle el scritp al cubo y dale al play. Ahora pulsa las teclas de flecha arriba y flecha abajo. El cubo efectivamente se mueve pero para mi gusto un poco demasiado deprisa así que vamos a fijarle una velocidad.

    Esto se introduce dentro de transform.Translate:

    transform.Translate ( Vector3(x,y,z) * AQUI );

    Y vamos a poner esto:




    Y pensarás: ¿Qué es eso de Time.deltaTime? Parece muy complicado pero es muy sencillo. Time.deltaTime significa "por segundo".

    Lo que quiere decir que ahora se moverá a una unidad por segundo. Prueba, guarda y dale al play. Puedes comprobar que se mueve más lento.

    ¿Y si quieres que vaya más deprisa o más lento? Pues entre el vector y el Time pones multiplicar por un número:

    Vector3(0,1,0) * 2 * Time.deltaTime //Ahora va el doble de rápido

    Vector3(0,1,0) * 0,5 * Time.deltaTime //Ahora va la mitad de rápido

    Una última cosa sobre los vectores antes de terminar. Unity ya tiene unos vectores de dirección predeterminados:
    Vector3.forward (hacia delante), Vector3.right / Vector3.left (hacia los lados), Vector3.up / Vector3.down (arriba y abajo).

    Si quieres puedes probar el script anterior cambiando Vector3(0,1,0) por Vector3.up


    1. Rayos

    Los rayos son también una herramienta muy útil, por ejemplo, los juegos de disparos para disparar utilizan un rayo. Del arma hacia delante hay un rayo invisible que por supuesto los jugadores no pueden ver, pero está.

    Ese rayo va del arma, del punto donde disparas, hacia delante hasta que topa con algo, así que cuando disparas es el que detecta si en disparas a otro jugador o a una pared o lo que sea.

    Pero no solo eso, ¿sabes esos juegos de estrategia sobre construir ciudades? Te habrás fijado que en algunos sitios te permiten construir edificios y en otros no. Hay varias formas de hacerlo, pero una es creando un rayo desde el ratón hacia delante, y detecta si puedes o no construir.

    Bueno, estos son solo unos ejemplos, las utilidades que le puedes dar a los rayos son infinitas, lo que te se ocurra. ¿Qué te parece si hacemos un script en el cuál si pasas el ratón por encima del cubo se destruye? Creando un rayo que detecte lo que estás tocando con el ratón, y si el objeto que tocas es el cubo lo destruye.

    Para crear un rayo primero debes crear la variable var rayo : RaycastHit, y aquí tienes un nuevo tipo de variable: RaycastHit. Ahora que esa variable está creada solo tienes que poner la condición:

    if (Physics.Raycast ( punto_de salida, dirección, rayo, distancia))

    Eso que he puesto entre paréntesis son los parámetros, es decir los datos que tienes que dar para crear la condición.

    Si algo no te queda claro espera que luego pongo la imagen del código y lo verás más claro.

    1 – Punto de salida y dirección

    Aquí deberiamos poner primero el objeto o punto desde donde sale el rayo, y luego un vector de dirección que diga si el rayo sale hacia delante, hacia atrás, hacia los lados, etc.

    Nosotros en vez de poner punto de salida y dirección pondremos un ray, un ray es un tipo de variable, que ya indica punto de salida y dirección. Igual que un vector indica x, y, z, un rayo indica punto y dirección. Total, que creamos otra variable así:
    var rayo_camara : ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    2 – Rayo. Aquí va el nombre de la variable que hemos creado (var rayo : RaycastHit). Nombre: rayo.

    3 – Esto es la distancia que tiene el rayo, es decir, el rayo puede llegar hasta 10 unidades de lejos, 100, 1000... Si quieres que el rayo sea infinito escribes Mathf.Infinity.

    Por tanto quedaría así:



    Habrás visto que las variables están DENTRO de Update, no fuera. Eso sinceramente da igual, la única diferencia es que si está fuera se puede usar en cualquier función: en Update, en Start, en OnTrigger... Pero si la variable se declara dentro de Update, solo se puede usar en Update. Si intentas utilizarla en otra función te dirá que la variable no existe.

    Esta condición que hemos puesto quiere decir que el código se ejecutará si detecta colisión. Pero no nos basta con eso, nosotros queremos que el cubo se destruya si le pasamos el ratón por encima, así que ahora tenemos que poner como condición que la colisión sea con el cubo.

    Así que ahora, dentro de la condición ponemos esto:

    if ( rayo.transform.name == this.gameObject.name)

    Traduzco.
    rayo.transform.name significa: "el nombre del objeto que tocamos"
    this.gameObject.name significa: "el nombre de este objeto"

    Por tanto la condición es:

    Si el nombre del objeto que tocamos = al nombre de este objeto....

    (El nombre de este objeto es el que lleva el script, si el script se lo pones al cubo, this.gameObject.name será igual a "cube")

    Y luego dentro de esta condición pones el Destroy:

    Destroy (this.gameObject);

    Y quedará algo como esto:


    Ponle el script al cubo, dale a Play y prueba de pasar el ratón por encima el cubo.

    Y terminamos con el apartado de los rayos! Pero antes quiero hacer una traducción del código que acabamos de hacer por si ayuda a entenderlo:

    si (el rayo choca con algo)
    si (y este algo es este objeto (el cubo) )
          Destruye (este objeto)
    }


    1. GUI

    La GUI es la interfaz, todo lo que sale en pantalla como imagen 2D, y que no es un objeto del espacio 3D.

    7.1 Texturas y Textos

    Esta parte la dedicaré a los GUITexture y GUIText que como ya sabes puedes crearlos desde Unity (GameObject > Create other > GUI Texture / GUI Text).

    Dado que estos son fáciles de crear y configurar desde Unity me limitaré a explicar algunas aplicaciones en la programación.

    Poniéndole un script a un GUI Texture o Text puedes cambiar cosas como el tamaño, color, posición en pantalla... Primero de todo te enseñaré como crear un script para cambiar cualquier propiedad de la Textura / Texto.

    Dentro de la función Update debes incluir esto:

    Para GUI Texture: this.guiTexture + .propiedad
    Para GUI Text: this.guiText + .propiedad

    Con "propiedad" me refiero al parámetro de la textura o texto que quieras cambiar, algunos parámetros de una textura son el color, el tamaño o la posición en pantalla.

    Pero el que usaremos nosotros, el más útil y más común será el pixelInset:

    this.guiTexture.pixelInset

    Te permite cambiar 4 parámetros: anchura, altura, posición horizontal y posición vertical. Para ello lo igualamos a esto:

    = Rect ( posición X, posición Y, ancho, alto);

    Así de simple, con el siguiente código puedes cambiar la posición y grandaria de una imagen:


    Ahora prueba a hacerlo, crea este script y pónselo a un GUI Texture, luego cambia la X, Y por un número. Eso será la posición en pantalla. Ten en cuenta que el punto 0, 0 es la esquina inferior izquierda de la pantalla, a partir de ahí cuanto mayor sea la X más a la derecha estará. Y cuanto mayor sea la Y más arriba estará.

    Luego en el apartado de posicionamiento te enseñaré como poner un GUI en posiciones concretas de la pantalla.

    Ahora te enseño sobre el GUI Text. Un GUI Text lo puedes posicionar igual que la GUI Texture, solo que en vez de pixelInset es pixelOffset, y en vez de Rect es Vector2:



    Como puedes ver esto es bastante fácil. Y ahora te enseño como cambiar el texto de un GUI Text desde script, puede que te preguntes ¿Para qué?, si el texto ya lo puedes cambiar desde el inspector.

    Pues hay variables, como la vida, la energía o la munción que van cambiando a lo largo del juego, entonces si creas un GUI Text para que te lo muestre en pantalla (EJ: 100 HP), la vida no será siempre 100, si te baja la vida el texto también tiene que cambiar. Para esto en la función Update escribes que el texto = HP, por tanto todo el rato, durante todo el juego, el texto será igual a la variable de la vida. Si la vida es 100, el text será 100, si la vida es 56, el text será 56. Si ya lo tienes claro procedo a explicarte el código:

    this.guiText.text // usas .text para seleccionar el texto del guiText.

    = variable.ToString();

    Igualarlo al nombre de la variable + .ToString()

    .ToString() lo que hace es pasar el valor de numérico a texto, es decir, HP (la variable de la vida) puede ser int o puede ser float, pero en todo caso es un valor numérico, y tu estás intentando meter un valor numérico en un texto, eso te daría error.

    Al usar .ToString despues del nombre de la variable pasas el valor numérico a carácteres (texto). Por tanto:

    this.guiText.text = HP.ToString();
    este texto = La variable HP

    Prueba este script, supongo que a estas alturas ya sabrá lo que hacer, pero te lo explico. Además de hacer que el texto que lleva este script indique la variable HP, luego si pulsas espacio le resta 10 de vida.

    Aprobecho para explicar que si quieres restar o sumar a una variable es:

    variable += o -= cantidad -----------> HP += 20 (Vida + 20)

    Le pones el script anterior a un GUI Text y compruebas que el texto de convierte todo el rato en la variable HP, y si pulsas espacio la variable cambia y el texto también.

    7.2 Box y Buttons

    Pues ahora entramos en el tema de los recuadros y los botones de Unity.

    Como ya explique en el apartado 2, la función OnGUI sirve solo para crear las imágenes de pantalla que tiene Unity, como son la GUI.Box o los botones.

    La GUI.Box es simplemente un recuadro. En muchos juegos cuando abres el menú te sale un recuadro en pantalla con varias opciones (continuar, salir, video, sonido...). Pues la GUI.Box es un recuadro como ese. La forma de crear un GUI.Box es esta:

    (Esto dentro de la función OnGUI, no Update)

    GUI.Box (Rect ( X, Y, ancho, alto), "Título del recuadro");

    Lo de "Título" es lo que te sale escrito arriba-centro del recuadro, pero esto es opcional, si no quieres que salga nada deja las comillas y no pongas nada: ""

    Y ahora los botones. Lo botones se crean como si fueran una condición:

    if (GUI.Button (Rect (X,Y,ancho,alto), "botón")){
    //Lo que pasa si pulsas el botón.
    }

    Los botones son un cuadro, que hace un pequeño efecto si lo pulsas, como muchos botones, y que dentro puede tener texto o una imagen:

    if (GUI.Button (Rect (X,Y,ancho,alto), "texto"))

    if (GUI.Button (Rect (X,Y,ancho,alto), imagen))

    La imagen tiene que ser una variable tipo texture que hayas declarado anteriormente: var imagen : Texture; y asignada en el inspector.

    7.3 Posicionamiento

    Pues ahora que ya sabes crear imágenes y textos en pantalla, recuadros y botones, te voy a enseñar como colocarlos adecuadamente en pantalla, es decir, el medidor de la salud quieres que esté siempre abajo en la esquina.

    Cuando pones una imagen en pantalla, si indicas las coordenadas con números (X = 200, Y = 150) te encontrarás con un problema, y es que al cambiar la resolución de la pantalla el icono se habrá movido o ni siquiera saldrá en pantalla.

    Por tanto, para posicionar una imagen en la pantalla y que siempre se encuentre en un punto determinado de la pantalla (como es la esquina inferior derecha) gastaremos siempre que sea posible Screen.width y Screen.height (Ancho de pantalla y Alto de pantalla).

    Por ejemplo, creas una GUI Texture, y le pones un script para que en todo momento esa imagen esté en el centro de la pantalla. Como quieres que esté en el centro exacto de la pantalla, mi pregunta es (e intenta pensarlo) cuáles serán las coordenadas X, Y?

    X = Screen.width / 2 es decir: X = al ancho de la pantalla / 2
    Y = Screen.height / 2 es decir: Y = al alto de la pantalla / 2

    Creo que esto es fácil de entender, si divides todo lo ancho que hace tu pantalla por la mitad, te quedas justo en el medio de la pantalla (el centro).

    Puedes probar de crear un GUI Texture, y ponerle este script para comprobar que la textura está siempre en el centro de la pantalla. Pero una cosa muy importante: el OBJETO del GUI Texture debes ponerlo en la posición (0, 0, 0) desde el inspector. X = 0, Y = 0, Z = 0.

    Si quieres experimentar, prueba de partir el ancho y alto de pantalla por más, entre cuatro, o entre diez, o entre quince, etc.

    A partir de aquí tu puedes ir pensando como posicionar las imágenes en la pantalla, te dejo algunos ejemplos para que vayas guiándote.





    1 – Centro de la pantalla, ya lo hemos explicado:

    Screen.width/2 = X , Screen.height/2 = Y

    2 – Esquina máxima inferior izquierda de la pantalla. Este es el punto 0, 0. Ahí es donde empieza la pantalla, el punto origen (0,0).

    3 – Este es igual que el 2 solo que tiene toda la pantalla de alto, es decir, se ha movido verticalmente, pero no horizonalmente. Sería:

    X = 0 , Y = Screen.height

    4 – Este es el contrario del 3, toda la pantalla de ancho, pero nada de alto:

    X = Screen.width Y = 0

    5 – Este lo tiene todo, está al máximo de altura y ancho:

    X = Screen.width Y = Screen.height

    6, 7, 8, 9 – Estas son esquinas visibles, es decir, no en la esquina exacta de la pantalla, sino tocando, cerca de la esquina, pero lo que nosotros entendemos por la esquina, que se acerca al borde de la pantalla pero no toca. Aquí es donde pondríamos los marcadores de vida, munición, etc.

    Para entender lo que vamos a hacer debes entender una cosa: como ya hemos hablado, si divides el ancho de la pantalla por la mitad, nos quedamos en el medio de la pantalla. ¿Y si lo dividimos entre tres? Pues se queda a un poco menos de la mitad. ¿Y entre 4? Pues se queda a un cuarto (1/4) de la pantalla. Míralo aquí:

    Como ves, cuanto más divides el ancho de la pantalla, más se acerca al borde izquierdo de la pantalla, aunque cada vez la diferencia es más pequeña.

    Por tanto si quieres que un GUI esté en la esquina izquierda, podrías dividir el ancho de pantalla entre... yo que sé, diez! Prueba con 10. Y para la altura es lo mismo, cuanto más dividas más se acerca al suelo, al borde de abajo. Si quieres que esté en la esquina inferior dividelo por diez también, y si quieres que esté en el borde superior dividelo por un número entre 1 y 2, como 1.5.

    Recuerdo que para que la imagen te salga en pantalla el objeto de la escena debe estar en las coordenadas tridimensionales (0, 0, 0). Sino es posible que la imagen no te salga en el sitio correcto o ni siquiera te salga en pantalla.

    Pues bueno, creo que si aun no te ha quedado claro algún detalle ahora lo terminarás de entender porque vamos a montar un menú.

    7.4 Montando un menú

    Pues ahora vamos a montar un pequeño menú, algo simple. Vamos a montar un recuadro GUI.Box con 3 botones dentro. Para los botones usa la imagen que viene junto a este tutorial en la carpeta Assets > GUI > Button.png

    Empecemos. Abre un nuevo script javascript. Primero de todo crearemos dos funciontes: function Update y function OnGUI.
    Primero de todo vamos a declarar una variable boolean (true or false) que nos diga si el menú está en pantalla o no.

    var activado : boolean = false;

    La igualamos a false, para que cuando empieze el juego sea falso, es decir, no hay menú en pantalla. Ahora tanto dentro de la función Update como OnGUI debemos poner como condición, que el menú aparezca si esta activado:

    if (activado == true) {

    // Menú

    }

    Hasta ahora tenemos esto:






    Bien, ahora vamos a crear el recuadro, como ya sabes el GUI.Box debe ir dentro de la función OnGUI, así que dentro de la condición que hemos puesto dentro de OnGUI, creamos un recuadro con estos parámetros:

    GUI.Box (Rect ( Screen.width/2 - 100, Screen.height/2 - 150, 200, 300), "");

    Te habrás fijado que a las posiciones (X,Y) les he restado respectivamente 100 y 150. Les resto a la X la mitad del ancho (200 / 2 = 100 , 300 / 2 = 150), y mira estás imagenes para entender porque lo hago:

    El recuadro tiene su punto origen 0,0 en la esquina superior izquierda, es decir, el recuadro empieza a crearse desde la esquina superior izquierda y se va expandiendo, por tanto si no le restara la mitad del ancho y alto a las coordenadas pasaría lo que pasa en la imagen de la izquierda, que el recuadro EMPIEZA en el centro de la pantalla, pero NO está en el centro de la pantalla.

    Para que esté en el centro de la pantalla, debe moverse hacia la izquierda justo la mitad de su ancho y hacia arriba la mitad de su altura, y entonces queda como en la imagen de la derecha. La verdad me ha costado bastante explicar esto porque no tenía ni idea de como hacerlo, así que si todavía no lo has entendido solo acepta que cuando haces un recuadro, para centrarlo debes restarle a las coordenadas X, Y las mitades de su tamaño.

    Ahora procedemos a crear los botones, para eso primero crea en la escena una GUI Texture, desde GameObject > Create other > GUI Texture, y asígnale desde el inspector la textura que te he dicho antes, la que viene con el tutorial en la carpeta Assets > GUI > Button.png.

    A continuación, en Project crea un "prefab" y ponle al prefab este GUI Texture con Button.png. Una vez ya tengas el prefab borra la GUI Texture de la escena.

    Explicado en pasos:

    1 – Crea un GUI Texture.

    2 – Asígnale el Button.png.

    3 – Crea un prefab.

    4 – Mete la GUI Texture dentro del prefab.

    5 – Borra la GUI Texture.

    Bien ahora volvamos al script. Creamos una nueva variable tipo GameObject.

    var boton : GameObject;

    A esta variable le tienes que asignar desde el inspector el prefab que acabas de crear.
    De momento voy a hacer que el menú se abra con la tecla Z y se cierre con X, luego ya haremos que pase con el mismo botón. Así que en la función Update, DEBAJO de la condición anterior creamos estas dos:

    if (Input.GetKeyDown (KeyCode.Z)){
    }
    if (Input.GetKeyDown (KeyCode.X)){
    }

    Si te pierdes tranquilo que luego pongo la imagen del código. Ahora pensemos, ¿qué pasa si pulsas Z? Que aparece el menú. ¿Qué pasa si pulsas X? Que desaparece el menú.

    Empecemos con la Z. Si pulsas la Z:

    1 – la boolean activado = true;

    2 – Se crean los botones, para eso usaremos el comando Instantiate. Creo que es la primera vez que te hablo de este comando. Sirve para crear cualquier objeto, se usa así:

    Instantiate ( variable , posición , rotación);
    En "variable" va una variable tipo GameObject, la que hemos creado antes.

    En "posición" la posición donde aparecerá el objeto. Recuerdo que ya dije antes que en el caso de las GUI.Textures, si queremos que salgan correctamente en pantalla debemos ponerlas en las coordenadas 0, 0, 0. Que es lo mismo que Vector3.zero.

    En rotación la rotación del objeto, pero al ser una imagen 2D esto nos da igual, así que lo dejamos en transform.rotation (esto quiere decir que tendrá la misma rotación que el objeto que lleva el script.

    Nos queda así:

    Instantiate ( boton , Vector3.zero, transform.position);

    Y como queremos 3 botones lo pondremos 3 veces, quedando así:

    Lo resumo: en OnGUI, si activado = true aparece el recuadro en pantalla. En Update si pulsas Z, activado = true (y por tanto aparece el recuadro) y se crean 3 botones.

    Puedes probarlo si quieres, pónselo a cualquier objeto y asigna en el inspector las variables. Lo pruebas y eso es todo lo que hace.

    Ahora debes declarar tres variabls más:

    var boton1 : GameObject;
    var boton2 : GameObject;
    var boton3 : GameObject;

    Y los igualas cada uno a un instantiate (se lo pones delante):

    boton1 = Instantiate (boton, Vector3.zero, transform.position);
    boton2 = Instantiate (boton, Vector3.zero, transform.position);
    boton3 = Instantiate (boton, Vector3.zero, transform.position);

    Cuando igualas un GameObject a un Instantiate, lo que estás diciendo es que ese GameObject es el que se crea con Instantiate. En este caso estás diciendo que el Boton1 es el que sea crea con Instantiate (boton, Vector3.zero, transform.position);

    Esto lo hacemos porque si luego queremos hacer referencia a esos tres botones, por ejemplo queremos destruirlos para que desaparezcan de la pantalla al cerrar el menú, ¿cómo los llamamos?

    Destroy (???);

    ¿Qué pondríamos aquí? Antes no podíamos hacerlo, una vez creados no podíamos hacerles cambios porque no tenían nombre, ahora sí, ahora pondríamos:

    Destroy (boton1.gameObject);

    Y eso es lo que vamos a hacer ahora. Dentro de la condición de si pulsas X, ponemos lo contrario a la Z. En la Z poníamos que activado = true, y se creaban boton1, boton2 y boton3. Pues ahora en la X activado = false y destruimos los botones:

    activado = false;
    Destroy (boton1.gameObject);
    Destroy (boton1.gameObject);
    Destroy (boton1.gameObject);

    Alomejor te preguntas: ¿por qué ponemos Destroy (boton1.gameObject) y no Destroy (boton1) simplemente??

    Muy fácil, si pones Destroy(boton1); lo que estás destruyendo es la variable, y si pones Destroy(boton1.gameObject); lo que destruyes es el OBJETO que lleva la variable.

    El objeto que lleva la variable (que es la GUI Texture) la puedes borrar tantas veces como quieras, pero la variable no la borres que la liamos! Cada vez que pulsamos Z necesitamos la variable, pero si al cerrar el menú (pulsar X) borras la variable, luego cuando quieras volver a abrir el menú (pulses Z) te dará error porque la variable ya no existe. ¿Entiendes, no?


    Con esto ya hacemos que el menú aparezca al pulsar Z y que desaparezca al pulsar X. Pero ahora falta poner los botones en buena posición.

    Esto lo haremos dentro de la condición de Update de si activado = true.


    Dentro de esto pondremos:

    Boton1.guiTexture.pixelInset = Rect( X, Y, ancho, alto);

    Con esto seleccionamos la GUI Texture que hay en Boton1. Y le vamos a cambiar la posición y el tamaño.

    Cuando tenemos que crear, por ejemplo un botón que va dentro de un recuadro, como es el caso, porque estos 3 botones que hemos creado van dentro del recuadro del GUI Box, tenemos que poner su posición (x,y) en referencia a la del recuadro, porque si no lo que nos puede pasar es que al cambiar la resolución de la pantalla, el botón se salga de dentro del recuadro.

    La posición X del recuadro es Screen.width/2 – 100.
    Pues empezamos poniendole al boton la misma posición.
    X del botón = Screen.width/2 – 100

    Ahora queremos que el boton esté en este sitio:


    Así que lo debemos mover un poco a la derecha. Sabemos que el recuadro tiene 200 de ancho y al botón vamos a ponerle de ancho.... 150, por ejemplo. Piensa que el botón tiene que tener menos ancho que el recuadro.

    Si queremos centrarlo, tiene que sobrarle el mismo trozo por la izquierda que por la derecha. Pensemos (que esto es más que nada pensar). Si el recuadro hace 200, y el boton 150, nos quedan libres 50. Si el boton tiene que tener el mismo trozo por la izquierda que por la derecha son 25 a cada lado:


    El que te dijo que las matemáticas no eran necesarias para programar te engañó.

    Como ves en la imagen, el recuadro tiene 200 de ancho, el boton 150, y le sobra 25 por cada lado.

    La conclusión que sacamos de esto es que el boton debe avanzar 25 unidades.

    Por tanto la posición X del boton será la misma del recuadro + 25

    X = Screen.width/2 – 100 + 25

    Y ahora para la vertical lo mismo, solo que ahora debemos tener en cuenta, que en el recuadro tienen que caber 3 botones, y que entre ellos debe haber un espacio.

    Por tanto, si el recuadro hace 300 de altura, hay 3 botones sería 100 de altura cada uno, pero como debemos contar los espacios entre ellos un poco menos de 100. Probemos con 75. Cada boton tendrá 75 de altura.

    75 x 3 = 225. 300-225 = Nos quedan libres 75. ¿Cuántos espacios hay? Uno del borde superior al primer boton, otro del pimer al segundo boton, otro del segundo al tercer boton y otro del tercer boton al borde inferior, eso son 4.
    75 / 4 = 18,75. Cada espacio debe tener 18.75. ¿Qué lío, no?

    Así que vamos ya a terminar con esto del menú. De X todos los botones deben tener recuadro + 25 -----> Screen.width/2 – 100 + 25

    Y de posición Y deben tener:

    boton 1: altura recuadro + 18,75

    boton 2: altura recuadro + 18,75 + boton1 + 18,75

    boton 3: altura recuadro + 18,75 + boton1 + 18,75 + boton 2 + 18,75






    Como resultado final quedará esto:

    Boton1.guiTexture.pixelInset = Rect( Screen.width - 75, Screen.height/2 – 141,25 , 150, 75);

    X = Screen.width/2 -100 + 25 = Screen.width/2 -75

    Y = Recuadro – 18,75 = Screen.height/2 – 150 + 18,75

    Boton2.guiTexture.pixelInset = Rect( Screen.width - 75, Screen.height/2 – 47.5 , 150, 75);

    Boton3.guiTexture.pixelInset = Rect( Screen.width - 75, Screen.height/2 + 46.25 , 150, 75);

    Como ves lo único que cambia es la Y, porque los botones están siempre en el mismo sitio solo que cada botón más arriba o abajo que los otros.

    Bien ahora te enseño el script final, lo pruebas, y ya podemos empezar a hacer nustro juego!


    Pon este script donde quieras, asígnale las variables necesarias (la de "boton" en la cuál debes poner el prefab) y dale al play. Pulsa Z para ver como aparece el menú y X para ver como desaparece.

    Aun me quedarían muchas cosas para explicar como hacer aparecer y desaparecer el menú con el mismo botón, hacer que pase algo al pulsar los botones, o el hecho de que si pulsas varias veces Z se crea muchas veces el menú... pero para no liar más las cosas explicaré todo esto cuando hagamos el menú del juego. De momento lo básico que es crear GUI y posicionarlos ya lo sabes.




    1. Empezemos nuestro juego!



    8.1 Controller Simple



    Pues empezemos! Después de todo lo que te he explicado espero que puedas seguir la creación de nuestro juego e intentaré explicarlo lo mejor y más simple que pueda aunque reconozco que hay cosas que me cuesta explicar.



    Vamos a hacer un juego sencillo de una nave que tu vas a controlar y que debe esquivar meteoritos y disparar a los enemigos. Yo solo te voy a enseñar a hacer un nivel, el sistema del juego y el menú, es decir, no vamos aquí a montar todos los niveles del juego y dejarlo listo para entrega, eso ya luego si tu quieres hacer terminas el juego y le añades todo lo que tu quieras.



    Primero de todo necesitaremos el modelo de una nave, puedes usar el que quieras, yo me he descargado este:




    Una vez tengas todo inicias un nuevo proyecto, o si ya tenías uno empezado te sitúas en ese. Creas una nueva escena. Siempre que crees una nueva escena, antes de empezar te aconsejo asegurarte de varias cosas:



    1 – La luz: GameObject > Create other > Directional Light



    2 – El Skybox: Edit > Render Settings y en el inspector, en Skybox, seleccionas un skybox.

    Para los que no sepan lo que es el Skybox es simplemente el cielo. El cielo en nuestra pantalla. Del cuadro que te saldrá busca algun cielo azul normal, ve probando.

    3 – Mapa: en este caso no crearemos mapa porque las naves se mueven en el espacio pero si quisieras un terreno: Terrain > create terrain. Y a continuación deberías aplicarle al mapa la grandaria deseada (en Terrain > set resolution) y la textura que quieras para el mapa (en el inspector).

    Y ya que vamos a hacer un juego del espacio te recomiendo usar este Skybox:
    Lo puedes descargar gratuitamente desde la asset store de Unity, solo debes estar registrado en la página, que es gratis también. Lo descargas desde Unity y le das a import.
    Ahora desde Edit > Render Settings, seleccionas Skybox y el asset importado lleva el nombre de "DSGWP", lo seleccionas en el cuadro de los skybox.

    Bien! Si ya tienes una escena con luz y cielo vamos a importar la nave. Importa el modelo de la nave en una carpeta tuya propia en Project y metela en la escena. Te aconsejo poner la nave en la escena, asignarle la textura y el tamaño deseado, y entonces crear un prefab, así metes la nave ya preparada dentro del prefab y cada vez que quieras situar una nave en la escena no tendrás que volver a seguir todos los pasos, valdrá con arrastrar.

    Si ya tienes el skybox puesto y la nave en su sitio deberías tener algo como esto:



















    Ahora procederemos a hacer que la cámara siga a la nave de un modo muy sencillo y sin necesidad de programar: en hierachy arrastra la cámara (Main Camera) hasta la nave, de manera que la camara sea un sub-objeto de nave.

    Al hacer esto la posición de la cámara será relativa a la de la nave, es decir, que si tu en el inspector, en position, en X pones 50, la cámara estará horizontalmente a una distancia de 50 unidades de la nave, eso significa que la nave es el punto origen, el centro de la cámara, el punto (0,0,0). Ahora nosotros queremos que la cámara esté justo detrás de la nave, para que nos la muestre en todo momento del juego desde atrás. Para ello, una vez la cámara es un sub-objeto de la nave, la seleccionamos (la cámara) y en el inspector, en position ponemos 0 para la X, 0 para la Y, 0 para la Z, es decir la situamos en el (0,0,0).

    Normalmente al hacer esto la cámara se situará en el mismo sitio que la nave, es decir tendrás la cámara DENTRO de la nave. Esto no es siempre así, depende del modelo que uses, pero normalmente si situas un sub-objeto en el punto (0,0,0) se sitúa dentro del propio objeto.

    Ahora bien, no queremos la cámara dentro de la nave, sino detrás, así que simplemente seleccionas la cámara y la mueves hacia atrás, y luego la rotas un poco hacia abajo para señalar la nave. Te aseguras que en la pantalla del juego se vea la nave por atrás, así:



















    De este modo tan simple, si la nave se mueve, la cámara también se moverá con ella, porque al estar la cámara dentro del objeto "nave" se les aplican las mismas transformaciones, esto significa que si mueves nave, mueves cámara, si haces más grande la nave, haces más grande la cámara, si borras la nave, borras la cámara, etc.

    Posiblemente, hecharía la cámara un poco más atrás, la he puesto demasiado cerca, eso ya a tu gusto.

    Pues ahora sí que llega la hora de programar. Ya teniendo nuestro nivel y nuestro player creado lo que necesitamos es que se mueva! Bien, haremos que la nave por si sola se mueva hacia delante constantemente, nosotros lo que hacemos es dirigirla de lado a lado y de arriba abajo.



    Antes de empezar a programar quiero recordar que cualquier duda que tengas sobre los comandos en Unity los puedes consultar en cualquier momento en Unity Script Reference, el diccionario de la librería de Unity:

    Primero de todo creamos un nuevo script. En este script ponemos la funcion Update:

    function Update() {
    }

    Y ahora dentro de esta función ponemos el comando que mueva la nave hacia delante. El comando para mover es este:

    (objeto).transform.Translate (Vector3 * Time.deltaTime);

    Objeto es el objeto que se tiene que mover, si es el propio objeto que lleva el script el que tenemos que mover no es necesario poner nada. Dentro de los parámetros de transform.Transalte debemos poner el Vector, es decir, la dirección en que se mueve.

    Un ejemplo sería: Vector3(1, 0, 0), es decir, solo se movería en el eje X, horizontalmente. Y si pones Vector3(0, 1, 0) en el eje Y verticalmente. Por suerte Unity ya viene con unos vectores predeterminados, hacia delante es:

    Vector3.forward

    Y luego lo de Time.deltaTime significa a velocidad de uno por segundo:

    Time.deltaTime = 1 por segundo 2*Time.deltaTime = 2 por segundo

    3*Time.deltaTime = 3 por segundo 4*Time.deltaTime = 4 por segundo

    Entonces la traducción literal sería:

    transform.Translate (Vector3.forward * Time.deltaTime);

    = mover objeto hacia delante a una velocidad de 1 por segundo.


    El problema que he tenido yo es que a uno por segundo no se nota el movimiento porque va muy lento, así que yo lo he puesto a 200 por segundo, de tal forma que quedaría así:

    transform.Translate (Vector3.forward * 200 *Time.deltaTime);

    Si ya lo tienes así, guarda el script y arrástralo hasta la nave. Ahora dale a jugar.
    Lo que te puede pasar es que tu no veas moverse la nave, ¿Por qué? Porque la nave es el único objeto en la escena: nosotros notamos el movimiento de un objeto en referencia a los demás objetos de su alrededor, es decir, notamos cuando algo se mueve porque todo lo demás está quieto, pero es que no hay nada en la escena, nada a lo que la nave se acerce o se aleje. Así que para que veas como se mueve la nave vamos a meter otros objetos en la escena. Ya que más tarde tendríamos que poner meteoritos, los ponemos ahora. Yo he usado este modelo:

    Turbosquid es una buena página para encontrar modelos para tus juegos gratis, te la recomiendo. Pero debes registrarte para descargar cualquier cosa.

    Bien, voy a situar unos cuántos meteoritos alrededor de la nave:




















    Si, como yo te ha pasado que no te aparecían los meteoritos en pantalla hasta que te acercaban, es que tienes que agrandar el rango de la cámara, la seleccionas, y en el inspector > Far, lo aumentas. Yo lo tengo en 10.000

    Y ahora pasaremos a algo importante, que la nave choque con el meteorito. Cuando importas modelos querrás que la mayoría de ellos puedan chocar con la resta de objetos, y que su colisión tenga su misma forma, para ello le debes poner al modelo recién importado un mesh collider. Pero atiende, lee bien esto que es IMPORTANTE.

    Muchas veces cuando importas modelos y los metes en la escena, lo que tu tienes (lo puedes ver en la pestaña hierachy) es un objeto principal con otros objetos dentro. Hay modelos que te vienen de una sola pieza, pero en cambio hay otros que vienen a partes. Por ejemplo, te descargas un modelo de Supermán, lo pones en Unity, y luego en Hierachy tu tienes un objeto llamado "Supermán", pero dentro de este tienes otro llamado "Cabeza", y es la parte de la cabeza. Otro llamado "Brazos", y son los brazos, etc.

    Cuando tu le pones un Mesh Collider a un modelo importado, se lo debes poner siempre y solamente a los sub objetos, es decir, a "cabeza", a "brazos", pero no a Supermán, del contrario no funcionará.

    Pero vamos a aplicarlo a nuestro juego y lo entenderás mejor. Si estás usando la misma nave que yo, por ejemplo, cuando sitúo la nave en la escena tengo un objeto llamado "Shuttle", y dentro de este tengo otro objeto llamado "LOD0".

    "LOD0" es el modelo, es la "malla", por lo tanto es a este al que le tienes que aplicar el Mesh Collider, no al primer objeto, Shuttle.

    Pues vamos a hacerlo, seleccionalos LOD0 y luego a Component > Physics > Mesh Collider.

    Luego abría que hacer lo mismo con el meteorito, pero lo he comprobado y no sé porque el Mesh Collider del meteorito no funciona bien, la nave lo atraviesa, no importa. Como el meteorito tiene una forma más o menos circular podemos usar un Sphere Collider, así que seleccionamos el meteorito y Component > Physics > Sphere Collider.

    Y si ya has puesto collider tanto a la nave como al meteorito ahora deberían chocar, vamos a comprobarlo. Mueve uno de los meteoritos y ponlo en la trayectoria de la nave:


















    Pues volvamos a la programación. Ahora deberíamos hacer que puedas esquivar las rocas. Para ello deberás poder moverte de arriba abajo y de lado a lado. Vamos al script que habíamos hecho anteriormente. Debajo de lo que teníamos antes: transform.Translate, vamos a escribir que si pulsas las teclas correctas la nave se mueve. Empezemos con ir hacia arriba, debemos escribir esto: "si tecleas W o flecha arriba, la nave se mueve". Vamos a ello. Primero la condición:

    if (Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.UpArrow)){
    }


    A ver, el Input.GetKey ya está explicado anteriormente. Si lo quieres revisar está en el apartado 4: Nuestro primer código, el operador || también está explicado, significa "o":

    Si pulsas W O pulsas flecha arriba = la nave se mueve.

    Ahora dentro de esa condición ponemos el comando de movimiento:

    transform.Translate (Vector3.up * 200 * Time.deltaTime);

    Luego solo tienes que copiar tres veces más este código cambiando el "arriba" por "abajo", por "izquierda", y por "derecha". Así quedaría:

    if(Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.UpArrow)){
    transform.Translate(Vector3.up * 200 * Time.deltaTime);
    }
    if(Input.GetKey (KeyCode.S) || Input.GetKey (KeyCode.DownArrow)){
    transform.Translate(Vector3.down * 200 * Time.deltaTime);
    }
    if(Input.GetKey (KeyCode.A) || Input.GetKey (KeyCode.LeftArrow)){
    transform.Translate(Vector3.left * 200 * Time.deltaTime);
    }
    if(Input.GetKey (KeyCode.D) || Input.GetKey (KeyCode.RightArrow)){
    transform.Translate(Vector3.right * 200 * Time.deltaTime);
    }


    El problema que tenemos ahora es que si tu pulsas mucho rato hacia arriba, por ejemplo, te vas, te vas, te vas, y te apartas de la pantalla. Quiero decir, no vas a llenar toda la escena de meteoritos, no terminarías nunca... Por eso, creamos solo un "conducto", un recorrido que hace la nave, de modo lineal. Entonces no nos interesa que el jugador se vaya demasiado hacia arriba, hacia abajo o hacia los lados, queremos que la nave se mantenga dentro del espacio.

    Esto lo podemos conseguir de una forma muy sencilla. Creamos dos variables (hori y vert) tipo float, y que al inicio son 0:

    var hori : float = 0; var vert : float = 0;

    Digamos que la posición central de la pantalla para la nave es 0, puedes subir hasta 5 o bajar hasta -5. Para ello vamos a la condición de pulsar W y ponemos dentro otra condición que sea:

    if (vert < 5)

    Y en la condición de pulsar S:

    if(vert > -5)

    quedando así:

    if(Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.UpArrow)){
    if (vert < 5){
    transform.Translate(Vector3.up * 200 * Time.deltaTime);
    }
    }
    if(Input.GetKey (KeyCode.S) || Input.GetKey (KeyCode.DownArrow)){
    if (vert > -5){
    transform.Translate(Vector3.down * 200 * Time.deltaTime);
    }
    }


    También puedes combinar los dos if de la seguiente forma:


    if( (Input.GetKey (KeyCode.S) || Input.GetKey (KeyCode.DownArrow)) && vert > -5)

    Traducción: si ( (pulsas S o Abajo) Y (vert es mayor que -5) )



    Y haces lo mismo con las condiciones de los movimientos laterales. Luego pondré una imagen del script cuando lo terminemos.

    Ahora debemos hacer que si vas hacia arriba, la variable vert aumente, para que cuando llegue a 5 no te deje subir más. Porque hemos puesto la condición de que para poder moverte hacia arriba, vert tiene que ser menor que 5.

    Entonces vert irá aumentando a medida que subimos hasta llegar a 5, y no te dejará subir más. Dentro de lo que pasa si pulsas W tenemos:

    transform.Translate(Vector3.up * 200 * Time.deltaTime);

    Pues debajo de esto pones otra línia que ponga:

    vert += Time.deltaTime;

    Con esto le sumas a vert 1 unidad por segundo. Eso significa que si pulsas W durante un segundo, vert = 1, si pulsas durante 2 segundos, vert = 2, si pulsas durante 1.5 seg, vert = 1.5 seg.

    Y en el resto pones:

    hacia abajo: vert -= Time.deltaTime;

    hacia izquierda: hori -= Time.deltaTime;

    hacia derecha: hori += Time.deltaTime;

    Importante: para poder sumarle a una variable uno por segundo (+= Time.deltaTime) la variable debe ser tipo float, no integuer, porque sumarle uno por segundo significa que si mantienes la tecla pulsada durante 0.372 seg, le sumas a la variable 0.372. No es que cada segundo le suma uno, sino que le suma CONTÍNUAMENTE mientas pulsas, y eso incluye decimales. Lo digo porque si alguna vez te da error asegúrate de que no pusiste tipo int.

    Pues aquí dejo la imagen del script final:
























    Lo que pasa es que cinco segundo es demasiado, yo he decidido cambiarlo a 2. Esto ya depende de lo ancho y alto que tenga el recorrido que hace la nave.

    Pues creo que ya va siendo hora de terminar con el apartado del Controller, ya podemos controlar nuestra nave. Solo una última cosa.

    En la velocidad a la que se mueve la nave yo tengo puesto 200. En vez de usar un número vamos a cambiarlo por una variable.

    Crea otra variable llamada "velo" tipo float o int como quieras, si la haces float podrás ajustar con más exactitud la velocidad. Pero solo creala, no le asignes ningún valor.

    Ahora cambia todos los 200 por velo. Si seleccionas la nave en la escena, ahora en el inspector, en el script te sale la variable velo, y le puedes poner el valor que quieras. Ponle un poco más rápido, yo le pondré 500. La ventaja de usar una variable para la velocidad es que luego puedes crear objetos que aumenten la velocidad de la nave, o que cuanto más avanzas más rápido se mueve la nave. Por eso se llama "variable", porque puedes variar su valor a lo largo del juego como te interese.

    8.2 Vida y energía

    Este apartado será largo. Aquí te enseñaré a ponerle vida y energía a tu nave, a mostrarlo en pantalla, con barras y también con números. Y además como la energía se tiene que gastar de alguna forma, haremos que la nave dispare.

    Primero de todo creamos un script que se llame como tu quieras, yo lo llamaré... HP. En Hp crearemos dos variables: vida y energía, tipo float que en inicio serán 100.

    var vida : float = 100; var energía : float = 100;

    Ahora deberíamos hacer que nos salga en pantalla. Hay varias formas de hacerlo, la que he usado yo es la siguiente. Con estas texturas:






    Esto funciona así: las barras llenas van encima de las vacías. La longitud de las llenas depende de la salud y la energía. Si la vida baja, la barra roja se empieza a hacer más estrecha y se verá como resultado el blanco de abajo, haciendo el efecto de una barra de vida que se agota, ya verás.

    Puedes cogerlas con guardar como. Lo que he hecho es crear 4 GUI Textures con estas texturas. Las he puesto todas dentro de un objeto vacío, llámalo como quieras EJ: "GUI Bars". Y le he aplicado a este objeto vacío, un script que te voy a explicar ahora.

    En primer lugar debemos hacer que estas barras se coloquen en la posición que queremos con el tamaño que queremos. Si has creado las GUI textures te deberían salir en pantalla, para que no te molesten simplemente el objeto vacío (GUI Bars) muevelo hacia un lado y las texturas desaparecerán de la pantalla. Tranquilo que en el juego te saldrán en pantalla, pero mientras no hace falta.

    Así que creamos un nuevo script, ponle un nombre. Debemos crear las variables de las barras, así que crea cuatro variables tipo GameObject:

    HPbar : GameObject;
    HPback : GameObject;
    SPbar : GameObject;
    SPback : GameObject;

    Luego en el inspector le asignas en HPbar la barra roja entera desde Hierarchy, y en HPback la barra roja con el espacio blanco. Lo mismo con SP y la barra azul.

    Ahora nos vamos a la función Update y usamos el siguiente comando:

    HPbar.guiTexture.pixelInset = Rect( x, y, ancho, alto);

    A ver, lo primero es el nombre del objeto al que te refieres. HPbar es la barra de la salud, la variable que hemos creado anteriormente. Si te quisieras referir al propio objeto que lleva el script sería this.gameObject. Luego, con guiTexture nos referimos a la guiTexture que contiene ese objeto. Nosotros hemos asignado al objeto HPbar el objeto que hemos creado, y dentro de ese objeto hay una GUI Texture, donde se encuentra la textura, la imagen de la barra. A continuación, con pixelInset hablamos de las coordenadas y tamaño de la textura. La traducción sería:

    el tamaño y posición de la textura que contiene este objeto es =

    Y ahora le asignamos una posición y tamaño. X, Y son las coordenadas de la pantalla. ¿Qué coordenadas? Esto siempre, cuando vayas a hacer cualquier juego debes pensarlo según la lógica. Considera que el punto origen, las coordenadas (0,0) están en la esquina inferior izquierda. Yo, en mi caso, he colocado las barras arriba a la izquierda, esquina superior izquierda. Para eso, primero vamos a la X. La barra no tiene que estar tocando al borde de la pantalla, no la queremos tocando el margen, sino que la queremos un poco separada. Creo que de esto ya expliqué un poco en el apartado de los GUI. En resumen, si queremos que la barra este cerca del borde izquierdo, pero no tocando, basta con ponerle un cierta distancia. Por ejemplo, 50. Ahora en la Y, si le pusieramos 50 estaría cerca de la base, de la parte inferior de la pantalla, y hemos dicho que queremos la barra arriba. Entonces hacemos lo siguiente: la colocamos en: altura de la pantalla – 50. Esto se escribe así:

    Screen.height – 50 es decir, a 50 unidades de la parte superior.

    Y las coordenadas están listas, ahora vamos con el tamaño, esto lo puedes modificar a tu gusto. Yo le he puesto ancho 200, alto 25. Tal que queda así:

    HPbar.guiTexture.pixelInset = Rect (50, Screen.height -50, 200, 25);

    Bien, con esto hemos configurado el tamaño y posición de la imagen, pero no del objeto que la contiene, el HPbar que tenemos en Hierarchy, el que has arrastrado al script. Para que la imagen se vea en pantalla ese objeto debe estar colocado en X=0 y Y=0. Si te acuerdas antes te he hecho moverlo, para que no saliera la textura en pantalla y no moleste. Ahora, con el script, vamos a hacer que mientras juegas, aparezca en esa posición, no tienes que moverlo. Para ello, vamos a crear otra línia con esto:

    HPbar.transform.position = Vector3(0,0,1);

    Con esto indicamos que la posicion del objeto HPbar es 0,0,1. ¿Por qué ese 1? Muy sencillo, en cuestión de texturas, si alguna vez tienes que poner una textura encima de otra, dos texturas en la misma posición pero una debe estar por encima de otra, como lo indicas? Con la Z. La posición Z indica que imágenes están por delante de otras. Una textura con Z = 2 está en una capa superior a otra con Z = 1.

    En este caso le he puesto Z = 1 porque la HPback la estará en la misma posición que HPbar, pero quiero que HPbar esté por encima. Y ahora estas mismas línias que acabamos de hacer, las copiamos y cambiamos HPbar por HPback. Así tendremos en la misma posición las dos barras rojas. Pero cámbiale en el Vector3 en 1 por 0: Vector3 (0,0,0) que es lo mismo que poner Vector3.zero.

    Con SPbar y SPback lo mismo, copia las línias, solo que debemos desplazarlos horizontalmente, para que estén un poco más a la derecha. Si queremos que haya entre las dos barras un espacio de por ejemplo, 50 unidades, la posición X que debemos poner es: los 50 que le pusimos a la otra barra, + el ancho de la barra + otros 50 de espacio entre las dos barras. Te hago un apaño gráfico, para que lo veas:

    |
    | 50 200 50
    |-------HPbarrabarrabarrabarra-------SPbarrabarrabarrabarra
    |
    Así que serían 50 + 200 + 50 = 300;

    En la X se las barras SP le pones 300;

    Te debería quedar así:

    HPback.transform.position = Vector3.zero;
    HPback.guiTexture.pixelInset = Rect (50, Screen.height -50, 200, 25);
    HPbar.transform.position = Vector3(0,0,1);
    HPbar.guiTexture.pixelInset = Rect (50, Screen.height -50, 200, 25);
    SPback.transform.position = Vector3.zero;
    SPback.guiTexture.pixelInset = Rect (300, Screen.height -50, 200, 25);
    SPbar.transform.position = Vector3(0,0,1);
    SPbar.guiTexture.pixelInset = Rect (300, Screen.height -50, 200, 25);


    Y una vez situada la barra tendremos que empezar con que las barras muestren la vida y la energía.

    Bien primero accedemos al otro script, en el cuál están las variable vida y energía, y pondremos delante de "var" la palabra "static" de manera que quede así:

    static var vida : float = 100;

    Con esto haremos que podamos acceder a la variable desde otros scripts. Así que volvemos al script de las barras, y cambiaremos el ancho de HPbar y SPbar por " 2 * HP.vida".

    Ves que para referirme a la variable "vida", que se encuentra en otro script (HP) basta con poner el nombre del script + punto + nombre variable: HP.vida.

    Y lo de multiplicarlo por 2, muy sencillo: vida es una variable que vale 100, máximo 100. Pero en canvio, la barra de la vida mide 200. Haces una simple regla de tres:

    100 = x Donde X es la vida que tengas en cualquier momento.
    200 = y Y es la longitud de la barra en ese momento.

    100y = 200x ----> y = 2x longitud es igual a vida por dos.

    Creo que esto es fácil. Pues ya está, si cambias el 200 de HPbar por 2*HP.vida tendría que funcionar. Lo que pasa es que tu no notarás la diferencia porque como la vida no varía. Vamos a hacer que la vida baje cinco unidades por segundo, para verlo. En el script HP donde tienes las variables de vida y energía inicia la función Update y en ella escribe:

    vida -= 5 * Time.deltaTime; // vida – 5 por segundo.

    Aquí no hace falta que escribas delante el nombre del script (HP.vida) porque ya estás en el propio script que las contiene. Ahora si guardamos y le damos al play deberíamos ver como la vida baja, viendo como la barra va bajando. Pero si te esperas verás que la vida llega a 0, y la barra sigue bajando, y bajando y se sale de la propia barra, eso es porque no le has puesto límite, y cuando llega la vida a 0, sigue bajando, a -5, -10, -15...

    Puedes solucionar esto de varias formas:

    ·Poniendo una condición que solo baje la vida si es mayor que 0:

    if (vida > 0){
    vida -= 5*Time.deltaTime;
    }

    ·Indicando que la vida = 0 si es menor que 0:

    if (vida <= 0){
    vida = 0;
    }

    Así en el momento que se convierta en menor que 0 o menor (-0.00001) al instante se convertirá en 0 y no lo dejará bajar.

    Bueno eso ya es cosa tuya que vayas jugando con los valores.

    Ahora vamos a quitar lo de bajar la vida, quítalo, y vamos a hacer que cuando disparas la energía baje.

    Crea un nuevo script llamado disparar. Más tarde haremos que la nave dispare un proyectil, pero de momento solo vamos a hacer que la energía baje si pulsas la tecla de disparar.

    Iniciamos la función Update y dentro creamos una condicion: si pulsas espacio la energía baja:

    if (Input.GetKeyDown (KeyCode.space)){
    HP.energía -= 5;
    }

    Guarda y sal. Si lo pruebas debería funcionar, pero otra vez si pulsas demasiado espacio la energía baja más de lo que debería tener permitido. Puedes cambiarlo o bien creando en el script HP que si energía <= 0, energía = 0, así no bajará nunca, o en este condición, añadir que energía debe ser mayor que 0, pero prefiero la primera forma.

    Dirígete al script HP y pon esto tanto para vida como para energía:

    if ( vida <=0){
    vida =0;
    }
    if( vida >= 100){
    vida = 100;
    }

    Haz lo mismo con vida y otra vez cambiando vida por energía. Con esto nos aseguramos que vida y energía nunca bajen más de 0 ni superen 100. Ahora si pruebas el juego y disaparas debería funcionar correctamente. Vamos a hacer ahora que la energía se recupere.

    En HP pon esta línia: energía += 3*Time.deltaTime;

    Guarda, prueba el juego y verás que la energía sube, después de gastarla. Si quieres que suba más rápida o más lenta eso ya es cosa tuya, solo cambia el 3 por otro valor. Ahora vamos a crear un proyectil que salga de la nave disparado hacia delante. Recuerdo que el proyectil luego tu puedes mejorarlo personalmente o hacer otro distinto, yo crearé uno básico.

    Crea en Project un nuevo prefab, llámalo como quieras, luego crea una esfera. La esfera por sí es muy poca cosa. Casi no se verá y tampoco es muy impresionante. En nuestro caso vamos a mejorarlo simplemente poniendo una luz. Creamos un point light, que podemos encontrar en GameObject > Create other > point light. Sitúas en hierarchy, la luz creada dentro de la esfera, como un sub objeto.

    Ahora asegúrate que la luz esté en su posición 0, 0, 0, es decir, en el centro de la esfera. Ahora ya es opción tuya hacer que la esfera se vea o no. Puedes hacer que el proyectil se vea como una esfera luminosa o como un luz sin más. En mi caso he hecho que la esfera no se vea, pero está ahí. Para eso basta con seleccionar la esfera y en el inspector desactivar la opción de "Mesh Renderer".

    Tras hacerlo queda algo como esto:



    Bien, una última cosa, selecciona la esfera y en el inspector activa la condición de "Is Trigger". Esto es para que la esfera sea atravesable.

    En un principio intenté hacer el proyectil sin activar esa opción y lo que me pasó es que al disparar el proyectil, como este sale desde la nave, colisionaba con ella. Además más tarde necesitaremos tener activada la opción para detectar la entrada de las rocas, ya lo veremos.

    Una vez activada la opción, mete el conjunto de esfera + luz en el prefab. Ahora vamos a hacer que la nave dispare. Esto hazlo dentro del mismo script que controla la nave, el que hicimos (en mi caso se llama "contnave"). Dentro de la función Update, y dentro de ésta, ponemos como condición pulsar la tecla de disparar, yo he elegido espacio. La condición sería así:

    if (Input.GetKeyDown (KeyCode.space)){

    Y si quieres por ejemplo el botón izquierdo del ratón:

    if (Input.GetKeyDown (KeyCode.Mouse0){

    Puedes encontrar una lista de teclas aquí:


    Dentro de esta condición crearemos el comando de Instanciar (crear) la bala:

    Instantiate = (bala_prefab, transform.position, transform.rotation);

    Claramente para esto debes crear, antes de la función Update, una variable tipo GameObject, llamada "bala_prefab":

    var bala_prefab : GameObject;

    Además, queremos que cuando dispares te baje la energía, ¿no? Pues debajo del Instantiate ponemos que la variable "energía" en el script HP baje:

    HP.energia -= 20;

    La cantidad es cosa tuya, elije cuánto quieres que baje la energía por disparo.

    Y arrastras, desde Project, el prefab que hemos creado hasta la variable en el inspector. Quedando todo así:

    var bala_prefab : GameObject;

    function Update(){
    // Controllador de la nave
    if (Input.GetKeyDown (KeyCode.space)){
    Instantiate (bala_prefab, transform.position, transform.rotation);
    HP.energia -= 20;
    }
    }

    Y listo, con esto disparas, pero... la bala no se mueve, porque no hemos hecho que se mueva. Crea otro script, llámalo... "mov_bala". En el haremos dos cosas: que la bala se mueva hacia delante, y que si en X tiempo no choca con nada se destruya. Para ello, iniciamos la función Update, y le ponemos el mismo comando que a la nave:

    transform.Transalte (Vector3.forward * X * Time.deltaTime);

    En X iría la velocidad de la bala hacia delante. Aquí tienes que pensar un poco: no puedes hacer que la bala vaya más lenta que la nave, porque entonces disparas y la bala se quedará atrás. No puedes hacer que la bala vaya a la misma velocidad que la nave, porque entonces la bala no llegará a los objetos que tienes en frente, sino que se moverá con la nave. Así que la velocidad tiene que ser mayor.

    Aunque también podrías hacer algo: es posible que a lo largo del juego nos interese variar la velocidad de la nave, en ese caso también debería variar la velocidad de la bala, lógico. Pues para eso podrías convertir la variable velo de la nave en "static var", porque así puedes acceder a ella desde el nuevo script: "mov_bala". Recuerdo que cuando conviertes una variable a static puedes acceder a ella desde otros script.

    Y en la X colocas esa variable velo + una cierta cantidad:

    transform.Transalte(Vector3.forward * (contnave.velo+300) * Time.deltaTime);

    Pero ahora surge otro problema, esto no te lo he explicado. Cuando conviertes una variable a static var hay el problema de que ya no te sale esa variable en el inspector. Anteriormente, para definir la velocidad que queríamos que tuviese la nave te dirigías al inspector, variable velo, y le asignabas un valor, lo escribías. Pues ahora no es tan fácil, cuando conviertes una variable en static ya no aparece en el inspector, y por tanto ya no le puedes asignar la velocidad. ¿Como solucionamos esto? De una forma muy sencilla. En el mismo script donde se encuentra velo, creas otra variable que no sea static, del mismo tipo, que se llame por ejemplo "local_velo":

    static var velo : float;
    var local_velo : float;

    Y en la función Update indicas que (velo = local_velo). Como local_velo no es static sí que aparece en el inspector, y si le puedes asignar manualmente un valor. Te recuerdo que la función Update sucede continuamente a lo largo de toda la pantalla. Por tanto, velo, que es static, será todo el rato igual a local_velo.

    Si le ponemos 500 a local_velo, velo es también 500. De esta forma, le asignamos a una variable static el valor que queremos desde el inspector. Entonces lo único que tienes que escribir es:

    velo = local_velo;

    Y todo listo. Guarda, ejecuta, y al pulsar espacio debería aparecer una bala disparada hacia delante. El script "mov_bala" resultante sería así:



    gl_velo es la variable velo, solo que yo la llamo así.

    Este script debes ponérselo al prefab de la bala, del proyectil que hayas hecho. Y en el script de disparar que es el mismo controlador de la nave, en el inspector, debes arrastrar el prefab del proyectil hasta la variable bala_prefab.

    Bueno que con esto terminamos este apartado. Ya tenemos una nave con vida y energía que se gasta al disparar, ahora vamos a por los peligros!


    8.3 Peligros

    A ver, esto es muy sencillo. Teníamos unos cuantos meteoritos en la escena ¿verdad? Pues ahora vamos a hacer que si chocas con ellos te quitan vida. Así de momento el juego consistirá en una nave que avanza automáticamente sin cesar y que debe esquivar las rocas para sobrevivir.

    Creo que ya tenías unas rocas en la escena. Bien, escoge una y borra todas las demás. Ahora con la roca que tienes, que llevaba un spherer collider que le pusimos anteriormente. Activa el trigger (seleccionas la roca > inspector > Sphere Collider > Trigger).

    Ahora con esto, la nave atraviesa las rocas, y vamos a hacer que si colisionas con una roca, la vida te baja. Para esto creamos un script, muy sencillo que diga simplemente esto: si colisionas con una roca te quita vida. Algo como esto:

    function OnTriggerEnter(other:Collider){

    if(other.name == "Sphere01"){
    HP.vida -= 10;
    }
    }

    En donde pone "Sphere01", tú tienes que poner el nombre del meteorito, es decir, el objeto que lleva el collider. En caso de que tengas el mismo meteorito que yo, el que yo puse en este mismo tutorial, el nombre será ese "Sphere01", si tuvieras otro modelo de roca pon el que sea.

    Este script debe llevarlo el objeto de la nave que tiene el collider. No podemos poner este código con el controller, porque el controller está en el objeto que contiene la nave + la cámara, para que cuando se mueve la nave, la cámara se mueva con ella. Por eso, este script, debe ir solamente a la nave, y más específicamente al objeto que tenga el collider. (Por si la nave está formada por varios objetos)

    Ahora también podríamos hacer que, ya que puedes disparar, si disparas a un roca la destruyes. Eso lo podemos hacer muy fácilmente. Necesitamos que cuando la bala colisione con la roca, esta desaparezca, puedes ver el método a continuación, pero me gustaría que intentases hacerlo tú antes por ti mismo.

    En mi caso lo hago así: en el script "mov_bala", el que hacía que la bala se moviese (hay una imagen de ese script más arriba) debajo de la función Update pongo una función OnTrigger Enter:

    function OnTriggerEnter(other:Collider){
    if(other.name == "Sphere01"){
    destroy(other.gameObject);
    }
    }

    De esta forma si la bala detecta que ha entrado en un meteorito, lo destruye. También puedes crear un efecto para cuando se destruyan las rocas. En mi caso he creado un particle system, poca cosa, unas particulas que salen disparadas cuando le das a un meteorito. Y lo haces aparecer con el comando Instantiate justo debajo de Destroy. Pero eso ya es cosa tuya.

    Bien, a ver! Los meteoritos ya están puestos, pon este meteorito que acabas de crear, con el collider, y todo en un prefab. Ahora podrías hacer algo muy sencillo y divertido que hice yo. Tienes un meteorito, lo seleccionas y lo duplicas, luego lo mueves y lo situas a una distancia del otro. Ahora tienes dos, los seleccionas, duplicas y tienes cuatro. Luego los seleccionas y los vuelves a duplicar, ya tienes 8, luego 16, 32, 64... Así los vas repartiendo por la escena.

    Te recuerdo que la nave siempre se mueve hacia delante, independientemente de los botones que pulses tú. Y que además, hicimos que solo se pueda mover verticalmente y horizontalmente una cierta cantidad, por lo que la nave se moverás linealmente a través de un recorrido, circuito o como quieras llamarlo:



    Cuadrado azul: la nave / Puntos rojos: meteoritos

    Con esto quiero decir que repartas los meteoritos por dentro de este circuito, no hay necesidad de ponerlos por fuera, en todo caso como decoración, pero eso ya es cosa tuya...

    Pues con los meteoritos ya he terminado. Pero eso son pocos peligros. Que tal unos drones (androides) que te ataquen, te disparen. Suena bien. Yo voy a usar este modelo que he encontrado:


    Primero de todo lo situamos en la escena, le aplicamos la textura y pensamos en lo que va a hacer ese drone. Pues se me ocurre que el drone estará fijo en un sitio, siempre mirándote (a la nave), y te va disparando. Si una bala te toca te quita vida, y tu puedes naturalmente destruirlos.

    Pues empezemos creando un script, yo lo llamo drone. Primero haremos eso de que te mire siempre. Para esto hay un comando en Unity llamado LookAt:

    transform.LookAt (transform)

    Donde pone transform va el transform de la nave. Por tanto, necesitamos el transform de la nave en una variable. Para esto, lo que he hecho yo es ir al script del collider de la nave, el que decía que si chocamos con una roca nos quitaba vida, ese. Y aquí creo una nueva variable static para poder acceder a ella desde el otro script:

    static var posnave : Transform;

    Y luego en función Update, especifico que esa variable tipo transform es el de la nave:

    posnave = this.transform;

    Aunque no es necesario poner "this.", automáticamente el compilador ya sabrçia que te refieres al transform de ese objeto si no pones nada:

    posnave = transform;

    Ahora volviendo al script del drone, lo único que tenemos que hacer es escribir en Update esta línia:

    transform.LookAt (naColl.posnave);

    El script que yo tengo en la nave se llama naColl. Y si quieres probarlo, guarda y dale al Play. El drone debería estar siempre mirando a la nave.

    Siguiente tarea, disparar. Para disparar no hace falta que crees otro proyectil, puedes gastar el mismo que usabamos para la nave. Solo que, luego haremos que si detecta colisión con el proyectil, te quita vida. Claro, el problema que podríamos tener es que al disparar tú con la nave, te hicieras daño con tu propio proyectil. Pues haremos simplemente que al disparar, el nombre de la bala cambia, ahora verás.

    En el mismo script del Drone, justo debajo de LookAt, haremos que el drone dispare. No queremos que dispare contínuamente, sino una bala cada cierto tiempo, me explico, queremos que dispare más bien como una pistola, no como una metralleta, así que haremos que dispare una bala cada segundo.

    Para esto, creamos una variable que se llame temp (o como quieras), tipo float, y en Update hacemos que aumente uno por segundo:

    temp += time.DeltaTime;

    Ahora escribimos una condición: si temp es más de un segundo, dispara:

    if (temp > 1){

    bala = Instantiate (bala_prefab, transform.position, transform.rotation);

    Para esto claramente tienes que crear la variable "bala_prefab", y "bala" tipo GameObject anteriormente. Y como hemos dicho antes, para que no se confunda esta bala con la de la nave le cambiamos el nombre. A continuación, dentro de la condición:

    bala.name = "Bala_Drone"

    Así, cambiamos el nombre de la bala a "Bala Drone".

    Y por último reseteamos el temporizador a 0. Porque si la condición es que el drone dispare cuando haya pasado un segundo, al disparar el temporizador vuelve a 0, vuelve a crecer hasta 1, y vuelve a disparar. Para esto a continuación de bala.name ponemos lo siguiente:

    temp = 0;

    Y cerramos condición. Hecho! Pero aun nos queda un problema. El drone nos dispara en todo momento, es decir, está bien que si lo tenemos delante nos dispare, pero lo mejor sería que si está muuuy lejos no lo haga. Para eso deberíamos indicarle que si la nave se acerca, empieze a disparar.

    Lo que yo he hecho es ponerle al Drone un Sphere Collider (Component > Physics > Sphere Collider), luego activar el Trigger, y ponerle a la esfera del collider un radio bastante grande, es decir, la esfera, el collider del drone es mucho más grande que él. Así ahora en el script ponemos que si la nave entra en el collider, empieze a disparar, y cuando salga del collider, pare de disparar.

    Para esto, creamos una variable tipo boolean que se llame disp, y la añadimos a la condición para disparar, esa condición de "if(temp>1)", ahora ponemos "if(temp>1 && disp == true), y luego fuera de la función Update escribimos:

    function OnTriggerEnter(other:Collider){
    if(other.name == "LOD0"){
    disp = true;
    }
    }

    function OnTriggerExit(other:Collider){
    if(other.name == "LOD0"){
    disp = false;
    }
    }

    Es decir: Si detecta algo entrando, y eso es "LOD0" (la nave), disp se activa, = true, y puede disparar. Si detecta la nave saliendo, disp = false, y ya no dispara.

    Recuerdo que en la condición (disp == true), no es necesario poner == true, solo con poner if(disp) es suficiente. En caso de que se quisiera hacer la condición (disp==false), sería lo mismo que (!disp).

    Bueno, todo el script quedaría así:



    Y ahora solo falta hacer que si la bala toca la nave, nos quite vida. En el script del collider de la nave, en mi caso se llama naColl.js, teníamos esto:

    function OnTriggerEnter(other:Collider){
    if(other.name == "Sphere01"){
    HP.vida -= 10;
    }
    }

    Pues a la condicón añadimos la roca:

    function OnTriggerEnter(other:Collider){

    if(other.name == "Sphere01" || other.name == "Bala_Drone"){
    HP.vida -= 10;
    }
    }


    Y listo! Terminamos así también este apartado. Ya tenemos rocas que nos hacen daño y enemigos que nos disparan, esto empieza a coger forma.


    8.4 Items

    Bueno... supongo que no hace falta explicar qué son los items: objetos. En este caso vamos a hacer un par de objetos flotantes que puedas obtener al tocarlos con la nave, uno que suba la velocidad, y otro que suba la vida.

    Primero nos descargamos estas dos imágenes que he hecho en un momento:




    Ahora vamos a Unity, y en nuestra escena creamos dos planos:

    GameObject > create other > plane

    En mi caso voy a hacer los items con planos porque ya os he enseñado a poner modelos 3D, y así aprendéis esto también, pero esto se puede hacer con modelos con el mismo método.

    Ahora arrastráis las imágenes descargadas cada una a un plano.

    Si váis a la escena veréis los dos planos con la imagen. Algo de lo que alomejor no os habéis dado cuenta es de que la parte transparente se pone negra. En estas imágenes no se aprecia porque solo el borde es transparente. Prueba a poner esta:


    Se supone que lo único pintado es la cara, y todo lo demás es transparente. Pues si la pones en un plano, lo transparente sale como negro. Para ponerlo transparente vas al inspector del plano, y en la propiedad de la textura, donde sale el cuadrito pequeño con la textura, pone Shader y al lado por defecto Diffuse. Selecciona diffuse y cambialo por Transparent > diffuse. Ahora y se verá bien.

    Ahora vamos a crear un script para que al tocar el objeto pase lo que queremos, pero para eso debemos detectar colision, y desgraciadamente el collider del plano no es muy bueno para eso. Por lo tanto seleccionamos el plano, vamos al inspector y sobre el mesh collider pulsamos "remove component", y luego le añadimos un box collider, en Component > Physics > Box collider. Normalmente ya se pone el collider al tamaño del plano automáticamente.

    Ahora pon el plano de cara a la nave. Otra cosa. Un problema con los planos es que solo se ven por un lado, si te fijas se ve la textura por un lado y por el otro el plano no se ve... fácil de solucionar. Copias el plano y lo pones mirando en la dirección contraria al otro. Es decir, los dos planos se juntan las espaldas.

    Ahora crea un nuevo script. Podrías hacer un script para cada item, pero sería demasiado, ¿no crees? Así que yo he optado por hacer un solo script, con una variable "func", y depende que valor tome, hace una cosa o otra.

    Primero de todo, para darle un "efecto" al objeto, vamos a hacer que rote, que dé vueltas. No hace falta, pero quedará bien. En function Update ponemos:

    this.transform.Rotate (Vector3.forward * Time.deltaTime * velocidad_giro)

    Yo he usado Vector3.forward, depende de como tengas colocado el plano, talvez tiene que usar otro vector (Vector3.left, up, etc) y en velocidad_giro pones lo que te parezca a tu gusto.

    Luego en function OnTriggerEnter() la comprobación de func:

    Así que ahora usamos un switch, que no me acuerdo si lo hemos usado o no, así que lo explico. Un switch es un operador que controla varios valores, es como poner: if a == 0 .. esto, if a == 1 .. esto, if a == 2 .. esto.
    Funciona así:

    switch (nombre_variable){

    case 1: // En caso de que sea 1
    Accion
    break;
    case "hola": // En caso de que sea "hola"
    .....

    ¿Qué es eso del break?

    Break es un operador que cierra bucles, cualquier bucle en el que estés: un for, un while (que creo que no los hemos dado) y un switch también.
    En cada case debes usar break porque si no lo hicieras, cuando se ejecuta el caso 1, se ejecutaria también el caso 2, 3, 4, 5, 6 etc.

    Ejemplo:

    switch (vida):

    case 25:
    ....
    case 50:
    ....
    case 75: ....

    Si no pones break, cuando la vida sea 50, se ejecutará lo del 50 y lo del 75 también, si en cambio pones el break, solo haría lo del 50.
    Esto se hace así porque en algunos casos puede interesar también que se hagan todos los de abajo.

    Por ejemplo, imagina un juego en el que tienes cuatro bolas de vida, y quieres que cada vez que te quitan una bola suene un sonido por cada bola perdida, en cada case pones reproducir sonido sin break. Lo cierto es que habría mejores y más sencillas formas de hacerlo... pero porque este no es un buen ejemplo.

    A lo que iba. En el script pones switch (func) y dos casos: case 1 y case 2.
    Si es 1, el objeto uno, por ejemplo sumar vida:

    HP.vida += 20;
    break;

    Y en el segundo sumar velocidad y break.
    contnave.thiso.GetComponent(contnave).velo += 300;
    break;

    contnave.thiso.GetComponent(contnave).velo += 300;

    Creo que esto está claro porque lo expliqué, pero por si acaso para que no queden dudas:

    Velo es la variable velocidad de la nave, que se puede cambiar desde el inspector, el problema es que necesitamos acceder a ella desde otros scripts, y si para ello la transformamos en estatica, static var, no nos sale en el inspector, lo cuál es bastane cómodo para cambiar la velocidad manualmente.
    Por eso, he hecho un apaño. Cree en contnave (que recuerdo es el script controlador de la nave) una variable static llamada "thiso" que es this gameObject, es decir la nave. Por lo tanto:

    contnave.thiso -----> la nave

    GetComponent(contnave) ---> obtener componente "contnave" que es un script.

    GetComponent sirve para obtener cualquier componente de un objeto. En este caso para obtener un componente de contnave.thiso, la nave.

    Velo -----> la variable velo de ese script.

    Resumen:

    contnave.thiso.GetComponent(contnave).velo += 300;

    = variable velo, del script "contnave" que obtenemos (Get) de "thiso" que es estatico y se encuentra en "contnave".

    Por último, fuera del bloque del switch ponemos el destroy:

    Destroy(this.gameObject)

    El Destroy lo hemos puesto ahí porque siempre se va a destruir el objeto, no importa cuál sea. Una vez lo usamos, fuera, por lo que no tenemos que ponerlo en cada case, lo pones al final una vez, y listo.

    El script resultante es este.


    Ahora sitúa los planos en el mapa y prueba que tal, técnicamente si los tocas deberían funcionar, y ya está, aparado acabado!

    Fallos comúnes:
    -No me detecta la colisión: mira si has activado el Trigger en el box collider.

    -Los dos planos contiguos (lo que hemos hecho para que se vieran por las dos caras) me rotan a distinta velocidad y hace un efecto raro:
    comprueba si en hierarchy, si has metido uno dentro de otro. No es muy buena idea, mejor pon los dos dentro de un Empty.

    Tan pronto como pueda continuaré con el curso, gracias por leer :)
    Siento la tardanza, continuaré tan pronto como me sea posible.