Páginas

domingo, 16 de febrero de 2014

Creando billboards en Unity

Crear un billboard en Unity es sencillo pero como todo, cuando te pones a hacerlo tiene su arte.



Para quien no lo sepa, un billboard es un plano 2D que se orienta automáticamente hacia la cámara, como estos árboles de Mario Kart.

Script

Este es mi script para billboards. He implementado tres métodos distintos.

using UnityEngine;
using System.Collections;

public class Billboard : MonoBehaviour {

public enum Metodo
{
metodo1,
metodo2,
metodo3

}

public Metodo metodo;

// Use this for initialization
void Start ()
{
if(metodo == Metodo.metodo1)
{
DoLookAt1();
}
else if(metodo == Metodo.metodo2)
{
DoLookAt2();
}
else if(metodo == Metodo.metodo3)
{
DoLookAt3();

}

}

// Update is called once per frame
void Update ()
{
if(metodo == Metodo.metodo1)
{
DoLookAt1();
}
else if(metodo == Metodo.metodo2)
{
DoLookAt2();
}
else if(metodo == Metodo.metodo3)
{
DoLookAt3();
}

}

void DoLookAt1()
{
// Tomo mi posicion y la de la camara
Vector3 cp = Camera.main.transform.position;
Vector3 op = transform.position;

// Proyecto las posiciones en el plano XZ
Vector3 cp2 = new Vector3(cp.x, 0, cp.z);
Vector3 op2 = new Vector3(op.x, 0, op.z);

// Muevo el objeto y la camara a las posiciones en XZ
transform.position = op2;
Camera.main.transform.position = cp2;

// Hago un lookAt
transform.LookAt(Camera.main.transform);

// Restauro las posiciones
transform.position = op;
Camera.main.transform.position = cp;

}

void DoLookAt2()
{
// Copio la rotacion de la camara
transform.rotation = Camera.main.transform.rotation;

// Pongo el objeto mirando al lado contrario
transform.Rotate(0, 180, 0);

}

void DoLookAt3()
{
// Tomo los angulos de euler de la camara y oriento el objeto
Vector3 ce = Camera.main.transform.eulerAngles;
transform.eulerAngles = new Vector3(0, ce.y, 0);

// Lo pongo a mirar en sentido contrario
transform.Rotate(0, 180, 0);

}
}


Método 1


// Tomo mi posicion y la de la camara
Vector3 cp = Camera.main.transform.position;
Vector3 op = transform.position;

// Proyecto las posiciones en el plano XZ
Vector3 cp2 = new Vector3(cp.x, 0, cp.z);
Vector3 op2 = new Vector3(op.x, 0, op.z);

// Muevo el objeto y la camara a las posiciones en XZ
transform.position = op2;
Camera.main.transform.position = cp2;

// Hago un lookAt
transform.LookAt(Camera.main.transform);

// Restauro las posiciones
transform.position = op;
Camera.main.transform.position = cp;


Lo que hace el método es transferir la cámara y el billboard al plano XZ, hacer un lookAt a la cámara y restaurar las posiciones de los objetos.




Con este método los billboards se orientan a la posición de la cámara.



En la vista, da sensación de perspectiva porque los billboards no tienen todos la misma orientación. Si eso es lo que se busca, pues ya está.


Método 2

// Copio la rotacion de la camara
transform.rotation = Camera.main.transform.rotation;

// Pongo el objeto mirando al lado contrario
transform.Rotate(0, 180, 0);


El método 2 lo que hace es poner a los billboards mirando en dirección contraria a la cámara.


Todos los billboards tienen la misma orientación.


Y la vista no da efecto de perspectiva. Probablemente esto sea lo que se busque en la mayor parte de los casos.


Ahora bien ¿Qué pasa si la cámara rota en X, o sea hace un picado? Que los árboles también miran hacia arriba. Nuevamente ésto puede ser bueno o puede ser malo, dependiendo de lo que se busque.


En la vista todos los árboles se ven de frente  pesar del picado.


Método 3

// Tomo los angulos de euler de la camara y oriento el objeto
Vector3 ce = Camera.main.transform.eulerAngles;
transform.eulerAngles = new Vector3(0, ce.y, 0);

// Lo pongo a mirar en sentido contrario
transform.Rotate(0, 180, 0);


Soluciona los problemas del método 2 porque tomamos solo el ángulo de euler Y de la cámara, lo transferimos al objeto y rotamos 180 grados en Y.


Con ésto los árboles no rotan en X y mantienen la perspectiva en el picado, lo que en este caso tendría sentido en mi opinión.

Descarga

Dejo aquí el proyecto, se abre con Unity 4.3






sábado, 15 de febrero de 2014

La buena, la fea y la mala o cómo implementar una pausa en Unity




Podríamos decir que, como en la mítica película del oeste, hay tres formas de implementar una pausa en Unity, la buena, la fea y la mala.

Empezaré por la mala.

La mala

La mala es poner el timeScale a 0, lo que provoca que se pause todo Unity, es decir el sistema de animación, el de físicas, etc. Solo podremos activar y desactivar objetos o recibir eventos.


void OnPause()
{
    Time.timeScale = 0;
    menuPause.active = true;
}

void OnResume()
{
    Time.timeScale = 1;
    menuPause.active = false;

}


Es un sistema sencillo y rápido de implementar, pero como he dicho tiene la desventaja de que no se pueden usar animaciones, por lo que el menú de pausa aparece y desaparece de golpe.

La buena

La buena es que tengamos un GameManager que sepa qué objetos forman parte del juego y que sea capaz de mandarles una señal a todos los objetos del juego para que se pausen y se despausen. Cada objeto implementará la pausa a su manera y el resto del engine seguirá funcionando como siempre.

Un objeto típico haría algo así

bool paused;

void Update( float delta )
{
    // Si estamos en pausa cortamos aquí
    if (paused) { return; }


    ...

}


void OnPause()
{
    animation.Stop();
    paused = true;

}

void OnResume()
{
    animation.Play();
    paused = false;

}


Y en el GameManager


List<GameObject> gameObjectList;

void OnPause()
{

    foreach(GameObject o in gameObjectList)
    {
        o.SendMessage("OnPause");
    }

    menuPause.animation.Play("Appear");
}

void OnResume()
{
    foreach(GameObject o in gameObjectList)
    {
        o.SendMessage("OnResume");
    }

    menuPause.animation.Play("Dissapear");

}


Este sistema requiere de una buena planificación a la hora de implementar el juego, porque hay que controlar qué se pausa y qué no, pero a cambio podremos tener unos menús de pausa animados.

La fea

La solución fea es... Poner el timeScale a un número muuuuuy pequeño, con lo que todos los sistemas siguen actualizándose pero muuuuy poco, con lo que podremos usar animaciones en el menú de pausa siempre y cuando las reproduzcamos a una velocidad muuuuuuy alta.

void OnPause()
{
    Time.timeScale = 0.00000001;

    menuPause.animation.speed = 10000000; 
    menuPause.animation.Play("Appear");

}

void OnResume()
{

    Time.timeScale = 1;
    menuPause.animation.speed = 1;
    menuPause.animation.Play("Dissapear");

}


Si un jugador paciente se queda mirando a la pantalla durante tres horas igual te pillan.

Conclusión

Estos son los tres métodos que conozco. La buena es adecuada si se tiene tiempo y se pone cuidado al implementarla, la mala es adecuada si no se tiene tiempo y se busca algo seguro, y la fea... Pues... Es fea. Si es el día de entrega y se tiene que implementar una pausa deprisa y corriendo... Puede ser una solución, pero yo no me iría a dormir tranquilo.

Aunque le haya llamado así, en el mundo real la fea es la adecuada para la mayor parte de los casos :)

Más info en Unity Answers.

sábado, 8 de febrero de 2014

Haciento tests

Esta tarde he hecho un test para una conocida compañía de videojuegos que empieza por g. El test no puedo difundirlo pero nadie dijo nada de mi solución, así que allá va.

Tres items para un juego de carreras



Dos tipos de superficie que no sea asfalto:


Diseña un circuito fácil:



Diseña un circuito difícil:


En total le he dedicado dos horas. He ido un poco deprisa y corriendo, como si estuviera haciendo un examen.

lunes, 3 de febrero de 2014

Recomendaciones para un juego de lucha


Una empresa me ha pedido que les haga una recomendación como diseñador para su juego de lucha, y ahí que he ido. Si me hubieran pagado ya la cosa hubiera sido genial pero ya se sabe cómo es España y también cómo salen las cosas luego, claro.  Aquí dejo el documento.


Nuevo blog profesional

Ha llegado el momento de dar un paso adelante y profesionalizarse, por lo que he creado un nuevo portafolio profesional con un look más serio. Espero que algún día me sirva para conseguir un trabajo mejor.



Este blog voy a continuar actualizándolo porque aquí puedo expresarme con más libertad y mantener en forma el espíritu juvenil. Según el proyecto en que me meta lo iré colocando en un blog u otro.