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:
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!