Tutorial: Criando um jogo de car parking – Parte 5: Acelerando o carro

A funcionalidade de virar as rodas do carro quando o jogador clica em uma das setas de volante já está implementada.

Porém, o carro ainda não anda, pelo fato de ainda não ter sido implementada a aceleração dele.

Vamos fazer esse carro andar. o

Vimos no último tutorial como adicionar a movimentação nas rodas do carro, caso o jogador vire o volante por meio dos botões de interação na tela. Para isso, implementamos a detecção de clique nos botões de virar e fizemos o jogo virar as rodas dianteiras do carro gradualmente. Nesse tutorial nós adicionaremos mais uma função no jogo: a aceleração e desaceleração do carro. Além disso, vamos adicionar os tratamentos de atrito nas quatro rodas, para que o carro não derrape quando o jogador o virar.

Para fazermos o carro acelerar ou desacelerar, utilizaremos uma lógica parecida com aquela utilizada no último tutorial. Não direi aqui a importância de você já ter finalizado o tutorial passado para dar continuidade nesse. Para virar as rodas dianteiras, nós atualizamos a velocidade dos motores que viram as rodas a cada novo quadro mostrado. Isso significa que, para fazermos elas virarem, basta atualizar a variável que armazena o valor de angulação que as rodas precisam estar no momento. Será mais ou menos assim a lógica utilizada para fazer o carro se movimentar.

Quando o jogador não aperta nenhum botão de aceleração (para frente ou para trás), não são aplicadas forças no centro dos objetos rígidos que representam as rodas dianteiras. Ou seja, a variável que armazena a força de aceleração do carro será igual a zero. Caso o jogador aperte no botão de acelerar para frente, serão aplicadas forças na direção e sentido que as rodas dianteiras estão apontando no momento. Caso o jogador aperte o botão de ré, então serão aplicadas forças na mesma direção, porém com sentido oposto ao das rodas dianteiras. Para isso, precisamos adicionar uma nova variável que armazena o valor das forças que estão sendo aplicadas nos objetos referentes aos pneus dianteiros. Essa variável será nomeada “forcaMovimentadora” e armazenará um valor positivo se o jogador estiver acelerando para frente, o valor zero se o jogador não estiver acelerando ou um valor negativo se o jogador estiver dando a ré.

Assim como no último tutorial, onde criamos uma variável que armazena o número identificador do toque para saber quando o jogador tira o dedo da tela, aqui teremos outra variável com a mesma função, porém para identificar quando o jogador para de acelerar o carro. Assim sendo, adicionaremos uma variável nomeada “cliqueAndarID” para identificar quando há mudança na aceleração do carro.

Iniciando a edição de código, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

int cliqueAndarID;

float forcaMovimentadora;

void aplicarAtrito();

logo após essa linha:

cocos2d::CCSprite* cima;

Note que criamos as duas variáveis mencionadas e declaramos um método pertencente à classe “HelloWorld” denominado “aplicarAtrito”. Esse método será responsável por adicionar o atrito lateral nas quatro rodas do carro, para que seja evitada a derrapagem. Se não for adicionado o atrito lateral, a movimentação do carro será semelhante a dirigi-lo sobre o gelo escorregadio, por exemplo. Salve o arquivo “HelloWorldScene.h”, já fizemos todas as modificações necessárias nele.

Abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::cliqueAndarID = -1;

HelloWorld::forcaMovimentadora = 0.0;

b2BodyDef definicaoParedes;

definicaoParedes.type = b2_staticBody;

b2Body* paredes = HelloWorld::mundoFisico->CreateBody(&definicaoParedes);

b2Vec2 vParedes[4];

vParedes[0].Set(0,size.height/PTM_RATIO);

vParedes[1].Set(size.width/PTM_RATIO,size.height/PTM_RATIO);

vParedes[2].Set(size.width/PTM_RATIO,0);

vParedes[3].Set(0,0);

b2ChainShape geometriaParedes;

geometriaParedes.CreateLoop(vParedes,4);

b2FixtureDef cascaParedes;

cascaParedes.shape = &geometriaParedes;

paredes->CreateFixture(&cascaParedes);

logo abaixo dessas:

HelloWorld::juntaRodaTraseiraDireita = static_cast<b2PrismaticJoint*>(HelloWorld::mundoFisico->CreateJoint(&definicaoJuntaRodaTraseiraDireita));

HelloWorld::cliqueVirarID = -1;

HelloWorld::anguloRoda = 0.0;

Com a inserção desse código nós inicializamos as duas variáveis que criamos e adicionamos as paredes que circundam a tela do celular. Sem essas paredes, o carro sai da tela e, normalmente, você o acaba perdendo no mundo do Box2D, devido à falta de foco da câmera. Agora adicione as seguintes linhas de código:

else if(HelloWorld::cima->boundingBox().containsPoint(ponto)) {

    HelloWorld::forcaMovimentadora = 20.0;

    HelloWorld::cliqueAndarID = toque->getID();

}

else if(HelloWorld::baixo->boundingBox().containsPoint(ponto)) {

    HelloWorld::forcaMovimentadora = -20.0;

    HelloWorld::cliqueAndarID = toque->getID();

}

logo abaixo dessas:

else if(HelloWorld::dir->boundingBox().containsPoint(ponto)) {

    HelloWorld::anguloRoda = -M_PI/3.0;

    HelloWorld::cliqueVirarID = toque->getID();

}

Acabamos de identificar o momento que o jogador clica em um dos botões de aceleração do carro. Notem que, caso o jogador aperte para frente, a variável “forcaMovimentadora” é igualada ao valor 20 e, caso o jogador aperte para trás, ela é igualada ao valor -20. Isso é o suficiente para que o carro ande para frente ou para trás, invertendo o sentido da força unicamente com o acréscimo do sinal negativo. Para finalizar a implementação dos botões de interação, adicione as seguintes linhas de código:

else if(toque->getID()==HelloWorld::cliqueAndarID) {

    HelloWorld::forcaMovimentadora = 0.0;

    HelloWorld::cliqueAndarID = -1;

}

logo abaixo dessas:

if(toque->getID()==HelloWorld::cliqueVirarID) {

    HelloWorld::anguloRoda = 0.0;

    HelloWorld::cliqueVirarID = -1;

}

Notem que, ao ser identificado que o jogador soltou um dos botões de aceleração do carro, o valor da variável “forcaMovimentadora” é igualado a zero. Isso significa que o carro não sofrerá aceleração nem para frente nem para trás, fazendo com que ele continue em movimento constante. Para finalizar a implementação da aceleração, adicione as seguintes linhas de código:

b2Vec2 forca = HelloWorld::corpoRodaDianteiraEsquerda->GetWorldVector(b2Vec2(1,0));

forca.x = forca.x*HelloWorld::forcaMovimentadora;

forca.y = forca.y*HelloWorld::forcaMovimentadora;

HelloWorld::corpoRodaDianteiraEsquerda->ApplyForceToCenter(forca);

HelloWorld::corpoRodaDianteiraDireita->ApplyForceToCenter(forca);

HelloWorld::aplicarAtrito();

logo abaixo dessa:

HelloWorld::mundoFisico->Step(dt,3,2);

O que fizemos nesse trecho de código? A cada novo quadro apresentado, são adicionadas forças nos objetos rígidos que representam as rodas dianteiras do carro. Note que essas forças são calculadas com base no valor da variável “forcaMovimentadora”, aquela que modificamos caso o jogador aperte um dos botões de aceleração. Caso essa variável possua o valor zero, então nenhuma força é aplicada nos corpos rígidos. Caso a variável possua algum valor, então o jogador está apertando um dos botões de aceleração e isso fará com que uma força seja aplicada em cada roda dianteira do carro. As forças são aplicadas na mesma direção que a roda aponta e com o sentido determinado pelo sinal do valor de “forcaMovimentadora”. Quanto ao valor do módulo da força, esse é igual a 20. Simples, não? =]

Ainda não terminamos a implementação do jogo. É possível notar que declaramos o método “aplicarAtrito” e realizamos uma chamada dele logo após aplicarmos as forças nas rodas dianteiras do carro. Porém, esse método ainda não foi implementado. Para finalizar o tutorial de hoje, adicione as seguintes linhas de código no final do arquivo:

void HelloWorld::aplicarAtrito() {

    b2Vec2 direcaoRoda,velocidadeLocal;

    direcaoRoda = HelloWorld::corpoRodaDianteiraEsquerda->GetWorldVector(b2Vec2(1,0));

    velocidadeLocal = HelloWorld::corpoRodaDianteiraEsquerda->GetLinearVelocityFromLocalPoint(b2Vec2(0,0));

    direcaoRoda.operator*=(direcaoRoda.x*velocidadeLocal.x + direcaoRoda.y*velocidadeLocal.y);

    HelloWorld::corpoRodaDianteiraEsquerda->SetLinearVelocity(direcaoRoda);

    HelloWorld::corpoRodaDianteiraDireita->SetLinearVelocity(direcaoRoda);

    direcaoRoda = HelloWorld::corpoRodaTraseiraEsquerda->GetWorldVector(b2Vec2(1,0));

    velocidadeLocal = HelloWorld::corpoRodaTraseiraEsquerda->GetLinearVelocityFromLocalPoint(b2Vec2(0,0));

    direcaoRoda.operator*=(direcaoRoda.x*velocidadeLocal.x + direcaoRoda.y*velocidadeLocal.y);

    HelloWorld::corpoRodaTraseiraEsquerda->SetLinearVelocity(direcaoRoda);

    HelloWorld::corpoRodaTraseiraDireita->SetLinearVelocity(direcaoRoda);

}

Nesse método, nós primeiramente capturamos o vetor que dá a direção e o sentido da roda dianteira esquerda e o armazenamos na variável “direcaoRoda”. Também armazenamos o vetor que dá a direção e o sentido da velocidade linear das rodas no ambiente do Box2D. Notem que esses vetores não apontam necessariamente para a mesma direção. Caso o carro esteja andando para frente e o jogador vira a roda, então o vetor da roda passa a apontar um pouco mais para a direita, por exemplo. Isso faz com que o carro não mude a sua movimentação instantaneamente, fazendo-o derrapar como se estivesse no gelo. É necessário excluirmos a movimentação lateral de todas as rodas, de forma que sejam aplicadas forças de atrito e o carro passa a se movimentar como se estivesse em um asfalto. Para isso, nós calculamos a velocidade do carro na direção que a roda aponta e forçamos para que a velocidade da roda seja na direção que a roda aponta. Isso excluirá qualquer velocidade fora da direção da roda. Aplicando essa velocidade nas quatro rodas, nós faremos com que o carro ande como se fosse no asfalto. Legal, né?

Compile o código e execute o game. O resultado será algo parecido com o que é mostrado no vídeo a seguir. Note que as rodas dianteiras viram conforme o que o jogador vira (funcionalidade implementada no tutorial passado) e note que o carro não desliza como se estivesse no gelo.

Vimos nesse tutorial como implementar as funcionalidades de aceleração para frente e para trás no carro. Para isso, precisamos aplicar forças nos objetos rígidos que representam as rodas dianteiras do carro. As forças apontavam no sentido da roda ou no sentido oposto dela. Para isso, bastou armazenarmos um valor negativo na variável que armazena a força a ser aplicada nas rodas, caso o jogador estivesse dando a ré. Vimos também a necessidade de incluir forças de atrito em todas as rodas para que o carro não deslize no ambiente como se ele estivesse sobre o gelo.

Assim, finalizamos mais um conjunto de tutoriais e temos mais um game diferente implementado. Ainda não pensei no jogo que implementaremos no próximo conjunto de tutoriais. Deixarei novamente vocês na curiosidade. =P

Um grande abraço e até mais. []

Santiago Viertel

Santiago Viertel

Formado em Bacharelado em Ciência da Computação (UDESC), mestre e doutorando em Análise de Algoritmos (UFPR). Foi programador da Céu Games por 8 anos. Possui a preferência por jogos de estratégia e de tiro em primeira pessoa. Jogando bastante DotA 2, Left 4 Dead 2 e Age of Empires II HD.

Send this to a friend