Carro 2D em JavaFX

Depois de muito tempo sem postar nada (muito ocupado com o mestrado), vo mostrar um pequeno programinha que fiz pra simular a movimentação de um carro.

carro

Ao fundo é um quadriculado para notar a movimentação do carro (que está sempre na mesma posição da tela). A linha azul é a velocidade do carro e a vermelha é a aceleração.

O resultado pode ser conferido pelo JavaWebStart clicando aqui. Infelizmente o google-sites não aceita scripts no html para que eu possa mostrar como um applet! =/

A partir desse básico posso evoluir para várias possibilidades! =D (ainda não decidi)

É apenas uma demonstração de física de um carro 2D (visão de cima), mas  pra fazer esse pequeno carro se movimentar de forma “realista” é preciso usar alguns conceitos descritos a seguir:

Movimentação independente do framerate

O básico que vejo em muitos tutoriais por ai sobre o loop principal de um jogo eh:

while(true){
    capturaComandos();//teclado e mouse
    atualizaVariaveis();//lógica do jogo e posição dos personagens
    redesenha();//desenha tudo na tela
}

Vamos imaginar que um objeto do jogo tenha uma posição (x,y) e uma velocidade (vx,vy). A ideia básica desse loop é capturar as ações do teclado e mouse, e em seguida atualizar a posição (x,y) do objeto.

void atualizaVariaveis(){
    x += vx;
    y += vy;
}

Mas existe um problema ai! Sempre que o loop for executado, a posição do objeto será alterada, sem importar a velocidade que o loop eh executado. Traduzindo, se seu computador for lento, seu objeto vai se mover lentamente, e se seu computador for rápido, seu objeto vai se mover rapidamente! Isso porque a chamada da função atualizaVariaveis() depende básicamente da velocidade do seu computador e da pláca de vídeo!

Uma solução é colocar um ‘sleep(time)’ no loop para fazer o computador ‘dormir’ por um tempo determinado. Esse ‘time’ deve ser calculado de forma que no final, o loop seja chamado sempre a mesma quantidade de vezes por segundo! Vc define um framerate (ex: 30 frames por segundo) e faz os cálculos do ‘time’ baseado no framerate. Assim seu jogo terá a mesma velocidade sempre, 30fps.

Mas essa técnica ainda tem um defeito, se o computador do seu amigo (pra quem vc quer mostrar o jogo) for muito lento e não suportar os 30fps, o jogo fatalmente ficará lento! =/

Para solucionar isso vc deve fazer a movimentação independente do framerate! Igual qualquer jogo profissional faz! Já reparou que em jogos profissionais o framerate varia durante o jogo, mas os objetos em cena se movem na mesma velocidade? Esses jogos tentam tirar o máximo da máquina pra alcançar o melhor framerate! Com mais frames por segundo o jogo parece mais suave e confortavel de jogar! Mas isso sem deixar os objetos mais rápidos ou lentos que o normal!

E como fazer isso? Como movimentar objetos independente do framerate?

O segredo é passar para a função de atualização um parametro referente à passagem de tempo desde a última chamada da função! Assim, a função sabe quanto tempo se passou desde a última chamada para atualizar as variaveis baseadas nesse tempo!

//time em milissegundos
void atualizaVariaveis(long time){
    float factor = time / 1000f;
    x += vx * factor;
    y += vy * factor;
}

E o loop ficaria assim:

long tempoAnterior = System.currentTimeMillis();
while(true){
    capturaComandos();
    long tempoAtual = System.currentTimeMillis();
    long tempoDecorrido = newTime - lastTime;
    tempoAnterior = tempoAtual;
    atualizaVariaveis(tempoDecorrido);
    redesenha();
}

Dessa forma, definindo a velocidade em distância por segundos, e o ‘time’ em milissegundos, a posição do objeto é atualizada de acordo com o tempo decorrido desde a última atualização.

Se passou 1000ms, ‘factor’ assumirá o valor 1.0 (1 segundo). Se passou 500ms, ‘factor’ assumirá o valor 0.5 (meio segundo).

Representação de um carro

Depois de fugir um pouco do assunto, vamos começar a falar do carro =D

Para representar o carro utilizei as seguintes variáveis:

  • x,y (posição do carro)
  • velocity (velocidade do carro)
  • angleVelocity (ângulo da movimentação do carro em radianos)
  • vel_max (valor máximo que a variavel ‘velocity’ pode assumir)
  • turnAngle (velocidade de giro do carro)
  • aceleration (aceleração do carro)
  • friction (atrito quando o carro anda de frente)
  • lateralFriction (atrito quando o carro derrapa de lado)

Com essas variáveis, o ângulo em que o carro se movimenta pode ser diferente do ângulo em que ele acelera! Possibilitando a simulação de derrapagens em curvas! Separei a fricção normal da fricção lateral para que o carro desacelere mais no sentido lateral que no sentido do carro (direção das rodas).

Atualização das variáveis

A primeira variável a atualizar é o ângulo do carro quando giramos para esquerda ou direita:

if(left){//quando a seta para esquerda estiver pressionada
    angleCar += turnAngle * factor;
}
if(right){//quando a seta para direita estiver pressionada
    angleCar -= turnAngle * factor;
}

Reparem que a rotação do carro também depende de ‘factor’ para que o carro sempre gire com a mesma intensidade independente do framerate.

Agora a aceleração:

if(up){//quando a seta para cima estiver pressionada
    var vxc = Math.cos(angleVelocity) * velocity;
    var vyc = Math.sin(angleVelocity) * velocity;
    var va = aceleration * factor;
    var vxa = Math.cos(angleCar) * va;
    var vya = Math.sin(angleCar) * va;
    var vx = (vxc + vxa);
    var vy = (vyc + vya);
    var v = Math.sqrt(vx*vx + vy*vy);
    var a = Math.atan2(vy, vx);
    velocity = Math.min(v,vel_max);
    angleVelocity = a;
}

O que eu faço nesse código é calcular as componentes x e y da velocidade e da aceleração (multiplicadas por ‘factor’ também).  Somar as componentes que dá o efeito de aceleração, e por fim, calcular o novo ângulo e intensidade  da velocidade (que não pode passar da velocidade máxima).

Depois disso, vem a parte do atrito! O código é bem complexo, envolve muita noção de vetores e trigonometria, e não vou me aprofundar muito nisso. Mas basicamente eu separo a velocidade em duas componentes, uma componente com o mesmo sentido do carro (normal), e outra componente transversal ao sentido do carro (lateral). As componentes ‘normal’ e ‘lateral’ são subtraidas de ‘friction’ e ‘lateralFriction’ respectivamente. E depois junto as novas componentes ‘normal’ e ‘lateral’ para formar a nova velocidade (intensidade e ângulo). Assim, se o carro está se movendo de lado ele irá parar mais rápido do que se estivesse se movendo de frente.

Depois que calculei os ângulos e velocidades novos, atualizo a posição (x,y) do carro:

x += Math.cos(angleVelocity) * velocity * factor;
y += Math.sin(angleVelocity) * velocity * factor;

Novamente lembrando que deve-se multiplicar a velocidade por ‘factor’ para que o carro se movimente independente do framerate.

Configurações do carro

Pode parecer estranho que algumas variáveis como a fricção sejam variáveis e não constantes. Mas isso é útil para desenvolver um jogo onde existam carros diferentes, com comportamenteo diferentes. Cada carro com sua aceleração e giro. Ou pra simular óleo na pista (vc pode diminuir temporariamente a aceleração e o atrito) fazendo o carro deslisar.

Nesse teste utilizei as seguintes configurações:

aceleration: 200
vel_max: 400
turnAngle: 90°
friction: 50;
lateralFriction: 400;

Com essas configurações o carro apresentou uma boa velocidade e aceleração, também com um bom controle em curvas.

Dificuldades encontradas

Bem, a principal dificuldade foi desenvolver a parte do atrito lateral! O resto das contas foram simples.

Uma coisa é muito importante! As funções trigonométricas de Java presentes na classe Math utilizam ângulos em radianos (em vários momentos errei utilizando valores em graus nas contas) e o JavaFX utiliza ângulos em graus para fazer a rotação! Então é preciso atenção para trabalhar com ângulos!

0 Responses to “Carro 2D em JavaFX”



  1. Deixe um comentário

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s





%d blogueiros gostam disto: