LIBGDX Vector direccion

De MediaWiki
Saltar a: navegación, buscar

Introdución

Nota: Esta explicación está relacionada coa sección 'Movendo os gráficos'.



En case todos os xogos imos ter a necesidade de facer que o noso protagonista ou algún inimigo se mova cara a algo.

Por exemplo, imaxinemos que temos un xogo no que disparamos un foguete e queremos que este se dirixa cara un certo punto.

Para conseguir isto imos explicar o que é un vector dirección.


Movendo os gráficos. Vector dirección

Normalmente en todos os xogo ides ter algún personaxe que se mova cara a algún outro personaxe. Por exemplo, cando disparedes queredes que as balas se movan cara o obxectivo.


Para conseguir isto imos facer uso do que se chama Vector Dirección.


Primeiro imos temos que saber o que é un Vector. De forma resumida para nós o Vector vai representar a dirección que ten que seguir un personaxe para chegar a un punto de destino.

Se dito punto de destino non varía no tempo o vector de dirección ten que calcularse ó principio e non variará. Se queremos seguir a un personaxe que se move, cada certo tempo teremos que re-calcular dito vector dirección.


A forma máis sinxela de velo é cun exemplo.

Nota: Os concepto aprendidos serán igualmente aplicables a 3D introducindo a coordenada Z.

Imaxinemos que estamos na coordenada (1,1) e queremos dirixirnos ata a coordenada (4,2). O que teríamos que facer é calcular cal é o vector de dirección, é dicir, que números (x,y) sumados a (1,1) nos levan ata o (4,2).


LIBGDX UD2 7 graficos 4.jpg

O máis lóxico é restar o punto de destino menos o punto de orixe, desta forma:

Vector Dirección = Punto_Destino – Punto_Orixe = (4,2)-(1,1) = (3,1).


En libgdx o faremos utilizando os métodos sub (para restar dous vectores) e cpy (para facer unha copia do vector) da clase Vector2 (con 3 coordenadas usamos un Vector3 e con dous Vector2):

Punto Orixe = Posición do personaxe = Vector2 posicion
Punto Destino = Posición do punto de destino (no exemplo) = Vector2 (4,2)
direccion = posicion_destino.cpy().sub(posicion_orixe);


Fixarse como usamos unha copia do vector de posición destino xa que o método sub modifica o vector orixinal.


Se queremos aumentar o rendemento (xa que estamos a facer new´s ó usar a función cpy) teríamos un vector temporal xa creado previamente (no constructor) e copiaríamos o contido chamando ó método set da forma:

temporal.set(posicion_destino);
direccion = temporal.sub(posicion_orixe);


Nota: Isto só ten sentido se estamos a chamar ó método cpy de forma continua ou se necesitamos gardar o vector posicion_destino para algo. Se, por exemplo, non modificamos o vector dirección, isto só o temos que facer unha vez no constructor e polo tanto non necesitamos vector temporal para esta operación.


No exemplo anterior teríamos un vector de dirección con valores (3,1). Agora poderíamos chegar ó punto de destino dun único salto, xa que na primeira iteración sumaríamos o seu valor e xa chegaríamos.

posicion = posicion + vector_dirección = (1,1) + (3,1) = (4,2).


Isto é debido a que a súa lonxitude é moi grande. Como o que queremos é que pouco a pouco vaia chegando podemos facer uso do método nor() que normaliza o vector e fai que o seu valor sexa o vector unidade (para nos terá un valor <=1) pero os seus valores farán que ó sumalos á posición se vaia achegando ó punto de destino.


A forma de facer uso del sería a de normalizar o vector dirección antes de sumalo:

direccion.nor();

Nota: Loxicamente isto só o temos que facer unha vez xa que nor modifica o vector de dirección. Se volvemos aplicar dito método o faríamos sobre o vector xa previamente normalizado. Isto só será necesario facelo cada vez que modifiquemos o vector dirección.


Agora para mover o personaxe ó punto de destino teríamos que facer:

posicion.add(direccion.cpy().scl(velocidade*delta));

Nota: Igual que no caso anterior podemos usar o mesmo vector temporal para asinarlle antes a dirección e non ter que facer un cpy de cada vez. Normalmente teremos que utilizalo nesta operación.

O método scl multiplica o vector por un escalar (no exemplo o escalar velocidade*delta).

Exemplo de código


Exercicio proposto: Facer que unha bolboreta se mova cara o punto premido na pantalla.

Preparación:

  • Copiade a seguinte imaxe ó cartafol assets:
LIBGDX GRAFICO mariposa.png
Nota: Imaxes obtidas dende http://opengameart.org/content/animated-butterfly
© 2005-2013 Julien Jorge <julien.jorge@stuff-o-matic.com>


  • Crear unha nova clase.


Clase Bolboreta:

  1. import com.badlogic.gdx.math.Vector2;
  2.  
  3. public class Bolboreta  {
  4.          
  5.     public Vector2 direccion,temporal;
  6.     public Vector2 tamano,posicion;
  7.     public float velocidade,velocidade_max;
  8.     public Vector2 puntoDestino;
  9.    
  10.    
  11.     public Bolboreta(Vector2 posicion, Vector2 tamano, float velocidade_max) {
  12.  
  13.         this.posicion=posicion;
  14.         this.tamano = tamano;
  15.         this.velocidade_max=velocidade_max;
  16.        
  17.         temporal = new Vector2();
  18.         direccion = new Vector2(0,0);
  19.         puntoDestino = new Vector2();
  20.     }
  21.  
  22.     public void update(float delta){
  23.        
  24.         temporal.set(direccion);
  25.         posicion.add(temporal.scl(velocidade_max*delta));
  26.        
  27.        
  28.     }
  29. }

Nota: Por motivos de tempo non facemos os métodos get e set.

  • Liña 24: Utilizamos un vector temporal para gardar a dirección, xa que na seguinte liña facemos unha operación de multiplicación que afectaría o vector.
  • Liña 25: Movemos a bolboreta en función do vector dirección.


Clase VectorDireccion:

  1. import com.badlogic.gdx.ApplicationAdapter;
  2. import com.badlogic.gdx.Gdx;
  3. import com.badlogic.gdx.InputProcessor;
  4. import com.badlogic.gdx.graphics.GL20;
  5. import com.badlogic.gdx.graphics.OrthographicCamera;
  6. import com.badlogic.gdx.graphics.Texture;
  7. import com.badlogic.gdx.graphics.g2d.SpriteBatch;
  8. import com.badlogic.gdx.math.Vector2;
  9. import com.badlogic.gdx.math.Vector3;
  10.  
  11. public class VectorDireccion extends ApplicationAdapter implements InputProcessor{
  12.         private SpriteBatch batch;
  13.         private Texture img;
  14.         private Bolboreta bolboreta;
  15.         private OrthographicCamera camara2d;
  16.    
  17.         @Override
  18.         public void create () {
  19.                 batch = new SpriteBatch();
  20.                 img = new Texture("LIBGDX_GRAFICO_mariposa.png");
  21.  
  22.                 bolboreta = new Bolboreta(new Vector2(30,30),new Vector2(50,50),100f);
  23.                
  24.                 camara2d = new OrthographicCamera();
  25.                 camara2d.setToOrtho(false,500f,500f);
  26.                 camara2d.update();
  27.                
  28.                 batch.setProjectionMatrix(camara2d.combined);
  29.                 Gdx.input.setInputProcessor(this);
  30.                
  31.         }
  32.  
  33.         @Override
  34.         public void render() {
  35.                 Gdx.gl.glClearColor(1, 1, 1, 1);
  36.                 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  37.                
  38.                 bolboreta.update(Gdx.graphics.getDeltaTime());
  39.                
  40.                 batch.begin();
  41.                 batch.draw(img, bolboreta.posicion.x, bolboreta.posicion.y);
  42.                 batch.end();
  43.        
  44.         }
  45.        
  46.         @Override
  47.         public void dispose() {
  48.                 img.dispose();
  49.                 batch.dispose();
  50.                
  51.                 Gdx.input.setInputProcessor(null);
  52.                
  53.         }
  54.  
  55.         @Override
  56.         public boolean keyDown(int keycode) {
  57.                 // TODO Auto-generated method stub
  58.                 return false;
  59.         }
  60.  
  61.         @Override
  62.         public boolean keyUp(int keycode) {
  63.                 // TODO Auto-generated method stub
  64.                 return false;
  65.         }
  66.  
  67.         @Override
  68.         public boolean keyTyped(char character) {
  69.                 // TODO Auto-generated method stub
  70.                 return false;
  71.         }
  72.  
  73.         @Override
  74.         public boolean touchDown(int screenX, int screenY, int pointer, int button) {
  75.                 // TODO Auto-generated method stub
  76.                 Vector3 novopunto = new Vector3(screenX,screenY,0);
  77.                 camara2d.unproject(novopunto);
  78.                
  79.                 bolboreta.puntoDestino.set(new Vector2(novopunto.x,novopunto.y));
  80.                
  81.                 Vector2 direccion = bolboreta.puntoDestino.cpy().sub(bolboreta.posicion);
  82.                 bolboreta.direccion.set(direccion.nor());
  83.                
  84.                 return false;
  85.         }
  86.  
  87.         @Override
  88.         public boolean touchUp(int screenX, int screenY, int pointer, int button) {
  89.                 // TODO Auto-generated method stub
  90.                 return false;
  91.         }
  92.  
  93.         @Override
  94.         public boolean touchDragged(int screenX, int screenY, int pointer) {
  95.                 // TODO Auto-generated method stub
  96.                 return false;
  97.         }
  98.  
  99.         @Override
  100.         public boolean mouseMoved(int screenX, int screenY) {
  101.                 // TODO Auto-generated method stub
  102.                 return false;
  103.         }
  104.  
  105.         @Override
  106.         public boolean scrolled(int amount) {
  107.                 // TODO Auto-generated method stub
  108.                 return false;
  109.         }
  110.  
  111.  
  112. }
  • Liña 76-77: Calculamos o punto de destino (onde ten que chegar a bolboreta). Debemos facer un unProyect do mesmo.
  • Liña 79: O punto de destino da bolboreta é o que devolve a cámara ortográfica (pero só nas coordenadas x - y).
  • Liñas 81-82: Calculamos o vector dirección e o asinamos á bolboreta. Cando chamemos ó método update da bolboreta xa se move en función do novo valor do vector dirección.


Podedes probar agora como a bolboreta se dirixe cara ó punto indicado.






TAREFA OPTATIVA A FACER




TAREFA OPTATIVA A FACER: Utilizando o gráfico anterior ou outro elixido por ti, crea un novo tipo de inimigo que cada certo tempo se dirixa cara á posición do alien.

Posteriormente, cando chegues a sección das colisións deberás xestionar cando o inimigo alcanza ó alien e matalo.





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