LIBGDX Colisions3D

De MediaWiki
Saltar a: navegación, buscar

UNIDADE 4: Colisións en 3D

Introdución

Clases utilizadas:


Información na wiki: https://github.com/libgdx/libgdx/wiki/Circles%2C-planes%2C-rays%2C-etc.


En 3D temos varias formas de controlar cando unha figura 'choca' con outra.

  • Envolver a figura cun prisma-cubo e comprobar se choca con outra figura.
  • Envolver a figura cunha esfera e comprobar se esta choca con outra figura.
  • Usar un raio: partir dun punto e cunha dirección ata o infinito, comprobar se dito raio atravesa (choca) con outra figura.


LIBGDX UD4 Animacion 6.jpg


  • O cubo-prisma denomínase BoundingBox e pódese obter a partires dun obxecto da clase Mesh ou ben crear nos un obxecto da clase BoundingBox e definir o seu tamaño e posición (con dous vectores (min-max) que conforman o volume)
  • A esfera defínese cun centro (nunha posición no espazo 3D) e un radio.
  • O uso de raios (parten dun punto e teñen unha dirección). Por exemplo, se prememos na pantalla podemos querer saber se estamos a tocar un obxecto que estaría en liña recta dende o punto ata o obxecto.


As colisións as temos que controlar usando a clase Intersector (no caso dos bounding box - rays), a propia clase BoundingBox, para controlar cando un BoundingBox choca con outro,chamando ó método intersects ou a clase Sphere que ten un método que indica cando se 'choca' con outra esfera (método overLaps).

Clase BoundingBox

O proceso é o seguinte:


  1. Mesh cubo;
  2. ......
  3.  
  4. BoundingBox boundingBox = cubo.calculateBoundingBox()


  • Unha vez o temos debemos de aplicarlle as mesmas transformacións que sufra o Mesh, chamando ó método mul.
  1. ..........
  2. boundingBox.mul(matriz);

Sendo matriz un obxecto da clase Matrix4 que foi aplicada ó obxecto Mesh. Lembrar que xa vimos o uso de matrices neste punto do curso.

  • Agora chamaremos ó método intersects enviando como parámetro o BoundingBox doutro obxecto a controlar:
  1. ..........
  2. if (boundingBox1.intersects(boundingBox2)){
  3.    // CHOCA
  4. }


Nota: O cálculo para obter o BoundingBox asociado o Mesh (chamado ao método calculateBoundingBox()) é bastante custoso.

Se queremos aumentar o rendemento o lóxico é que gardemos unha copia do BoundingBox orixinal e outra copia para aplicar a matriz coas transformacións.

Así por cada Mesh do noso xogo crearíamos unha propiedade BoundingBox para gardar o BoundingBox orixinal (estaría situado na posición 0,0,0 sen transformación ningunha). Podería ser de clase (Static). Dependerá se dende a clase que representa o modelo no noso personaxe ten acceso a dita propiedade.

Neste exemplo estamos a supoñer que cargamos un Mesh en forma de Cubo dende a clase AssetsXogo:

  1. public class AssetsXogo{
  2.       .......
  3.       public static BoundingBox BBoxCuboOrixinal;
  4.       public staic void cargarMesh(){
  5.       .........
  6.       BBoxCuboOrixinal = meshCubo.calculateBoundingBox();
  7.       }
  8.  
  9.       .....
  10. }


Agora dende cada clase que faga uso deste Mesh crearemos un BoundingBox temporal ao que asinaremos o orixinal antes de aplicar as transformacións:

  1. public class Inimigos {
  2.       .........
  3.       BoundingBox temporal;
  4.       public Inimigos(){
  5.  
  6.          temporal = new BoundingBox();
  7.       }
  8.    
  9.       public void update(float render){
  10.          ..........
  11.          temporal.set(AssetsXogo.BBoxCuboOrixinal);
  12.          temporal.mul(matriz);
  13.       }
  14.  
  15. }

Sendo matriz un obxecto da clase Matrix4 que ten gardadas todas as transformacións a aplicar sobre o Inimigo (posición, escala e rotación).

Clase Intersector - Rays

O que facemos neste caso é calcular a dirección que ten un raio (vector unidade cunha dirección) e comprobamos se a proxección deste raio no espazo 3D choca cun BoundingBox.

O proceso é o seguinte:

  • Primeiro temos que calcular o raio. Neste exemplo imos supoñer que o xogador preme na pantalla e que queremos controlar se premendo nese punto, a proxección dese punto cara 'dentro da pantalla' choca cun BoundingBox.

Para obter o raio temos que facer uso do método getPickRay da cámara en perspectiva. Normalmente dito control o faremos no método touchDown que se produce cando prememos na pantalla.

  1. @Override
  2. public boolean touchDown(int screenX, int screenY, int pointer, int button) {
  3.  
  4.     Ray ray = camara3d.getPickRay(screenX, screenY, 0, 0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
  5.  
  6.    ......
  7. }

O método está sobrecargado polo que temos varias opcións. Nesta enviamos as coordenadas en pixeles da pantalla e o tamaño do mesma estando a posición 0,0 na parte inferior esquerda da pantalla.

  1.         if (Intersector.intersectRayBoundsFast(ray, boundingBox)){
  2.                  // CHOCA
  3.         }


Clase Sphere

Neste caso temos que asociar a cada obxecto Mesh un obxecto da clase Sphere.

Unha esfera está definida pola posición e radio.

Normalmente nos xogos o tamaño da esfera asociada ó obxecto Mesh non vai variar e o que temos que facer é actualizar a posición en función da posición do obxecto Mesh.

Se estamos a desenvolver o xogo seguindo o patrón Model-View-Controller como xa vimos no xogo 2D teremos que definir a esfera como unha propiedade máis dentro da clase. Instanciala no constructor e movela no método update da clase.

  • O tamaño: o tamaño ten que ven co tamaño que teña o obxecto no programa 3D de deseño. Así, no seguinte gráfico podemos ver como a esfera que representa a Terra ten un tamaño de dúas unidades (2x2x2)


LIBGDX UD4 Animacion 3.jpg


Neste exemplo o radio da esfera é de 1 unidade e ten un diámetro de 2 unidades:

diámetro = radio x 2


Agora ben, cando eu defino unha figura a podo escalar. Entón a esfera teremos que aplicarlle a escala tamén.

Así, se instancio a terra cunha escala de 25 significa o seguinte:

diámetro orixinal sen escalar = 2 Unidades
diámetro escalado = 25 x 2 = 50 Unidades

Polo tanto eu terei que definir unha esfera de 50 unidades de diámetro e polo tanto de 25 Unidade de Radio.

Isto o teremos que facer unha vez (pode ser no constructor) se durante o xogo o tamaño da figura 3D non ten que variar.


O proceso polo tanto sería:

  • Definimos a esfera:
  1. public Sphere esfera;
  • Instanciamos a esfera e lle damos unha posición igual á do obxecto a controlar e unha escala de acordo a escala do obxecto a controlar. No caso da Terra no noso exemplo:
  1.         esfera = new Sphere(pos, escala);
  • Actualizamos a esfera coa posición no obxecto a controlar. Debemos chamar á propiedade center:
  1. esfera.center.set(posicion);


  • Agora queda controlar cando a esfera 'choca' con outra esfera. Para iso temos que chamar ó método overlaps da clase Sphere:
  1.                 if (esfera.overlaps(outraesfera) {
  2.                    // CHOCA                    
  3.                 }



TAREFA 4.6 A FACER: Esta parte está asociada á realización dunha tarefa.




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