jueves, 23 de junio de 2016

Como crear textos al agarrar objetos en tiempo real en Unity | How to create texts when grabbing objects in real time in Unity

Español | English



Un efecto clásico. En muchos juegos de Arcade o de plataforma tenemos este pequeño detalle que tal vez algunos ignoran, un pequeño texto cuando agarramos un objeto (o un simple "+1" al agarrar una moneda). Así que me decidí por averiguar cómo funciona.

La idea básica es simple: hacer que un texto se muestre cuando el jugador colisione con cierto objeto, el texto debía aparecer un poco más arriba del jugador para poder apreciarlo bien. Pero también, el texto debía aparecer un poco más adelante, ya que el jugador esta constantemente en movimiento, por lo que el texto va a estar, adelante y por arriba del jugador.

¿Pero cómo le decimos donde aparecer al texto, si en Unity el GUI y el "mundo" se manejan por separado? Para aclarar el problema, si un objeto esta en la posición 100 horizontal, no hay forma de llevarle esas coordenadas al texto que va a aparecer en la interfaz. Además se suma el hecho de que la cámara se mueve constantemente con nosotros.

Para esto debemos ajustar algunas propiedades del Canvas. El Canvas es el lugar en donde tienen que estar los elementos del GUI (como los botones o textos, etc). Estos se pueden crear y hasta tener más de uno en la misma escena. (Nota: Al crear un botón o cualquier elemento del GUI, Unity por defecto crea un Canvas si no había uno existente).

Luego de modificar el Canvas vamos a crear una "Sorting Layer" y finalmente, vamos, por código, a indicarle al texto donde tiene que aparecer.



La opción que vamos a cambiar es "Render Mode". Vamos a utilizar la opción "Screen Space- Camera", lo que hace cada opción de la lista es ofrecernos distintas formas en las que podemos utilizar el Canvas; si queremos que este por encima de la cámara, si queremos que este fija en el espacio del juego o si queremos que trabaje en el espacio de la cámara.

Lo que vamos a hacer nosotros es trabajar en el espacio de la cámara. Lo siguiente será arrastrar nuestra cámara a la casilla de "Render Camera" para asignarla.


Las Sorting Layers son capas de prioridades, se usan principalmente para indicar que elementos se muestran al fondo y cuales más adelante. Hacemos Click en Sorting Layer y luego en "Add Sorting Layer".

Primero clickeamos en el "+" y luego la nombramos, finalmente debemos volver a la propiedad de Canvas para asignarle esa capa que le creamos.

El Canvas esta listo. Este trabajo fue hecho para que el Canvas trabaje en el espacio del juego pero los elementos que muestre estén "una capa adelante", con lo cual no se va a superponer con otros objetos del juego.

Lo siguiente a hacer es crear dentro de este Canvas un texto de UI, haciendo click derecho en el Canvas/UI/Text.

Por último, vamos a crear el Script que le va a indicar a ese texto donde tiene que aparecer y que tiene que decir.

Las variables a utilizar son:

Public Text cartel;
(Nota: Luego hay que arrastrar un elemento Texto a la casilla de la variable pública)

Voy a utilizar un corutina por 2 razones: 
  1. Quiero poder llamar a esta función poniendo como parámetro el texto que quiero que muestre.
  2. Quiero crear un delay para que el texto no desaparezca enseguida.


IEnumerator Show_Sign(string texto)
    {
        cartel.gameObject.SetActive(true);
        cartel.text = texto;
        cartel.color = new Color(1, 1, 1, 1);
        cartel.gameObject.transform.position = new Vector3(player.position.x + 1, player.position.y + 1, 0);
        yield return new WaitForSeconds(0.2f);
        cartel.gameObject.SetActive(false);
     }

La función se basa en activar y desactivar el texto que habíamos creado, luego le dice cual es el texto que tiene que mostrar y le asigné el color blanco. Luego, le indico que tiene que estar 1 unidad más arriba y adelante de la posición del jugador. Por último, le digo que haga un delay hasta la próxima instrucción que es para desactivar este texto.

Lo que no quería hacer era tener que crear y eliminar el texto cada vez que el jugador agarra un objeto, ya que consumía muchos recursos, por eso, es más preferible ya tenerlo creado y simplemente activarlo o desactivarlo.

Ahora podemos llamar a la corutina indicándole que texto mostrar de la siguiente manera:

StartCoroutine(Show_Sign("Calculator!"));

Y así queda creado este efecto clásico pero muy interesante que aporta un agregado más al diseño del juego en general.


-L



------------------------------------------------------------------------------------------

English

A classic effect. In many Arcade or platform games, we have this small detail that many can ignore, a small text when we grab an object ( or a simple "+1" when we grab a coin). So I decided to figure out how it works.

The basic idea is simple: Make a text show when the playe collides with a certain object, the text should appear a little higher than the player so it can be seen clearly. But also, it has to be a little bit more forward as the player is constantly moving. So the text is going to be placed higher and a bit forward than the player.

But how do we tell the text where to go, if in Unity the GUI and the "world" work on different spaces? To make the problem more clear, lets say there is an object in the horizontal position 100, there is no way to send the coordinates to the text which is going to appear on the interface. Plus, there is the fact that the camera moves following the player.

So we need to make some few changes to some of the properties of the Canvas. The Canvas is the place where the elements of the GUI (such as buttons, text, etc) have to be. This can be created and the same scene can have multiple Canvas. (Note: When creating a button or any GUI element, Unity by default creates a Canvas if didn't exist).

After having done the modifications to the Canvas, we are going to create a "Sorting Layer" and finally, by code, we are going to indicate the text where it should appear.



The option we are going to change is "Render Mode". We are going to use the option "Screen Space- Camera", what each option of the list does is to offer us different ways in which we can use the Canvas; if we want it to be above the camera, or we want it with fixed position in the game space or we want it to work in the same space of the camera.

What we are going to do is to work in the same space of the camera. The next thing is to drag the camera to the "Render Camera" box to assign it.

Sorting Layers are layers of priorities, they are used mainly to indicate which elements go to the background and which elements will be above those of the background. We click in Sorting Layers and then in "Add Sorting Layer".




First we click the "+" and then we name the layer, finally we go back to the properties of the Canvas and assign the layer we have just created.

The Canvas is ready. This job was done to allow the Canvas to work in the same space than the camera but making the elements of the Canvas be on a different layer, this will avoid the collision between the elements of the GUI and the rest of objects of the game.

The next thing to do is to create inside the Canvas, a UI text, making right click on Canvas/UI/Text.

Finally, we are going to create the Script that will indicate the text where it should go and what to say.

The variables I'm going to use are:

Public Text mysign;
(Note: Later on, a Text element has to be dragged to the public variable box)

I'm going to use a coroutine because of 2 reasons:

  1. I want to call this function with the text I want to show as a parameter.
  2. I want to create a delay so that the text doesn't disappear right away.


IEnumerator Show_Sign(string mytext)
    {
        mysign.gameObject.SetActive(true);
        mysign.text = mytext;
        mysign.color = new Color(1, 1, 1, 1);
        mysign.gameObject.transform.position = new Vector3(player.position.x + 1, player.position.y + 1, 0);
        yield return new WaitForSeconds(0.2f);
        mysign.gameObject.SetActive(false);
     }


The function is based on toggling on and off the text which we have created, it laters tells the text what should say and I make it white. Then, I tell the text that it should be 1 unit more high and forward than the player's position. Finally, I create a delay for the next instruction, which is to deactivate the text.

What I didn't want to do was to create and eliminate the text every time the player grabs an object, as it would consume a lot of resources, that is why, it is better to have the text already created and simply activate it or deactivate it.

Now, we can call the coroutine indicating what we want it to say:

StartCoroutine(Show_Sign("Calculator!"));

And this is how we create this classic but very interesting effect which adds its own value to the general game design.

-L

martes, 14 de junio de 2016

Como crear trayectoria Senoidal para un objecto por Código | How to create a Sinusoidal trajectory to an object by Script

Español | English

Muchas escuchamos o hicimos la pregunta a nuestro/a profesor/a de matemáticas: "¿Voy a usar esto en el futuro?" y tal vez el o ella no sabía que responder, seguro les contestaba que dependía de la carrera que el alumno o la alumna fuera a seguir.

Para el desarrollo de Videojuegos se utiliza toda herramienta posible de las matemáticas, ya que nos pueden servir para resolver los casos que se nos planteen. Por eso es importante tener un amplio conocimiento de las ciencias exactas para tener más herramientas disponibles.

La situación de la cual partimos se genera a partir de un objeto que se mueve en línea recta a velocidad constante. Este objeto es uno de los encargados de instanciar (Spawnear) otros objetos, que el jugador va a recolectar.

Ya había hablado antes de que el Spawner instancia objetos en intervalos aleatorios de tiempo, pero siempre en linea recta. Por lo que nos podemos quedar quietos en una misma posición y siempre agarrar objetos.

Una solución a este problema es cambiar la posición vertical ("altura") del Spawner constantemente, para que genere objetos a distintas "alturas" y así, cubrir más superficie del mapa. Al moverse verticalmente de arriba hacia abajo y con velocidad constante, podemos ver que la trayectoria del Spawner va a crear una forma senoidal.

Para eso primero creamos las variables que van a actuar:

float distancia = 0.13f;
public float h = 0;
public float hmax = 0;
public float hmin = 0;
public bool up = true;

Donde h representa la altura del objeto, se actualiza constantemente, hmax y hmin representan la altura máxima y mínima que puede tomar ese objeto, y por último, "up" determina si tiene que subir o no.

Las variables hmax y hmin tienen que estar asignadas en la función Start() del Script, ya que no deberían cambiar.

void Start(){
hmax = 30f;
hmin = 1f;

}

Ahora vamos a focalizarnos en la función Update(). Primero, vamos a asignarle a la variable h el valor de la posición vertical del Spawner, luego vamos a indicarle si debe subir o bajar.

h = this.GetComponent<Transform>().position.y;

Nota: Se asume que el Script esta añadido al objeto, de no ser así, se debería crear una variable pública de tipo Transform a la cual le pondremos los valores del objeto.

if (up == true)
        {
          this.gameObject.GetComponent<Transform>().position += new Vector3(0, distancia, 0);
        if (h >= hmax)
        {
            up = false;
        }
        }
        if (up == false)
        {
           this.gameObject.GetComponent<Transform>().position -= new Vector3(0, distancia, 0);
        }
        if(h <= hmin)
        {
            up = true;
        }

Aquí esta la lógica del movimiento. Se basa en analizar si el objeto tiene que subir o bajar, en caso de que tenga que subir, se actualiza constantemente su posición vertical hasta que alcanzó el límite, luego, se le indica que debe bajar y de nuevo se modifica la posición vertical. Esto se lleva a cabo infinitas veces.

Se puede, como complemento, ajustar la velocidad a la que se desplaza o agrandar el intervalo.Y así queda creada la mecánica para hacer que un objeto recorra una trayectoria senoidal.


-L

--------------------------------------------------------

English

Many times we have heard someone ask (or did it ourselves) to the teacher: "Am I going to use this in the future?" and maybe the teacher didn't know what to answer, they surely said that it depended on what the student was going to do when he or she grow up.

For Game development, we use every available tool from math, as they can be useful to solve cases that may appear. That is why we need to have a wide range of knowdledge from the exact sciences to have as many tools as possible.

Our starting situation is generated by and object that moves in straight line with constant speed. This object is one of many who instantiates (Spawns) the objects that the player will collect.

I've spoken before about the Spawner, saying that it instantiates in random periods of time, but always in straight line. So we can stand still in a fixed position and alwats grab objects.

A solution to this problem is to change the vertical position ("height") of the Spawner constantly, so that it generates the objects in different "heights", which will cover more surface of the map. As the object moves up and down and it is also moving at a constant speed horizontally, we can see that this creates a sinewave trajectory.

To do this we first need to create the variables that will act:

float distancia = 0.13f;
public float h = 0;
public float hmax = 0;
public float hmin = 0;
public bool up = true;

Where h represents the height of the object, which is constantly being updated, hmax and hmin represent the maximum and minimum height that the object can have, finally, "up" determines whether  the object has to go up or not.

The variables hmax and hmin have to be assign in the Start() function of the Script, as they are not meant to be modified.


void Start(){
hmax = 30f;
hmin = 1f;
}

Now we are going to focus on the Update() function. First of all, we need to assign the vertical position of the object to the variable h, then we are going to indicate if it needs to go up or down.

h = this.GetComponent<Transform>().position.y;

Note: I assume that the Script is attached to the gameObject, if this is not the case, a public variable of the type Transform needs to be created and later on, it has to be assign to an object.

if (up == true)
        {
          this.gameObject.GetComponent<Transform>().position += new Vector3(0, distancia, 0);
        if (h >= hmax)
        {
            up = false;
        }
        }
        if (up == false)
        {
           this.gameObject.GetComponent<Transform>().position -= new Vector3(0, distancia, 0);
        }
        if(h <= hmin)
        {
            up = true;
        }

Here it is the logic of the movement. It is based on analyzing whether the object has to go up or down, in case it has to go up, its vertical position is being constantly updated until the limit is reached, then, it indicates the object that it has to go down and it modifies the vertical position again. This is performed infinite times.

As a complement, one can also adjust the speed on which it moves or make the periods longer. And so it is created the mechanic that make an object travel a sinusoidal trajectory.

-L

lunes, 13 de junio de 2016

Como crear un botón de pausa | How to create a pause button

Español | English

Los botones de pausa son fundamentales para los videojuegos, son una herramienta que no puede faltar en cualquier juego sin importar en que medio se este jugando. Por eso hoy voy a crear un simple tutorial para crear un botón de pausa en Unity.

Si bien yo creo que cada juego debería ser analizado individualmente dependiendo de sus características, lo que principalmente buscamos es crear un estado en donde toda actividad se detiene. Lo que más en detalle vamos a hacer sería detener el tiempo, ya que muchas funciones dependen de el para funcionar.

Como no todas las mecánicas del juego dependen del tiempo (como el desplazamiento de un objeto que recibe un Input como el teclado), estas van a seguir activas mientras el tiempo este detenido, de ahí que digo que cada juego puede utilizar mecánicas especiales y por eso debe ser pensado distinto.

Primero en principal debemos crear un botón de GUI: 


Ahora creamos el Script (o usamos uno ya existente) en donde vamos a poner una función publica para que el botón pueda acceder

public void Pause (){
If(Time.timeScale == 1)
        {
            Time.timeScale = 0;
            velocidad = 0;
            is_pause = true;
        }
        else
        {
            Time.timeScale = 1;
            velocidad = 0.13f;
            is_pause = false;
        }
}

Acá podemos ver que la propiedad importante a usar es Time.timeScale, que lo que hace es modificar la escala del tiempo del juego, si la escala está en 0, el juego esta detenido, si esta en 0.25, el juego va a estar "en cámara lenta", una propiedad de la que se puede aprovechar.

Di un ejemplo de como sería si por ejemplo en mi juego hay un objeto a velocidad constante. Al poner pausa, como lo que hace mover al objeto no es la velocidad sino un cambio en el espacio, no es afectado por el tiempo, por eso tengo que "frenarlo" manualmente. Así también podemos poner unos flags que indiquen si el juego esta pausado o no.

Finalmente, queda asignarle la función al botón, para eso vamos a ir a la sección de "OnClick()" que esta en el inspector cuando seleccionamos un botón


Haciendo click en el más, le asigamos una función al botón, falta decirle que función, para eso vamos a:
  1. Agregar el Script al objeto
  2. Arrastrar el botón desde el inspector hasta esta propiedad "OnClick()" del botón
De ahí que podemos, desde una lista de elementos desplegables, elegir nuestro Script y luego, nuestra función. Aviso: El hecho de primero agregar el Script al botón puede ser innecesario en algunos casos. Yo lo hago por si quiero que varios botones tengan la misma función pero que, al accionarla, funcione solo para un botón y no para todos.

Para los que querrían que su personaje no se pueda desplazar, les recomiendo usar las Constraints del Rigidbody

Primero declaramos una variable publica (por si no lo hicimos antes) del RigidBody (en mi caso, 2D):

public Rigidbody2D rb;

y luego les asignamos el bloqueo de posición en el eje Y y Z.


rb.constraints = RigidbodyConstraints2D.FreezePositionX;
rb.constraints = RigidbodyConstraints2D.FreezePositionY;

para luego desbloquear esto, simplemente ponemos:

 rb.constraints = RigidbodyConstraints2D.none;

Y así queda creado un simple botón de pausa.

-L



----------------------------------------------------------

English
 
Buttons are fundamental for video-games, they are a tool that can't be absent in any game regardless the media on which the game is played. That is why today I'm creating a simple tutorial to teach how to create a pause button in Unity.

Although I think each game needs to be analyzed separately depending on its features, what we are mainly looking for is to create a state where every activity is stopped. Going into the details, we are going to stop time, as many functions depend on the time to work.

As there are some mechanics that don't depend on time (such as the displacement of an object which recieves an Input like a keyboard), this are going to be active whether the time is "on" or "off", that is why I mentioned earlier that there could be games that use special mechanics and that is why they need to be thought different.

First of all we need to create a GUI Button:



Now we create the Script (or we use one that we had before) on where are going to write a public function that the button can access.

public void Pause (){
If(Time.timeScale == 1)
        {
            Time.timeScale = 0;
            velocidad = 0;
            is_pause = true;
        }
        else
        {
            Time.timeScale = 1;
            velocidad = 0.13f;
            is_pause = false;
        }
}

Here we can see that the important property to use is Time.timeScale, that modifies the time scale of the game, if the scale is on 0, the game is stopped, if it is on 0.25, the game will be in "slow motion", a property which can be given different uses.

I also did an example of how it would be the case where I have an object that moves at constant speed. When I pause the game, the object will keep moving as the movement doesn't uses the time but the space, that is why I need to "stop" it manually. In the same way, we can create flags to indicate whether the game is paused or not.

Finally, we need to assign the function to the button. To do that we are going to go to the "OnClick()" section which is in the inspector when we select a botton.


Clicking on the plus we assign a function to the button, now we have to say which function, for that we:

  1. Add the Script to the object
  2. Grab and drag the button from the inspector to the "OnClick()" property of the button
From there we can, from a drop down list, select our Script and then, our function. Note: The step of adding the Script first to the object is not necessary in some cases. I use it in case I have several buttons using the same function and I want each one of them to work separately when I activate the function.

For those who would like their character not to move when it is on pause, I recommend to use Rigidbody's Constraints

First we declare a public variable (in case we didn't do it before) of the Rigidbody (in my case, 2D):

public Rigidbody2D rb;

Then we add the block to the X and Y axis:


rb.constraints = RigidbodyConstraints2D.FreezePositionX;
rb.constraints = RigidbodyConstraints2D.FreezePositionY;


Later, to unblock the axis we need to use:

 rb.constraints = RigidbodyConstraints2D.none;

And that is how we create a simple pause button

-L

domingo, 12 de junio de 2016

Falta poco! | Almost there!

Español | English


De a poco seguimos avanzando. Ya casi termino con la programación, las lógicas están hechas, los mini-juegos creados y las dificultades creadas.

Mi mayor complicación ahora, como mencioné antes, es crear el arte del juego. Lo que dejé para el final son los personajes que van a aparecer (tanto el principal como los secundarios), como el mapa mismo del juego. Lo bueno es que no necesito modificar mucho, ya que simplemente les agrego las imágenes correspondientes a los objetos que tenía antes.

Las últimas modificaciones que se hicieron fueron:
  • Solucionar problemas de colisión entre los objetos que agarramos.
  • Creé el botón de pausa.
  • Se modificó el juego para que se complique a medida que avanzamos
  • Se agregaron textos al agarrar cosas.
De el último elemento de la lista voy a dedicarme más en detalle a futuro, ya que no se podía hacer de forma directa sino que había que modificar algunas cosas. Más adelante también voy a hacer tutoriales sobre como hacer las cosas que hice.

Esto les dejo por ahora sigan al tanto que no falta mucho para que el juego este en la Play Store.

-L

---------------------------------------------------------------------

English


Little by little we keep moving forward. I'm almost done with all the programming, the logics are made, the mini-games are created and the difficulties made.

My biggest problem right now, as I mentioned before, is the art of the game. What I left for the end was the characters (both the main characters and the secondary characters), as the main map of the game. The good thing is that I don't need to modify much, as I simply add the images I create to the corresponding objects that I had before.

The last modifications I did were:

  • Solve  problems with the collision between the objects we collect.
  • I created the pause botton.
  • I modified the game so that it is harder as we move forward.
  • I added texts that show up when we grab objects.
I'll dedicate more detail in the future to the last item of the list, as it can't be done directly because some configurations need to be made. In the future I'll also make tutorials telling how I did those things.

I leave you with this by now, keep up with the blog as it won't be long before the game is on Play Store.

-L