LIBGDX Vector direccion
Sumario
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).
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:
- 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).