UNITY Paso de coordenadas 3D - Screen

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

Paso de coordenadas desde ScreenSpace a WorldSpace

  • Imaginemos que queremos obtener la posición de pulsar el botón izquierdo del ratón en nuestro mundo 3D.
Para ello es necesario 'trasladar' nuestro punto (x,y) de la pantalla nuestro mundo 3D, pero necesitaremos indicar el plano sobre el que se va a proyectar.
Recordemos que nuestra cámara tiene dos planos definidos (near - far) pero podríamos proyector el punto sobre cualquier plano entre esos dos puntos (imaginar un cubo al que lo cortáis para para hacer sándwiches, cada corte es un plano al que podéis llevar el punto).


Veamos un ejemplo:
Crear un script de nombre 'UD_CamaraPerspectiva_1' y asociarlo a un cubo en la posición (0,0,0). Situar a la cámara en la posición (0,0,-8) y que se vea el cubo.
Unity3d camPers 1.JPG


Escribir este código en el script:
 1 public class UD_CamaraPerspectiva_1 : MonoBehaviour {
 2 
 3     private Camera cam;
 4     private Vector3 posWorld;
 5 
 6   // Use this for initialization
 7   void Start () {
 8         cam = Camera.main;
 9         posWorld = transform.position;
10   }
11 	
12   // Update is called once per frame
13   void Update () {
14 		
15         if (Input.GetMouseButtonDown(0)) {  // Pulsamos el botón izquierdo del ratón
16             posWorld = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 8));
17             Debug.Log("Posición en el mundo:" + posWorld);
18         }
19         transform.position = posWorld;
20   }
21 }
Si ahora pulsáis en la pantalla podéis ver como el cubo se mueve al punto pulsado.
Nota: transform.position es la posición del cubo. Lo veremos en el siguiente punto.
Fijarse que en el código tenemos puesto 8 como valor z al llamar al método ScreenToWorldPoint. Esto es así ya que la distancia desde la cámara al cubo es de 8 unidades hacia adelante de la cámara (la cámara está situada en la posición -8):
Unity3d camPers 2.JPG


Podemos observar en la pestaña de consola cuales son las coordenadas (x,y,z) de nuestro punto en el mundo en el plano z=8 unidades:
Unity3d camPers 3.JPG


  • Veremos más adelante que Unity (y todos los motores/frameworks) tienen mecanismos para implementar rayos de colisión. Un rayo es un punto con una dirección que atraviesa nuestro mundo 3D. Es como si dispararas una flecha en línea recta desde donde tocas la pantalla hacia el interior de nuestro mundo 3D. Parte del punto de la pantalla donde pulsamos y 'atraviesa' todos los planos de nuestro mundo 3D hasta un plano determinado (normalmente empieza en el plano near de la cámara y acabaría en el plano far de la misma, ya que es el área de visualización) siguiendo una dirección. Más información en este punto.




Paso de coordenadas desde WorldSpace a ScreenSpace

  • En el caso contrario a lo visto anteriormente. Pasamos de coordenados en nuestro mundo 3D a coordenadas del monitor en pixeles.
Debemos de tener en cuenta que para la cámara, la coordenada (0,0) se encuentra abajo-izquierda mientras que las coordenadas en la pantalla, la coordenada (0,0) está arriba a la izquierda. Por tanto, cuando obtengamos la coordenada de pantalla a partir de la coordenada 3D debemos de hacer la operación: Screen_Height-Y.


  • Modificar el código anterior:
 1 public class UD_CamaraPerspectiva_1 : MonoBehaviour {
 2 
 3     private Camera cam;
 4     private Vector3 posWorld;
 5 
 6     // Use this for initialization
 7     void Start () {
 8         cam = Camera.main;
 9         posWorld = transform.position;
10 
11     }
12 
13     private void OnGUI()
14     {
15         Vector2 posPantalla = cam.WorldToScreenPoint(transform.position);
16         posPantalla.y = Screen.height - posPantalla.y;
17         Rect areaVisual = new Rect(posPantalla, new Vector2(50, 20));
18 
19         Debug.Log("Posición en pantalla:" + posPantalla);
20 
21         GUILayout.BeginArea(areaVisual);
22         GUI.color = Color.yellow;
23         GUILayout.Label("CUBO");
24         GUILayout.EndArea();
25 
26     }
27 
28     // Update is called once per frame
29     void Update () {
30 		
31         if (Input.GetMouseButtonDown(0)) {  // Pulsamos el botón izquierdo del ratón
32             posWorld = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 8));
33             
34         }
35         transform.position = posWorld;
36     }
37 }
  • Línea 15: Pasamos de coordenadas 3D a coordenadas de pantalla (pixeles)
  • Línea 16: Debido a que el punto (0,0) de la cámara es diferente al de la pantalla debemos de restar el alto de la misma.
  • El resto del código ya lo iremos viendo, pero básicamente crea un rectángulo donde dibuja un Label de color amarillo con la palabra 'Cubo'.
Si ejecutáis el código anterior, al pulsar sobre la pantalla podéis ver como el cubo se traslada al punto y aparece la palabra 'Cubo' siempre sobre él:
Unity3d camPers 4.JPG




Rayos de detección de choques

  • Crea una nueva escena de nombre Escena_UD2_CamaraPerspectiva_Rayos.
Carga dicha escena pulsando dos veces sobre la misma.
Recuerda moverla (o crearla directamente) sobre la carpeta 'Assets/Scenes' de la ventana 'Project Window'.
Agrega a dicha escena un objeto 3D Plane y cambia la escala a 10x10x10.
Sitúa la cámara en una posición en la que se vea todo el plano.


  • Veremos en otras secciones la forma que tiene Unity (y otros motores) de detectar 'colisiones' entre diferentes GameObjects.
En esta sección veremos como hacer uso de los rayos.
Un rayo es una línea recta que se desplaza desde una posición inicial siguiendo una dirección.
Al desplazarse por el espacio 3D va atravesando diferentes GameObjects.


  • Un ejemplo de uso es cuando queremos disparar hacia un enemigo y 'presionamos' con el ratón sobre el mismo.
Para comprobar si le hemos dado, el programador debe de crear un rayo que vaya desde la posición inicial (en este caso sería desde la cámara) hacia el punto indicado por el ratón.
Nota: Indicar que podríamos hacer uso del método anterior para 'pasar' del punto x-y del ratón al espacio 3D, pero necesitaríamos saber la distancia Z para llevar el punto al plano exacto donde se encuentra la posición del ratón, por lo que es mucho más complicado.


Unity3d camara ray 1.jpg
En la imagen anterior podemos ver como al presionar sobre el plano, queremos 'lanzar' un rayo desde la perspectiva de la cámara y ver en que posición choca con el suelo.


  • Nota Importante:
Ya hablaremos en otra sección de los 'collider'. Para adelantar digamos que son figuras 3D que rodean a los GameObjects y que nos van a permitir gestionar los choques entre diferentes gameobjects.
Para que funcione el registro de la 'colisión' entre un rayo y un objeto 3D, es necesario que este tenga algún tipo de collider asociado (existen varios tipos, ya los veremos).
Por defecto, cando agregamos un plano, ya crea un collider asociado:
Unity3d camara ray 1B.jpg



  • Previamente a ver el código que nos permite detectar el choque, vamos a realizar una optimización que va a consistir en lo siguiente: Cuando lanzamos un rayo este puede atravesar múltiples 'gameobjects' de nuestro juego, pero seguramente a nosotros solo nos interesa detectar los choques 'contra ciertos gameobjects' en concreto. Por ejemplo, en nuestro ejemplo, solo nos interesa detectar el punto de choque con el suelo.
Para ello podemos hacer uso de Layers.
Los Layers son capas que están identificadas con un nombre y que asociamos a diferentes GameObjects.
Vamos a crear una nueva capa (layer) y la asociaremos con el suelo (es un Plane, escalado a 10x10x10) del ejemplo.


  • Ahora vamos a crear un nuevo script de nombre UD2_CamaraPerspectiva_Rayos
Unity3d camara ray 5.jpg
  • El código del script sería el siguiente:
 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 
 5 public class UD2_CamaraPerspectiva_Rayos : MonoBehaviour {
 6 
 7     // Identificador de la capa (Layer) creada previamente sobre la que queremos detectar el choque
 8     private int idCapa;
 9     // Objecto que nos va a servir para obtener información sobre el choque del rayo con el suelo
10     private RaycastHit raycastHit;
11     // Distancia del rayo
12     private float distanciaRayo;
13 
14 	// Use this for initialization
15 	void Start () {
16 
17         idCapa = LayerMask.GetMask("Suelo");   // Podríamos poner varias separadas por coma...
18         distanciaRayo = 100;
19 	}
20 	
21 	// Update is called once per frame
22 	void Update () {
23         if (Input.GetMouseButtonDown(0))
24         {
25             Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition); // Crea un rayo desde la posición del ratón en la dirección de la cámara
26             if (Physics.Raycast(camRay, out raycastHit, distanciaRayo,idCapa))
27             {
28                 Debug.Log("Posición choque:" + raycastHit.point);
29                 Debug.DrawRay(Camera.main.transform.position, camRay.direction*50, Color.red,10,false);
30             }
31         }
32 
33     }
34 
35 }
  • Líneas 8,10,12: Definimos una serie de variable que vamos a necesitar.
  • El id del layer 'Suelo' creado previamente y asignado al plano.
  • Un objeto de la clase RaycastHit que nos va a servir para obtener el punto de 'choque' del rayo con el suelo.
  • Especificamos la distancia del rayo, ya que si no, este se desplazaría a lo largo del view frustrum y no es necesario.
  • Línea 17: Obtenemos el identificador de la capa (Layer) Suelo.
  • Línea 18: Establecemos una distancia máxima para el rayo de 100 unidades.
  • Línea 23: Comprobamos si pulsamos el botón izquierdo del ratón.
  • Línea 25: Creamos un rayo que vaya desde la cámara hasta la posición del ratón.
  • Línea 26: Hacemos uso de la clase Phisics para llamar al método de clase Raycast el cual recibe como parámetros: el rayo, un objeto de la clase RayCastHit de tipo output, esto quiere decir que se van a guardar datos en dicho objeto, la distancia del rayo y el id del Layer que queremos que se tenga en cuenta. Este método va a devolver un boolean indicando si hubo o no choque.
  • Línea 28: En caso de choque, podemos saber el punto 3D del mismo con la propiedad 'point' del objeto raycastHit.
  • Línea 29: Dibuja el rayo saliendo de la cámara. Esto solo se debe de hacer mientras estamos en la fase de desarrollo. Dibuja una línea solo visible en la ventana Scene. El rayo tendrá un tamaño de 50 unidades (camRay.direction*50), tardará 10 segundos en desaparecer, será de color Rojo y saldrá de la cámara. El último parámetro indica que el rayo no se vea afectado por otros GameObjects que haya cerca.


  • Si asociamos el script creado al GameObject 'Suelo' (solo hay que arrastrarlo gráficamente), al pulsar sobre el plano en ejecución podemos ver el punto de choque en la ventana Console:
Unity3d camara ray 6.jpg




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