O uso de trigonometria em ambientes virtuais nos permite simular as forças da física com realismo. Vamos tratar nesse post do lançamento obliquo. Para saber o que é isso preparei logo abaixo um jogo que demonstra. Mude a força do lançamento e seu ângulo para obter diferentes resultados. esse jogo leva em conta uma força constante de gravidade atuando sobre y. Não considero uma força de resistência em x, como a resistência do ar no mundo real.
Lançamento de Projétil
Força: 50 |
Ângulo: 45° |
O conceito é similar ao do jogo Angry Birds, amplamente conhecido. Alterações no ângulo e força alteram a altura máxima alcançada, o tempo da trajetória e o alcance. Para definir tudo isso esse jogo só precisa de 2 parâmetros: a força empregada no lançamento e o ângulo. Porém para representar graficamente um objeto em movimento nós precisamos, em um instante do tempo por todo o percurso, saber suas coordenadas em relação ao eixo x e y do plano cartesiano. Como extrair a coordenada (x,y) se temos somente a força e o ângulo? É aí que entra a trigonometria!
Vamos ver o gráfico para analisar as informações que temos no momento inicial do lançamento:
Se o usuário setou uma força de 50 e angulo de 45 graus, temos a figura acima. A ponta da seta representa onde o objeto deverá estar no final do primeiro período de tempo. Porém, essa localização representa um valor no eixo x e no eixo y, como mostra a figura abaixo:
Como achar essa posição (x,y)? Analise bem essa gravura. Ela te lembra alguma coisa? Triângulo retângulo! Veja abaixo:
Todo triângulo retângulo tem um ângulo reto de 90 graus, uma hipotenusa (o maior lado) e dois catetos: adjacente (o lado colado ao angulo de referência (45 graus)) e o oposto (o lado oposto a esse ângulo de 45 graus). Se acharmos as medidas desses catetos, temos então a localização x,y do objeto daquele instante no tempo. Como determinar as medidas dos catetos? Vamos utilizar as clássicas relações trigonométricas:
O seno de um ângulo é igual ao cateto oposto a esse ângulo dividido pela hipotenusa. Conforme abaixo:
Já o cosseno de um ângulo é igual ao cateto adjacente a esse ângulo dividido pela hipotenusa. Conforme abaixo:
Assim, substituindo os valores, poderemos achar a força em y conforme abaixo:
Utilize uma calculadora científica para achar o seno de 45. Achamos a força atuante no eixo y! Agora para achar a força atuante em x, vamos utilizar a fórmula abaixo:
Pronto, temos agora a força atuante em x. Para achar as coordenadas devemos ter em mente que o movimento em x é um simples M.R.U. (Movimento Retilínio Uniforme). Isso quer dizer que a velocidade do deslocamento em x (da esquerda para a direita) não se altera pois nesse eixo não temos nenhuma resistência ao projétil lançado (não temos o ar nem empurrando ou criando resistência). Assim basta somar a força ao ponto original de x e multiplicar pelo tempo decorrido que teremos essa coordenada. Logo, se x começou no ponto 0 do plano cartesiano, x agora vale 35,3 após o primeiro período de tempo decorrido. Então a fórmula é essa:
X = (Pos.emX.Original) + (Força.emX) x (Tempo.decorrido)
Substituindo os valores temos:
X = 0 + 35,3 x 1 = 35,3 (no primeiro período de tempo o projétil andou 35,3 em x)
X = 0 + 35,3 x 2 = 70,6 (no segundo período de tempo o projétil andou 70,6 em x)
X = 0 + 35,3 x 3 = 100,9 (no terceiro período de tempo o projétil andou 100,9 em x)
Ou seja, no terceiro período de tempo a coordenada em x terá se deslocado 100,9 em relação ao ponto original, e isso em uma velocidade constante.
Já para achar o ponto em Y é um pouco mais complicado, pois agora temos a força da gravidade atuando. O movimento em Y é um M.R.U.V. (Movimento Retilínio Uniforme Variado). É variado por que a velocidade varia ao longo do tempo. Ela vai desacelerando a medida que chega no ponto mais alto e acelera até o chão, tudo devido a força constante da gravidade. Assim no próximo instante de tempo a força no eixo y sofrerá alteração. Veja o gráfico abaixo:
Para calcular o ponto em y num determiado período do tempo a partir dessa força vamos usar a fórmula:
Y = (Pos.emY.Original) - (Força.emY) x (Tempo.decorrido) + (Força.Gravidade) x (Tempo.decorrido²)/2
Substituindo os valores temos (supondo uma força 10 na gravidade):
Y = 0 - 35,3 x 1 + 10 x 1²/2 = -30,3 (no primeiro período de tempo o projétil andou 30,3 em y. Observe que andou menos que x nesse mesmo período por causa da gravidade. O valor está negativo por que no plano cartesiano computacional, quanto menor o valor de Y mais alto o projétil vai. Isso se da porque as coordenadas 0,0 estão no canto superior esquerdo do monitor)
Y = 0 - 35,3 x 2 + 10 x 2²/2 = -50,6 (no segundo período de tempo o projétil andou 50,6 em y. Observe que andou menos que x nesse mesmo período por causa da gravidade)
Y = 0 - 35,3 x 3 + 10 x 3²/2 = -60,9 (no segundo período de tempo o projétil andou 60,9 em y. Observe que andou menos que x nesse mesmo período por causa da gravidade)
Comparando os valores tanto no eixo x como em y os três períodos de tempo chegamos a conclusão:
Perceba que enquanto no eixo X o projétil se move a uma velocidade constante, no eixo Y ele sofre desaceleração devido a gravidade. Em determinado momento a força da gravidade terá anulado totalmente a força em Y, fazendo com que o projétil comece a descer ao invés de subir.
Dessa forma, podemos simular o lançamento oblíquo com a ajuda da trigonometria algumas contas.
Segue o código do game no início:
<center>
<h1>Lançamento de Projétil</h1>
<script>
//força e angulo do lançamento definido pelo usuario atraves do slider
var xo = 50;
var yo = 265;
var forca = 50;
var forca_gravidade = 10;
var t = 0;
var dt = 0.2;
var angulo = 45;
var lancou = false;
</script>
<table>
<tr>
<td>
Força: <span id="forca_label">50</span><br/>
<div id="slider_forca" style="width: 100px;"></div>
<script>
$('#slider_forca').slider({min:10,max:100, value:50, slide:function(event, ui){ forca = parseInt(ui.value); $('#forca_label').html(ui.value); }} );
</script>
</td>
<td>
Ângulo: <span id="angulo_label">45°</span><br/>
<div id="slider_angulo" style="width: 100px;"></div>
<script>
$('#slider_angulo').slider({min:1,max:90, value:45, slide:function(event, ui){ angulo = parseInt(ui.value); $('#angulo_label').html(ui.value+'°'); }} );
</script>
</td>
<td>
<br/>
<input type="button" value="Lançar" onclick="lancou = true;"/>
</td>
</tr>
</table>
<br/>
<br/>
<canvas id="game_lancamento" width="416" height="302" style="border: 1px solid #000000;">
<p>Seu navegador não suporta HTML5!</p>
</canvas>
<script >
// cria o contexto grafico no canvas
var drawingCanvas = document.getElementById('game_lancamento');
var ctx = drawingCanvas.getContext('2d');
var framecount = 0;
// carrega imagens
var bgReady1 = false;
var bgImage1 = new Image();
bgImage1.onload = function () {
bgReady1 = true;
};
bgImage1.src = 'campo_1.png';
var bgReady2 = false;
var bgImage2 = new Image();
bgImage2.onload = function () {
bgReady2 = true;
};
bgImage2.src = 'campo_2.png';
var bolaReady = false;
var bolabg = new Image();
bolabg.onload = function () {
bolaReady = true;
};
bolabg.src = 'bola_golf.png';
//objetos do jogo
var bola = {
x:50,
y:265,
w:30,
h:30
};
// frame update
var update_lancamento = function(modifier){
if(lancou && bola.y <= 265){
var rad= parseInt(angulo) * Math.PI / 180;
var vxo = forca * Math.cos(rad);
var vyo = forca * Math.sin(rad);
bola.x = xo + vxo * t;
bola.y = yo - vyo * t + forca_gravidade * Math.pow(t,2)/2;
t = t + dt;
}else if(lancou){
// bola já tocou o chão
lancou = false;
bola.x = 50;
bola.y = 265;
t = 0;
}
}
//render game
var render_lancamento = function(){
//clear no canvas
if (bgReady1 && bgReady2) {
ctx.beginPath()
if(lancou){
ctx.drawImage(bgImage2, 0, 0);
}else{
ctx.drawImage(bgImage1, 0, 0);
//desenha atrajetoria da bola conforme o angulo e força
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.strokeStyle = 'rgb(255, 255, 255)';
ctx.moveTo(bola.x+14, bola.y+14);
var rad= parseInt(angulo) * Math.PI / 180;
var vxo = forca * Math.cos(rad);
var vyo = forca * Math.sin(rad);
ctx.lineTo(bola.x+vxo+14,bola.y-vyo+14);
ctx.arc(bola.x+vxo+14, bola.y-vyo+14, 5, 0, Math.PI*2, true);
ctx.stroke();
ctx.fill();
}
ctx.closePath()
}
if (bolaReady) {
ctx.drawImage(bolabg, bola.x, bola.y);
}
}
//função que irá controlar o game loop
var main_lancamento = function () {
var now = Date.now();
var delta = now - then;
update_lancamento(delta / 1000);
render_lancamento();
then = now;
};
//iniciando o game
var then = Date.now();
setInterval(main_lancamento, 70);
</script>
</center>
Não esqueça de importar no <head> o jquery e o jquery ui
<head>
<title>Jogo</title>
<link href='http://code.jquery.com/ui/1.9.2/themes/redmond/jquery-ui.css' rel='stylesheet'/>
<script src="http://code.jquery.com/jquery-1.9.1.js" type="text/javascript"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js" type="text/javascript"></script>
</head>
Atenciosamente,
Gustavo Marques