Veja neste artigo como simular um “tiro” como em um jogo 2D estilo plataforma, utilizando regras matemáticas para traçar o percurso do disparo.

Você já deve ter visto, em algum momento, um jogo 2D estilo plataforma, onde personagem disparam uns contra os outros, estando eles em posições horizontal e verticalmente distantes no cenário. Mas como saber movimentar o “tiro” até que ele atinja o oponente? É exatamente isso que abordaremos neste artigo, veremos que a importância da matemática em situações como essa.

De fato, o Delphi não foi pensado para ser uma plataforma de desenvolvimento de jogos, uma boa prova disso é que as posições horizontal e vertical dos controles são dadas por números inteiros, o que nos fará perder um pouco de precisão no disparo, mas nada que atrapalhe tanto. Então, mãos à obra.

Inicialmente, vejamos a parte matemática por trás disso tudo. Observe a imagem abaixo:

Interpretação geométrica do cenário

Figura 1 - Interpretação geométrica do cenário

Como se trata de um jogo 2D, o cenário é baseado no plano cartesiano, onde a posição de cada elemento é dada por um par ordenado (X, Y), no Delphi, (Left, Top). Na figura, o personagem A encontra-se na posição (Xa, Ya) e o personagem B encontra-se na posição (Xb, Yb). A distância vertical entre eles é dada por Dy = Ya-Yb, e, de forma análoga, a distância horizontal é dada por Dx = Xa-Xb.

Para simular o disparo, é necessário traçar uma reta entre os pontos A e B. Do estudo da geometria, sabemos que toda reta é dada por uma equação do tipo: Y = a.X + b, onde “a” é chamado de coeficiente angular e define a inclinação da reta; “X” é a posição horizontal; e “b” indica o valor de Y para X=0, que deve ser somado para “equilibrar” a posição vertical, caso contrário, estaríamos supondo que a reta passa pela origem (o que nem sempre é verdade).

Para encontrarmos os valores de “a” e “b”, usamos as seguintes expressões:

a = Dy/Dx = (Ya-Yb)/(Xa-Xb)

b = Yb - a.Xb (equivale a resolver a equação Y = a.X+b, para X=Xb e Y=Yb)

Com isso, temos o bastante para definir uma reta que representará o percurso descrito pelo disparo, desde o atirador até o alvo. Porém, antes de codificar isso no Delphi, é necessário entender uma pequena diferença entre o posicionamento de um ponto no plano cartesiano e em um form no Delphi. No plano cartesiano, o eixo Y é crescente no sentido ascendente, já no Delphi, ocorre o inverso, o Top dos componentes cresce “para baixo”. Para adaptarmos as teorias matemáticas vistas aqui ao Delphi, é preciso fazer a seguinte alteração: a posição Y dos controles (personagens e disparo) deverá ser calculada como a diferença entre a altura do form que os contém e da sua posição Top, ou seja Y = form.Height - controle.Top;

Partindo para a prática, crie uma nova aplicação VCL, com um form e, neste form, adicione 3 TShape, 1 TButton e 1 TTimer e configure-os da seguinte forma:

Listagem 1: Definição dos componentes utilizados


TShape
Name = ‘Tiro’
Left = 10
Top = 500
Width = 25
Height = 25
Brush.Color = clNavy //ou outra cor, apenas para diferenciar do personagem 
Shape = stCircle
  end
TShape
Name = ‘Atirador’
Left = 10
Top = 500
Width = 60
Height = 60
  end
TShape
Name = ‘Alvo’
Left = 500
Top = 100
Width = 60
Height = 60
TButton
Name = ‘btnDisparar’
Caption = 'Disparar'
  end
TTimer
Name = ‘Timer1’
Enabled = False
Interval = 1
  end

Depois, declare as seguintes variáveis na seção private do form:

Listagem 2: Definição das variáveis utilizadas


  x0, y0, x1, y1, a, b:Real;
  direcao:Integer;

A variável direção será usada para indicar se o alvo está na frente(da esquerda para a direita) ou atrás do atirador. Se estiver na frente, seu valor será 1, caso contrário, receberá -1.

No evento onClick do botão, escreva o seguinte trecho de código:

Listagem 3: Definindo o percurso do disparo


  x0 := Atirador.Left;
  y0 := Self.Height - Atirador.Top;
  x1 := Alvo.Left;
  y1 := Self.Height - Alvo.Top;

  a := ((y1-y0)/(x1-x0));
  b := y1 - a*x1;

  if (Alvo.Left > Alvo.Left) then
     direcao := 1
  else
     direcao := -1;

  Timer1.Enabled := True;

Por fim, no evento onTimer do Timer1, codifique da seguinte maneira:

Listagem 4: Movimentando o tiro até o alvo


  Tiro.Left := Tiro.Left + direcao;
  Tiro.Top := Round(Self.Height - (a*Tiro.Left + b));

  if ((direcao = 1) and (Shape3.Left >= Shape2.Left))  or 
     ((direcao = -1) and (Shape3.Left <= Shape2.Left))then
  begin
     Timer1.Enabled := False;
     Tiro.Left := Shape1.Left;
     Tiro.Top := Shape1.Top;
  end;

O if da listagem 4 é usado para reposicionar o tiro quando ele atingir o alvo ou passar deste.

Usamos ainda a função Round para arredondar o valor de Y para o tiro, pois, como foi dito, o Delphi trabalha com números inteiros para as propriedades Top e Left.

Bem, esse foi um exemplo bem simples de como simular um disparo em jogos 2D. Caso fique alguma dúvida sobre o conteúdo exposto, ponho-me à disposição para esclarecimentos, através dos comentários ou por email (de preferência pelos comentários, para que outros possam ver).

Por hoje é só, pessoal. Espero que tenham gostado. Até a próxima!