Matemática para jogos - Como Utilizar Vetores
No nosso jogo, desenvolvido na engine Unity, o Zumbi precisa perseguir a heroína, como fazer isso?
Como podemos observar na imagem abaixo, a heroína está na posição x = 100, y = 30 e o zumbi, na posição x = 200, y = 120.
Vamos escrever um código que faça com que o zumbi se desloque até a posição da heroína.
Uma boa prática na hora de resolver problemas é dividi-lo em partes simples e resolver cada uma antes de resolver o problema todo. Nesse caso, qual é a parte mais simples do problema?
Se precisamos sair da posição (200, 120) para a posição (100,30), precisamos alterar a posição do inimigo em dois eixos - horizontal e vertical. Vamos mover o inimigo apenas na horizontal, para depois mover ele no outro eixo.
Se eu estou na posição 200 e quero chegar na posição 100, qual a distância que eu preciso andar?
Na imagem temos d representando a distância que precisamos nos mover para chegar até a posição desejada. Fica fácil perceber que o zumbi precisa andar 100 unidades para chegar na posição da heroína.
Só que o computador não consegue "ver" a imagem e descobrir essa distância. Precisamos achar uma maneira dele calcular essa distância. Nesse caso, para chegarmos no número 100,precisamos subtrair a posição X do zumbi da posição X da heroína.
Depois, é só somar essa distância na posição atual do zumbi para ele se deslocar!
float distânciaX = zumbi.x - heroína.x;
zumbi.x += distânciaX ;
Abaixo, você confere como o código fica na Unity.
public class Zumbi : MonoBehaviour {
[SerializeField]
private Transform alvo;
private void Update()
{
float distanciaX = this.transform.position.x - this.alvo.position.x;
this.transform.position = new Vector3( this.transform.position.x + distanciaX , this.transform.position.y, this.transform.position.z);
}
}
O componente que guarda as informações de posição dos objetos é o Transform
, por isso, declaramos uma variável "alvo" do tipo Transform
que vai receber seu valor por meio do inspetor da Unity.
Apesar de estarmos trabalhando em um jogo 2D, a Unity ainda guarda as posições com 3 valores (X, Y e Z). Por isso, na hora de atribuirmos a nova posição ao zumbi, utilizamos um Vector3
.
this.transform.position = new Vector3( this.transform.position.x + distanciaX, this.transform.position.y, this.transform.position.z);
O que acontece agora se testarmos nosso código?
Reparem que o zumbi foi para o lado errado, se afastando da heroína.
Acontece que o sentido do deslocamento importa e, quem define o sentido que estamos andando é o sinal de positivo(+) ou negativo(-).
Quando desenhamos o eixo horizontal, utilizamos uma seta (preta) para representá-lo. Ao mesmo tempo que usamos outra seta (laranja) para representar a distância que devemos andar.
A seta que representa o eixo X aponta na direção em que esse eixo cresce, ou seja se andarmos naquela direção vamos aumentar o valor da nossa posição em X. Nesse caso, estamos indo no sentido positivo (+) do eixo.
A seta que representa o caminho a percorrer aponta no sentido contrário do eixo, isso indica que queremos andar no sentido negativo (-) desse eixo. Como podemos mudar a conta que estamos fazendo para refletir isso?
A conta atual é:
float distânciaX = zumbi.x - heroína.x;
colocando os valores ficaria:
float distânciaX = 200 - 100;
O valor numérico dessa conta está correto, mas queremos o sinal contrário. Como a ordem dos valores de uma subtração importa, podemos inverter os valores da nossa conta para termos o resultado desejado.
float distânciaX = heroína.x - zumbi.x;
distancaX = 100 - 200;
distanciaX = -100;
Se voltarmos no nosso código, precisamos trocar somente a linha onde fazemos essa subtração.
private void Update()
{
**float distanciaX = this.alvo.position.x - this.transform.position.x;**
this.transform.position = new Vector3( this.transform.position.x + distanciaX , this.transform.position.y, this.transform.position.z);
}
Será que essa abordagem funciona também para o eixo vertical?
Vamos testar. No caso do eixo Y, a heroína está na posição 120 e o zumbi na posição 30.
Colocando esses valores na conta:
float distânciaY = heroína.y - zumbi.y;
float distânciaY = 30 - 120;
float distânciaY = - 90;
Chegamos ao número -90 e ele faz sentido, pois o zumbi tem que andar 90 unidades na direção oposta a direção de crescimento do próprio eixo Y.
Podemos então generalizar, ou seja, sempre que precisamos calcular o sentido e a distância entre dois pontos, precisamos subtrair a posição X e Y do nosso destino pelo X e Y da nossa origem.
Voltando ao nosso código da classe zumbi, adicionamos o cálculo da *distânciaY *para que o zumbi vá atrás da heroína.
private void Update()
{
float distanciaX = this.alvo.position.x - this.transform.position.x;
float distanciaY = this.alvo.position.y - this.transform.position.y;
this.transform.position = new Vector3( this.transform.position.x + distanciaX, this.transform.position.y + distanciaY, this.transform.position.z);
}
Será que agora chegamos no comportamento desejado?
Parece que não, agora o zumbi está se teleportando para a posição da heroína.
Resumindo: sempre que temos um comportamento de perseguição precisamos subtrair a posição do destino da posição da origem para achar a direção que temos que seguir e a distância que estamos do alvo.
Ao somar o resultado da subtração na posição do inimigo ele se moverá instantâneamente para o destino.
Como esse texto está ficando longo, resolver apenas esse problemas do perseguidor teleportar para a posição de destino em um outro post.Você conhece alguma outra situação onde podemos usar essa mesma lógica? Qual outro problema relacionado a jogos você gostaria de ver explicado aqui no blog? Deixa nos comentários!
Aproveita e olha lá no site da alura os cursos que temos sobre jogos, tenho certeza que você vai encontrar algo interessante para aprender.