Unity Gestión Dispositivos de Entrada

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • Durante el juego es necesario que el jugador interaccione con el mismo de alguna manera.
  • Dependiendo de la plataforma, puede variar el dispositivo de entrada:
  • En un PC puede hacer uso del teclado, ratón o joystick
  • En un teléfono móvil puede hacer uso de la pantalla táctil o del acelerómetro.
  • En una consola de su mando de juegos.
  • Con gafas de realidad virtual con su vista o movimiento de cabeza.



Unity3d input 8.jpg

Entrada estándar

  • La idea que utiliza Unity para detectar entradas de diferentes dispositivos es la de 'inventar' variables (denominados ejes => Axes) que son usadas por los scripts para gestionar los eventos que se producen desde dispositivos externos como teclados o ratones.
Unity va a 'asociar' los diferentes dispositivos a estos 'ejes'.
  • Veamos un ejemplo para verlo más claro.
Una variable que crea Unity es Input.GetAxis ("Horizontal")
Podemos hacer uso de esta variable desde cualquier script de Unity y devolverá un valor entre -1 y 1, siendo 0 el valor por defecto, -1 cuando cuando mantenemos presionado durante cierto tiempo un dispositivo de entrada (por ejemplo, un joystick desplazando la palanca hacia la izquierda, o presionando y manteniendo la presión de una tecla de un teclado, como la flecha izquierda,...) y +1 cuando el desplazamiento es hacia el otro sentido y la tecla pulsada es otra (por ejemplo la flecha derecha).
  • Ahora desde Unity podemos indicarle que dispositivo de entrada se va a corresponder con esta variable.
Nota: Unity tiene definidos unos 'ejes' por defecto, pero será necesario hacer uso de ellos en nuestros scripts para que tengan algún efecto sobre nuestro juego.


  • Eje Jump:
  • Normalmente será empleado para 'saltar'.
  • Devolverá un valor entre 0 y 1.
  • Propiedad Name: Es utilizada para hacer referencia a ella desde los scripts.
  • Propiedad Positive Button: Indica que parte del dispositivo de entrada provocará que esta variable tenga un valor positivo. Por defecto es la barra espaciador
  • Propiedad Gravity: Indica como se ve afectado este eje por la gravedad cuando llega a su valor máximo negativo o positivo (-1 o 1). Si ponemos un valor muy alto (como viene por defecto) pasaremos de 1 a 0 o de -1 a 0 de forma inmediata cuando dejemos de pulsar la tecla.
  • Propiedad Dead: Es una propiedad que se utiliza en dispositivos de tipo analógico (como Joystick´s ) e indica que valor como mínimo tendría que tomar el dispositivo para que se tuviera en cuenta su pulsación (por ejemplo, al mover la palanca del Joystick, a partir de que punto se detectaría el salto).
  • Propiedad Sensitivity: Es lo contrario de Gravity e indica la velocidad en unidades por segundo en que aumenta el valor del eje al pulsar la tecla espaciador. Si ponemos un valor muy alto pasará inmediatamente de 0 a 1 o de 0 a -1.
  • Propiedad Type: Indica que dispositivos de entrada van a tenerse en cuenta para modificar el valor de este eje. Está indicado 'Key or MouseButton'. En este caso sólo está configurado para que responda a la tecla espaciador, pero el tipo engloba a los dos dispositivos.
  • Propiedad Axis: En este caso no importa el Axis que pongamos ya que la activación la hacemos al pulsar una tecla. Esto tendría sentido si por ejemplo determinamos que el salto se controla con el movimiento de ratón al movernos de izquierda a derecha (pondríamos X axis) o con el movimiento de la palanca de un Joystick subiéndola o bajándola (pondríamos Y axis y en Type pondríamos Joystick Axis)


  • Veamos un ejemplo práctico de este eje.


Script UD3_DispositivosEntrada_1: Asociado a la esfera de la escena de ejemplo.

 1 using UnityEngine;
 2 
 3 public class UD3_DispositivosEntrada_1 : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private Rigidbody rb;
 7     [SerializeField]
 8     private float empuje;
 9 
10     private float saltar;
11 
12     private void Start()
13     {
14         empuje = 20f;
15         saltar = 0;
16     }
17     private void Reset()
18     {
19         rb = GetComponent<Rigidbody>();
20     }
21 
22     private void FixedUpdate()
23     {
24         saltar = Input.GetAxis("Jump");           // Valores 0 o 1
25         Debug.Log(saltar);
26         rb.AddForce(Vector3.up * empuje * saltar);
27     }
28 }
  • Línea 24: Como vemos recuperamos el valor del eje por su nombre.


  • Y os estaréis preguntando, ¿ por qué es mejor esta forma que esta otra ?
 1     private void FixedUpdate()
 2     {
 3         //saltar = Input.GetAxis("Jump");           // Valores 0 o 1
 4         if (Input.GetKeyDown(KeyCode.Space))
 5         {
 6             Debug.Log(saltar);
 7             saltar = 1;
 8         }
 9         if (Input.GetKeyUp(KeyCode.Space))
10         {
11             saltar = 0;
12         }
13         rb.AddForce(Vector3.up * empuje * saltar);
14     }



Por un lado está que el otro código es bastante más sencillo (se podría hacer que saltar fuese booleana e igualarla al Input.GetKeyDown, pero en ese caso no podríamos utilizar valores entre 0-1 para saltar, como podemos hacer si utilizamos los ejes.
Por otro y quizás el más importante, nos hace dependiente del dispositivo. Es decir, tendría que programar todas las opciones de todos los dispositivos que supusieran realizar un salto.
Por ejemplo, si quiero que si pulso el botón derecho del ratón también salte tendría que poner:
 1     private void FixedUpdate()
 2     {
 3         //saltar = Input.GetAxis("Jump");           // Valores 0 o 1
 4         if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0))
 5         {
 6             Debug.Log(saltar);
 7             saltar = 1;
 8         }
 9         if (Input.GetKeyUp(KeyCode.Space) || Input.GetMouseButtonUp(0))
10         {
11             saltar = 0;
12         }
13         rb.AddForce(Vector3.up * empuje * saltar);
14     }
15     }
Y lo mismo para Joystick´s y cualquier otro dispositivo.
Veamos como sería si hacemos uso del Eje Jump:
  • Dejamos el código como estaba:
1     private void FixedUpdate()
2     {
3         saltar = Input.GetAxis("Jump");           // Valores 0 o 1
4         Debug.Log(saltar);
5         rb.AddForce(Vector3.up * empuje * saltar);
6     }
  • Modificamos la propiedad 'Alt Positive Button' para indicar que otro dispositivo de entrada provoca un cambio positivo (desde 0 a 1) en el valor del eje. En nuestro caso sería mouse 0 que es un valor predeterminado de Unity para hacer referencia al botón izquierdo del ratón.
Unity3d input 6.jpg
Y ya está no tenemos que realizar nada más.
  • Nota: Fijarse que podríamos crear nuevos ejes con el mismo nombre y que funcionaran de acuerdo a otros dispositivos de entrada. Podría crear otro eje 'Jump' que funcionara cuando pulse un determinado botón del Joystick'.


  • Otra ventaja que tiene hacer uso de este método es de permitir al jugador configurar las teclas de los diferentes 'ejes' antes de jugar.
Unity3d input 7.jpg
Pantalla de configuración de un Juego generado por Unity.
Podemos ver en la parte izquierda todos los ejes definidos y que teclas/controles están asociados, pudiendo cambiarlos.



  • Seguimos con nuestro ejemplo anterior y vamos a hacer que la esfera se mueva en función de las teclas o movimiento del ratón.
Como comenté antes, Unity tiene 'predefinidos' unos nombres para referirse a partes de un dispositivo (por ejemplo botón izquierdo de ratón, una tecla , botón disparo joystick...
Son los siguientes valores:
Unity3d input 0.jpg
Imagen obtenido de este enlace.


  • También por defecto tiene configurado unos ejes típicos asociados a diferentes teclas-botones de ratón-botones de joystick:
Unity3d input 9.jpg
Imagen obtenido de este enlace.


  • Si nos sirven estas opciones para nuestro juego solamente tendríamos que hacer uso de ellas como vimos anteriormente con el eje Jump.
Vamos a hacer uso de los ejes 'Horizontal-Vertial' para mover la esfera.
Analicemos el eje 'Horizontal' que trae por defecto Unity al crear un Proyecto:
Unity3d input 10.jpg
  • Devolverá un valor entre 0 y 1.
  • Propiedad Name: Es utilizada para hacer referencia a ella desde los scripts.
  • Propiedad Negative Button: Left > flecha izquierda. Aumenta el valor hasta 1.
  • Propiedad Positive Button: Right > flecha derecha. Aumenta el valor hasta 1.
  • Propiedad Alt Negative Button: a > pulsar la tecla a. Disminuye el valor hasta 0.
  • Propiedad Alt Positive Button: d > pulsar la tecla d. Aumenta el valor hasta 1.
  • Propiedad Gravity: Valor 3. Al dejar de pulsar, no pasaremos de forma 'inmediata' al valor 0.
  • Propiedad Sensitivity: Valor 3. Al pulsar alguna de las teclas anteriores, no pasaremos de forma inmediata al valor 1/-1.
  • Propiedad Snap: Checkeado. Indica que cuando pulsemos el botón contrario, pasemos inmediatamente al valor 0 y después aplicará el efecto del botón. Es decir, en este caso, si pulsamos la fecha derecha y tenemos un valor del eje en +0.6, al pulsa la flecha izquierda, pasaremos inmediatamente al valor 0 y después aplicará el efecto de pulsar la flecha izquierda, disminuyendo su valor hasta -1.
  • Propiedad Type: Indica que dispositivos de entrada van a tenerse en cuenta para modificar el valor de este eje. Está indicado 'Key or MouseButton'. En este caso sólo está configurado para que responda a la tecla espaciador, pero el tipo engloba a los dos dispositivos.
  • Propiedad Axis: En este caso no importa el Axis que pongamos ya que la activación la hacemos al pulsar una tecla. Esto tendría sentido si por ejemplo determinamos que el salto se controla con el movimiento de ratón al movernos de izquierda a derecha (pondríamos X axis) o con el movimiento de la palanca de un Joystick subiéndola o bajándola (pondríamos Y axis y en Type pondríamos Joystick Axis)


  • Vamos a aplicar esto a nuestro script:
 1 using UnityEngine;
 2 
 3 public class UD3_DispositivosEntrada_1 : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private Rigidbody rb;
 7     [SerializeField]
 8     private float empuje;
 9     [SerializeField]
10     private float velocidad;
11 
12     private float saltar;
13 
14     private void Start()
15     {
16         empuje = 20f;
17         saltar = 0;
18         velocidad = 5f;
19     }
20     private void Reset()
21     {
22         rb = GetComponent<Rigidbody>();
23     }
24 
25     private void FixedUpdate()
26     {
27         saltar = Input.GetAxis("Jump");           // Valores 0 o 1
28         rb.AddForce(Vector3.up * empuje * saltar);
29 
30         Debug.Log(Input.GetAxis("Horizontal"));
31         float movHorizontal = Input.GetAxis("Horizontal") * velocidad * Time.deltaTime;
32         rb.MovePosition(rb.position + transform.right * movHorizontal);
33 
34     }
35 }
  • Línea 31: Al multiplicar la velocidad por el eje Horizontal esta se irá incrementando poco a poco hasta llegar a su valor. Recordar que el eje 'Horinzotal' no pasa directamente de 0 a 1 al pulsar la tecla.
Si ejecutamos el juego podemos comprobar como la esfera se mueva de izquierda a derecha aumentando su velocidad. Si quisiéramos que pasara directamente a la velocidad indicada, iríamos a su eje y cambiaríamos el valor de 'Sensitivity' a un valor muy alto.
Completamos el script para mover la esfera adelante y atrás.
 1     private void FixedUpdate()
 2     {
 3         saltar = Input.GetAxis("Jump");           // Valores 0 o 1
 4         rb.AddForce(Vector3.up * empuje * saltar);
 5 
 6         float movHorizontal = Input.GetAxis("Horizontal") * velocidad * Time.deltaTime;
 7         float movVertical = Input.GetAxis("Vertical") * velocidad * Time.deltaTime;
 8         rb.MovePosition(rb.position + transform.right * movHorizontal + transform.forward*movVertical);
 9 
10     }
  • Si ejecutamos el juego podremos mover la esfera con las teclas del cursor o con las teclas a,w,d,x (podremos saltar al mismo tiempo y movernos en el aire, pero eso es porque no hemos programado las condiciones para que si estamos saltando, no se pueda mover :) )


  • NOTA: Recordar eliminar del Proyect Settings todos los ejes que no vayáis a utilizar en vuestro juego.


  • Si quisiéramos hacer uso del ratón para mover la esfera tenemos varias posibilidades:
  • Que al pulsar el ratón sobre el plano la esfera se desplace hacia esa dirección o directamente al punto. Esto ya vimos como hacerlo cuando vimos el uso de rayos en la sección de la cámara.
  • Que al mover el ratón se mueva la esfera. En este ejemplo no tiene mucho sentido ya que la esfera seguirá el movimiento del ratón, pero vamos a hacerlo para probar.
Indicar que Unity tiene dos ejes preparados para hacer uso del movimiento del ratón de nombre Mouse X y Mouse Y.
Unity3d input mobile 11.jpg
No vamos a modificarlos y haremos uso de ellos tal cual están.
Los valores que devuelven no están limitados a 0-1 por lo que vamos a crear un Vector2 con los dos valores que devuelve (X,Y) y vamos a llamar al método normalized para que los valores estén entre (-1-0-1)
 1     // Update is called once per frame
 2     void Update () {
 3         Vector2 raton = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
 4         raton = raton.normalized;
 5 
 6         playerControl.Saltar(Input.GetAxis("Jump"));           // Valores 0 a 1. Podríamos modificar el eje Jump para que devuelva 0-1 como en el caso de los móviles.
 7 
 8         float movHorizontal = raton.x * velocidad * Time.deltaTime;
 9         float movVertical = raton.y * velocidad * Time.deltaTime;
10         rb.MovePosition(rb.position + transform.right * movHorizontal + transform.forward*movVertical);
11 
12     }
Nota: Indicar que el Vector2 debería ser una propiedad y debería estar instanciado (hacer el new) en el método Start y en el Update realizar una asignación.
Si ejecutamos el juego podemos ver como la esfera se mueve al mover el ratón.




Dispositivo de entrada: Móviles

  • En el manejo de dispositivos móviles se tiene que realizar directamente en el los scripts haciendo uso de la clase Input.



  • Un control que normalmente querremos tener programado será el de que si el usuario pulsa el botón BACK del teléfono, salga del juego.
Eso se consigue controlando la pulsación de la tecla ESCAPE:
1         if (Input.GetKeyUp(KeyCode.Escape))   // Permite salir de la aplicación si presionamos el botón BACK del móvil.
2         {
3             Application.Quit();
4             return;
5         }


  • Para detectar cuando se pulsa la pantalla disponemos de la estructura Input.Touch la cual nos informa de que se ha pulsado sobre la pantalla, en qué lugar y cuantas veces se ha pulsado (de forma continuada).
  • Vamos a aplicar esto a nuestra escena anterior y vamos a hacer que cuando se pulse dos veces sobre la pantalla, la esfera salte.
 1     private void FixedUpdate()
 2     {
 3         //   saltar = Input.GetAxis("Jump");           // Valores 0 o 1
 4 
 5         if (Input.touchCount > 0){
 6             if (Input.GetTouch(0).tapCount == 2)
 7             {
 8                 saltar = 1;
 9             }
10 
11         }
12         else
13         {
14             saltar = 0;
15         }
16         rb.AddForce(Vector3.up * empuje * saltar);
17 
18         float movHorizontal = Input.GetAxis("Horizontal") * velocidad * Time.deltaTime;
19         float movVertical = Input.GetAxis("Vertical") * velocidad * Time.deltaTime;
20         rb.MovePosition(rb.position + transform.right * movHorizontal + transform.forward * movVertical);
21 
22     }
23 }


Como vemos preguntamos si hemos pulsado la pantalla y en caso afirmativo comprobamos que el número de pulsaciones sea 2.
Para indicar a Unity que ejecute unas órdenes u otras en función de la plataforma, disponemos de una serie de directivas, las cuales las podemos consultar en este enlace.
En nuestro caso vamos a hacer que sólo ejecuta las líneas de tocar sobre la pantalla cuando sea un móbil y en caso contrario lo manejaremos por teclado-ratón.
 1     private void FixedUpdate()
 2     {
 3 #if UNITY_IOS || UNITY_ANDROID
 4         if (Input.touchCount > 0){
 5             if (Input.GetTouch(0).tapCount == 2)
 6             {
 7                 saltar = 1;
 8             }
 9 
10         }
11         else
12         {
13             saltar = 0;
14         }
15 #else
16          saltar = Input.GetAxis("Jump");           // Valores 0 o 1
17 #endif
18 
19         rb.AddForce(Vector3.up * empuje * saltar);
20 
21         float movHorizontal = Input.GetAxis("Horizontal") * velocidad * Time.deltaTime;
22         float movVertical = Input.GetAxis("Vertical") * velocidad * Time.deltaTime;
23         rb.MovePosition(rb.position + transform.right * movHorizontal + transform.forward * movVertical);
24 
25     }
  • Nota:Fijarse que el salto en el caso de dispositivo móvil tiene valores 0 o 1, por lo tanto varía con respecto a la versión PC.


  • Podemos comprobar como la parte del código de IOS-UNITY queda oscurecida indicando que esa parte no se va a ejecutar ahora ya que estamos en la versión PC.



  • Podríamos, por tanto, tener un código diferente para cada plataforma que queramos desenvolver, dentro del método FixedUpdate (en este caso ya que hacemos uso de un RigidBody).
Otra forma sería la de hacer independiente el movimiento de la plataforma y crear un script diferente por cada plataforma.
El proceso sería el siguiente:
  • Determino que variables van a necesitar las diferentes plataformas para 'mover' el personaje.
En nuestro ejemplo el movimiento es arriba-abajo-derecha-izquierda pero estas cuatro opciones las controlo con dos variables las cuales pueden tener valores positivos-negativos.
  • Creamos un script que sea independiente de la plataforma y que mueva al protagonista:

Script UD3_DispositivosEntrada_Independiente

 1 using UnityEngine;
 2 
 3 public class UD3_DispositivosEntrada_Independiente : MonoBehaviour
 4 {
 5 
 6     [SerializeField]
 7     private Rigidbody rb;
 8     [SerializeField]
 9     private float empuje;
10     [SerializeField]
11     private float velocidad;
12 
13     private float saltar;
14     private float moverseLR;    // Left-Right
15     private float moverseUD;    // Up-Down
16 
17 
18     private void Start()
19     {
20         empuje = 20f;
21         saltar = 0;
22         velocidad = 5f;
23         moverseLR = 0;
24         moverseUD = 0;
25     }
26     private void Reset()
27     {
28         rb = GetComponent<Rigidbody>();
29     }
30 
31 
32     public void Saltar(float saltar)
33     {
34         this.saltar = saltar;
35     }
36     public void MoverseIzquierdaDerecha(float movLR)
37     {
38         moverseLR = movLR;
39     }
40     public void MoverseArribaAbajo(float movUD)
41     {
42         moverseUD = movUD;
43     }
44     
45 
46     private void FixedUpdate()
47     {
48 
49 
50         rb.AddForce(Vector3.up * empuje * saltar);
51 
52         float movHorizontal = moverseLR * velocidad * Time.deltaTime;
53         float movVertical = moverseUD * velocidad * Time.deltaTime;
54         rb.MovePosition(rb.position + transform.right * movHorizontal + transform.forward * movVertical);
55 
56         if (rb.position.y < 0)
57         {
58             rb.position = new Vector3(0, 2, 0);
59         }
60 
61     }
62 }
  • Como vemos tenemos tres métodos (Saltar, MoverseIzquierdaDerechay MoverseArribaAbajo) que esperan recibir un dato como parámetro y que se asigna a variables locales que son las que van a mover la esfera.



Script UD3_DispositivoEntrada_PC > Usado sólo cuando sea la plataforma PC. En caso contrario desaparece.

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 
 5 public class UD3_DispositivoEntrada_PC : MonoBehaviour {
 6 
 7     [SerializeField]
 8     UD3_DispositivosEntrada_Independiente playerControl;
 9 
10     private void Reset()
11     {
12         playerControl = GetComponent<UD3_DispositivosEntrada_Independiente>();
13     }
14 
15 
16     // Use this for initialization
17     void Start () {
18 #if !UNITY_STANDALONE       // Si no es PC se destruye. Dependiendo de los casos puede que nos interese dejar la condición así: #if (UNITY_IOS || UNITY_ANDROID)  
19         Destroy(this);      //  ya que de esta forma podríamos generar para más plataformas donde sigan funcionando estos controles como WebGL
20         return;
21 #endif
22     }
23 
24     // Update is called once per frame
25     void Update () {
26 
27         playerControl.Saltar(Input.GetAxis("Jump"));           // Valores 0 a 1. Podríamos modificar el eje Jump para que devuelva 0-1 como en el caso de los móviles.
28 
29         playerControl.MoverseIzquierdaDerecha(Input.GetAxis("Horizontal"));
30         playerControl.MoverseArribaAbajo(Input.GetAxis("Vertical"));
31 
32 
33     }
34 }
  • Cogemos como referencia la clase que lleva el control del protagonista y llamamos a los métodos que hacen que se mueva en función de las teclas pulsadas.




Script UD3_DispositivoEntrada_IOSANDROID > Usado sólo cuando sea la plataforma IOS-ANDROID. En caso contrario desaparece.

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 using UnityEngine.UI;
 5 
 6 public class UD3_DispositivoEntrada_IOSANDROID : MonoBehaviour {
 7 
 8     [SerializeField]
 9     UD3_DispositivosEntrada_Independiente playerControl;
10 
11     private void Reset()
12     {
13         playerControl = GetComponent<UD3_DispositivosEntrada_Independiente>();
14     }
15 
16     // Use this for initialization
17     void Start () {
18 #if !(UNITY_IOS || UNITY_ANDROID)       // Si no es Android o IOS de destruye el script y no se ejecuta
19         Destroy(this);
20         return;
21 #endif
22     }
23 	
24 	// Update is called once per frame
25 	void Update () {
26         if (Input.touchCount > 0)
27         {
28             if (Input.GetTouch(0).tapCount == 2)
29             {
30                 playerControl.Saltar(1);
31             }
32 
33         }
34         else
35         {
36             playerControl.Saltar(0);
37         }
38 
39         if (Input.GetKeyUp(KeyCode.Escape))   // Permite salir de la aplicación si presionamos el botón BACK del móvil.
40         {
41             Application.Quit();
42             return;
43         }
44 
45         float rotacionX = Input.acceleration.x; // El teléfono debe de estar en apaisado y con el botón de casa al lado derecho
46         float rotacionY = Input.acceleration.y;
47         playerControl.MoverseIzquierdaDerecha(rotacionX);
48         playerControl.MoverseArribaAbajo(rotacionY);
49 
50 
51     }
52 }
  • Cogemos como referencia la clase que lleva el control del protagonista y llamamos a los métodos que hacen que se mueva en función del acelerómetro y de pulsar dos veces sobre la pantalla.




Haciendo pruebas con un dispositivo móvil

Puede ser instalado en un emulador de Android.
Si disponemos de un dispositivo móvil conectado, podremos instalar automáticamente el juego, al escoger la opción 'Build and run' al generar los ejecutables del proyecto.



  • Otra forma de probar el juego sin necesidad de instalarlo es haciendo uso de la aplicación para dispositivo móvil Unity Remote la cual puede ser descargada desde el App Store, tanto en IOS como en Android.
Podéis consultar en este enlace toda la información.
Nota: Es necesario que el móvil tenga activada la opción de 'Depuración por USB' como está explicado en este enlace.



Lo que hace esta aplicación es que una vez abierta, pondremos en Unity a ejecutar el juego y a partir de ese momento todos los eventos del móvil (tocar la pantalla, acelerómetro,...) serán recibidos por Unity.





Enlace a la página principal del curso



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