Hello, World! en Unity3D

Este es un tutorial de introducción a Unity3D, una herramienta que nos permite desarrollar aplicaciones 3D con una herramienta integrada, que ademas nos ayuda a exportar nuestra obra a muchas plataformas distintas, como PC/MAC, IOS, Android, etc. Para ello haremos una versión del Simon pero usando cubos.

Empecemos por descargarla desde Aqui. Podemos bajar una versión de prueba de 30 días con acceso a las capacidades pro. Pero de todas formas, no usaremos, en este tutorial, ninguna de estas características.

Un vez que lo hallamos descargado, lo instalamos y ejecutamos. El editor se nos abrirá con un proyecto de ejemplo y una ventana de bienvenida. Cerramos la ventana de bienvenida, y creamos un nuevo proyecto, Menú File -> New Project, le ponemos un nombre que nos guste, como, SimonPower. No importamos ningún paquete y le damos a Create Project.

Bien, ahora estamos sobre nuestro lienzo en blanco, vamos a aprender un poco a desenvolvernos en él.

Aquí puedes ver la barra superior que nos permite acceder a algunas de las funciones mas básicas del entorno.

  1. Barra de herramientas de transformación, en orden nos permiten: arrastrar la vista, trasladar, girar y escalar.
  2. Modificadores a como se aplican transformaciones
  3. Botones de juego, el play nos permite ejecutar el escenario actual en la vista de Game. El pause nos permite parar la simulación y el último botón pasar al siguiente frame.
  4. Selección de vista, nos permite intercambiar entre distintas disposiciones de ventanas para el editor.

Nos hemos saltados las capas, pero no las usaremos en este tutorial, de hecho hay algunas partes importantes, como los Prefabs que no trataremos por simplicidad, y por que el objetivo aquí, es aprender a desenvolvernos con el entorno de trabajo de unity.

Esta es la vista 2 by 3 y esta separada en:

  • Scene: Vista de la escena actual, donde aplicamos la transformaciones. Podemos movernos por ella, usando una cámara en primera persona, manteniendo pulsado el botón derecho del ratón sobre ella, ahora podemos girar moviendo el ratón, o usar las teclas a, w, s y d para movernos. También podemos usar el botón central del ratón para arrastrar la vista.
  • Game: Vista de la escena actual en modo juego.
  • Project: Aquí se muestran todos los assets globales al proyecto.
  • Hierarchy: Aquí se muestran todos los objetos de la escena actual.
  • Inspector: Aquí podemos modificar las propiedades del objeto actual.

Bien ya esta bien de tantas presentaciones, vamos a empezar a jugar un poco.

En el panel “Hierarchy” haz click en “Create” y crea un “Cube”. Lo renombramos a “Base”, puedes hacerlo pulsando con el botón derecho y eligiendo “Rename” o en OSx pulsando enter.

Manteniendo la selección en “Base” en el “Inspector” ponemos la caja en la posición [0,0,0] y ajustamos la escala a [4,1,6]. Con el cursor en la vista “Scene” si pulsamos f, nos centrara en el objeto actual.

Vamos a crear los botones. El la vista “Hierarchy” creamos otro cubo y le ponemos “btn1″ como nombre. En el “Inspector” lo posicionamos en [0,0.3,1.8] y como escala [3.6,1,1].

Duplicamos 3 veces el botón, click derecho -> duplicate, y los nombramos, btn2, btn3, btn4. Ajustamos la componente Z de estos usando 0.6, -0.6, -1.8 en su orden.

Ponemos la “Main Camera” en “Hierarchy” desde arriba poniéndola en [0,6,0] con rotación [90,90,0] en el Inspector.

En la vista “Game” deberíamos poder ver un cuadro gris sobre un fondo azul. No parece un gran comienzo, pero merecerá la pena.

Vamos a darle un poco de color a los botones para poder distinguirlos. Creamos una carpeta en “Project” y la renombramos a Materials. En esta carpeta creamos un material y lo renombramos a Base. En el inspector le ponemos un color oscuro. Y lo arrastramos al GameObject “Base” en “Hierarchy”.

Creamos otros cuatro materiales para los botones verde, amarillo, azul, rojo y los arrastramos a los botones en este orden.

Vamos a hacer que los botones suenen cuando los toques. Puedes descargar de aquí los sonidos que usaremos.

Crea una carpeta Sounds en Project.
Arrastra desde el Finder o el explorador de archivos los sonidos red.wav, blue.wav, yellow.wav, green.wav a esta carpeta.
Desactiva en cada uno de los sonidos desde el inspector el 3D Sound.

Ya tenemos los assets listos, los botones también, vamos a meter código!

Creamos una carpeta Scripts en Project y dentro de esta un script Javascript que llamaremos ButtonBehaviour.

Los script en Unity desciende de la clase MonoBehaviour.
Nosotros vamos a usar scripts en js y en estos casos la herencia es automática.

Aquí está el contenido de este script:

// Necesitamos un recurso de audio que se nos mapea a "audio"
@script RequireComponent(AudioSource)

// La función Start se llama una vez al principio durante la vida del objeto.
function Start () {
  audio.loop = true;
  audio.Stop();
}

// Función que se llama cuando se pulsa un botón sobre el collider del objeto
function OnMouseDown () {
  audio.Play();
}

// Función que se llama cuando se suelta un botón sobre el collider del objeto
function OnMouseUp () {
  audio.Stop();
}

Ahora necesitamos vincular cada uno de los botones al script y a un sonido, esto lo hacemos arrastrando el script hasta el botón y haciendo lo mismo con el sonido. Repetimos con cada uno de los botones.

Podemos dándole al play ver en la vista Game como podemos tocar las barras y suena el tono mientras la mantenemos pulsada. Uuooo, ya es interactiva.

Vamos a hacer que el botón se hunda cuando esta pulsado.

En el script ButtonBehaviour vamos a añadir una función que nos permita cambiar el estado del botón:

function changeButton(val) {
  if ( val ) {
    audio.Play();
    // Podemos acceder a la posición de los objetos a través de la propiedad transform
    transform.position.y -= 0.1;
  } else {
    audio.Stop();
    transform.position.y += 0.1;
  }
}

function OnMouseDown () {
  changeButton(true);
}

function OnMouseUp () {
  changeButton(false);
}

Uuuoooo movimiento. Vamos a dejar un poco el código y vamos a jugar un poco con la luz.

En Hierarchy creamos una nueva Directional light y la colocamos un poco por encima a la derecha, y la giramos un poco hacia abajo. Ya esto va cogiendo otro color.

Vamos a poner una luz encima de los botones para que sobresalte mas el efecto.

En Hierarchy Creamos un Point Light y se lo añadimos al primer botón como hijo arrastrándolo sobre el. Lo ajustamos para dejarlo por encima del botón centrado. Luego lo vamos duplicando, y se los vamos a añadiendo a los demás botones.

Ahora de nuevo en nuestro querido código vamos a togglear las luces.

// Añadimos un propiedad para guardar la luz y no estar siempre haciendo búsquedas
var childLight : Light;

function Start () {
  ...
  // buscamos la luz y cambiamos al estado de desactivado por defecto
  childLight = GetComponentInChildren(Light);
  changeButton(false);
}

function changeButton(val) {
  // Simplemente cambiamos el estado "enabled" de la luz
  childLight.enabled = val;
  if ( val ) {
    audio.Play();
    transform.position.y -= 0.1;
  } else {
    audio.Stop();
    transform.position.y += 0.1;
  }
}

El ultimo efecto que vamos añadir son unas partículas que salgan de los botones. En Hierarchy creamos un Particle System y se lo añadimos como hijo al primer botón. Usa la config de la imagen para ajustar las partículas, puedes jugar con las opciones.

Duplica las partículas y vete añadiéndoselas a los otros botones, ajusta los colores.

Como hicimos con la luz vamos a activar y desactivar las partículas.

var childLight : Light;
var particle : ParticleEmitter;

function Start () {
  ...
  childLight = GetComponentInChildren(Light);
  particle = GetComponentInChildren(ParticleEmitter);
  ...
}

function changeButton(val) {
  childLight.enabled = val;
  // jugamos con el emit para que las partículas duren mas que la pulsación
  particle.emit = val;
  if ( val ) {
    ...
  }
}

UUuuoooo ya tenemos algo bastante resultón. Tenemos muchas de las piezas que necesitamos para poder hacer el juego, ahora vamos a empezar a juntarlas con nuestro pegamento, si, javascript.

Creamos un nuevo script en la carpeta Scripts del proyecto con el Nombre Sequence. Seleccionamos los botones y hacemos que sean hijos de “Base” ahora podremos acceder a ellos desde nuestro script con GetComponentsInChildren().

// Necesitamos una fuente de audio para el fallo.
@script RequireComponent(AudioSource)

// Aqui guardaremos las notas
var seq_ary = new Array([]);
var seq_opts = 0; // número de notas == num de botones
var current = 0; // posición en la que estamos de la secuencia
var buttons; // un array con los GameObject de los botones
var playing = false; // Si estamos en medio de la reproducción de las notas

function Start () {
  // Buscamos todos los objetos que tengan nuestro script ButtonBehaviour
  buttons = GetComponentsInChildren( ButtonBehaviour );
  seq_opts = buttons.length;
  audio.loop = true;
  audio.Stop();
  // Creamos la primera nota y la reproducimos
  createNote();
  seqPlay();
}

// Crea una nota al azar
function createNote () {
  seq_ary.Push( Random.Range( 0, seq_opts ) );
}

// Reproduce la secuencia actual
function seqPlay () {
  playing = true;

  // Esta construcción nos permite suspender el script para ejecutar los pasos
  // de la animación
  yield WaitForSeconds ( 1.1 );
  // iteramos por las notas y vamos haciendo sonar las notas
  for ( var n in seq_ary ) {
    buttons[n].changeButton( true );
    yield WaitForSeconds ( 0.4 );
    buttons[n].changeButton( false );
    yield WaitForSeconds ( 0.09 );
  }
  playing = false;
}

// Función que llamaran los botones al ser pulsados para comprobar el estado
// del juego.
function check ( obj ) {
  if ( !playing ) {
    // sigue a partir de --->>
    // si aun sigues interesado en este código es la peor forma de encontrar
    // un objeto en un array
    var c;
    var found = -1;
    for ( c = 0; c < buttons.length; c++ ) {
      if ( buttons1==obj ) {
        found = c;
      }
    }
    // --->> En found tendremos el indice del botón que nos han pasado
    if ( found != -1 ) {
      // Si es la nota que esperamos
      if ( seq_ary[current] == found ) {
        // Subimos el apuntador
        current++;
        // Y si hemos terminado
        if ( current >= seq_ary.length ) {
          // Añadimos una nota, ponemos el apuntador al principio y
          // ejecutamos la secuencia.
          createNote();
          current = 0;
          seqPlay();
        }
      } else { // Si no es la nota
        audio.Play(); // Reproducimos el sonido de error
        yield WaitForSeconds ( 0.4 ); // pausa
        audio.Stop();
        // Nuevo juego
        seq_ary = new Array([]);
        current = 0;
        createNote();
        seqPlay();
      }
    }
  }
}

// Indica el nivel en el que estamos
function level() {
  return seq_ary.length;
}

Se lo vinculamos a Base arrastrándolo desde Project a Hierarchy. Nos falta el sonido de fallo, que lo añadimos a Sounds en Project desde wrong.wav y una vez que lo tenemos allí se lo vinculamos a Base arrastrándoselo, no olvides desactivarle el 3D.

Por ultimo al script ButtonBehaviour.js en el método OnMouseUp añadimos esta linea:

// Hacemos una búsqueda global buscando el objeto Base y le enviamos el
// mensaje "check" con el parámetro this, pure OO :`)
GameObject.Find("Base").SendMessage('check',this);

Para que cuando se suelte el botón se compruebe el estado del juego.

Podemos probarlo. Deberíamos si no nos hemos equivocado en algún paso poder jugar un rato con el invento.

Vamos a darle un pequeño bonus para terminar. Vamos a mostrar el nivel en el
que estamos.

En Hierarchy creamos un nuevo GUI Text al que llamamos LevelText. Lo posicionamos en [0,1,0] desde el “Inspector” que sería la esquina de arriba a la izquierda y ponemos el Font Size en 24.

Ahora creamos un Script en la carpeta de siempre llamado LevelCounter.

// Este objeto requiere un GUIText que se vincula a guiText
@script RequireComponent (GUIText)

var sequence;

function Start() {
  // buscamos el componente Sequence del objeto Base para usarlo mas tarde
  sequence = GameObject.Find("Base").GetComponent(Sequence);
}

// Por fin una de las funciones mas importantes de Unity, Update, esta se
// ejecuta en cada actualización puedes usarla para un montón de cosas
// nosotros la usaremos para actualizar el nivel en el que estamos
function Update () {
  guiText.text = "Level " + sequence.level();
}

Y se lo vinculados LevelText. Ya tenemos nuestro primer mini-juego en Unity!!!

Aplausos …

Ahora Podemos construir nuestra criatura para ser ejecutada en otros sitios.

Vamos a File -> Build Settings Y podemos Construir el proyecto para ejecutarlo.

Si lo ejecutamos nos mostrara la pantalla de configuración y pulsando en play podremos disfrutar de nuestra creación en toda su magnitud.

Bueno, hemos creado nuestro primer mini juego en unity, estamos listos para hacer un GOW3 o un GTAV o un Crisys2 ;)

Si tienes el plugin de unity puedes verla funcionando aqui.

Happy coding!

  • luis aneudy heredia mariñez

    Buen post!
    Tengo una duda, he tenido problemas con esta linea:
    particle.emit = val;

    Me da este error: NullReferenceException: Objetct reference not set to an instance of an objetct

    • cartuchogl

      Supongo que con el tiempo que ha pasado, ya lo habras solucionado, pero por si acaso.

      Comprueba que el sistema de particulas es un hijo directo del botón. Ya que se carga en la función Start con:

      particle = GetComponentInChildren(ParticleEmitter);

      • JRuiz

        No se si debe ser por diferencias de interface entre el plus y el free, pero cuando quiero añadir el sistema de partículas como variable al script no me lo admite, y cuando añado el código de las partículas, deja de ejecutarme el movimiento de los botones. Quizás la discusión haya quedado obsoleta, pero estaría agradecido en recibir ayuda, estoy empezando con Unity a testear

    • Cesar Luis Nicho Diaz

      Prueba asi:
      var particle : ParticleSystem;
      particle.enableEmission = val;

  • Ferlle

    Nice tutorial, the link of the sounds is down.

    • cartuchogl

      Thanks, I fixed.

  • Federico Gallo

    Hola tengo problema en la linea if ( buttons1==obj ) {
    found = c;
    }

  • Federico Gallo

    ya lo solucione gracias if ( buttons{c}==obj ) {
    found = c;
    }