LIBGDX Colisions3D

De Manuais Informática - IES San Clemente.
Ir a la navegación Ir a la búsqueda

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 -- (2014).