Tutorial: Criando um jogo de car parking – Parte 3: Adicionando os pneus

Criamos um cenário para o nosso game e parte do mundo físico que o representa.

Agora criaremos outros elementos indispensáveis para a simulação física: os corpos que representam as rodas do carro.

Partiu adiantar o nosso game de car parking.

No último tutorial, nós criamos o mundo físico que simulará o nosso jogo. Adicionamos três corpos rígidos nesse mundo do Box2D: dois corpos que representam os carros estacionados e um que representa o carro que o jogador estacionará. Nesse tutorial, adicionaremos as quatro rodas do carro que será estacionado. Esse é um processo um pouco trabalhoso, porque precisamos criar quatro objetos rígidos e ligá-los ao objeto que representa o carro. Porém, trabalhoso não significa difícil. Bora dar início a mais um tutorial.

 

Criando os Sprites dos pneus

Colocaremos um pneu em cada canto do carro, porém eles não ficarão à mostra se o jogador não virar o volante. Optei por isso para evitar a necessidade de criarmos pneus para os carros estacionados também. Caso o jogador vire o volante, será possível ver as pontas dos pneus dianteiros.

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

cocos2d::CCSprite* rodaDianteiraEsquerda;

cocos2d::CCSprite* rodaDianteiraDireita;

cocos2d::CCSprite* rodaTraseiraEsquerda;

cocos2d::CCSprite* rodaTraseiraDireita;

logo abaixo dessa:

b2Body* corpoCarroJogador;

Note que criamos referências para quatro novos Sprites dos pneus do carro. Agora abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::rodaDianteiraEsquerda = CCSprite::createWithSpriteFrameName(“Pneu.png”);

HelloWorld::rodaDianteiraEsquerda->setScale((0.05*size.height)/HelloWorld::rodaDianteiraEsquerda->boundingBox().size.width);

HelloWorld::rodaDianteiraEsquerda->setPosition(ccp(0.1*size.width-0.37*HelloWorld::carroJogador->boundingBox().size.width,

0.2*size.height+0.3*HelloWorld::carroJogador->boundingBox().size.height));

HelloWorld::rodaDianteiraEsquerda->setRotation(-90);

addChild(HelloWorld::rodaDianteiraEsquerda);

HelloWorld::rodaDianteiraDireita = CCSprite::createWithSpriteFrameName(“Pneu.png”);

HelloWorld::rodaDianteiraDireita->setScale((0.05*size.height)/HelloWorld::rodaDianteiraDireita->boundingBox().size.width);

HelloWorld::rodaDianteiraDireita->setPosition(ccp(0.1*size.width+0.37*HelloWorld::carroJogador->boundingBox().size.width,

0.2*size.height+0.3*HelloWorld::carroJogador->boundingBox().size.height));

HelloWorld::rodaDianteiraDireita->setRotation(-90);

addChild(HelloWorld::rodaDianteiraDireita);

HelloWorld::rodaTraseiraEsquerda = CCSprite::createWithSpriteFrameName(“Pneu.png”);

HelloWorld::rodaTraseiraEsquerda->setScale((0.05*size.height)/HelloWorld::rodaTraseiraEsquerda->boundingBox().size.width);

HelloWorld::rodaTraseiraEsquerda->setPosition(ccp(0.1*size.width-0.37*HelloWorld::carroJogador->boundingBox().size.width,

0.2*size.height-0.3*HelloWorld::carroJogador->boundingBox().size.height));

HelloWorld::rodaTraseiraEsquerda->setRotation(-90);

addChild(HelloWorld::rodaTraseiraEsquerda);

HelloWorld::rodaTraseiraDireita = CCSprite::createWithSpriteFrameName(“Pneu.png”);

HelloWorld::rodaTraseiraDireita->setScale((0.05*size.height)/HelloWorld::rodaTraseiraDireita->boundingBox().size.width);

HelloWorld::rodaTraseiraDireita->setPosition(ccp(0.1*size.width+0.37*HelloWorld::carroJogador->boundingBox().size.width,

0.2*size.height-0.3*HelloWorld::carroJogador->boundingBox().size.height));

HelloWorld::rodaTraseiraDireita->setRotation(-90);

addChild(HelloWorld::rodaTraseiraDireita);

logo abaixo dessa:

HelloWorld::carroJogador->setRotation(-90);

Acabamos de adicionar um Sprite para cada pneu do carro. Posicionamos um em cada canto de forma que eles não apareçam. Perceba que criamos e incluímos esses Sprites antes de incluir o Sprite do carro. Isso porque, visualmente, os pneus ficam embaixo do carro. Se você for um perfeccionista, perceberá que dá para ver pouca coisa dos pneus do carro do jogador.

Agora que já adicionamos os Sprites dos pneus, vamos à criação dos corpos rígidos que os representam no mundo físico do Box2D.

 

Adicionando os objetos rígidos dos pneus

Agora iniciaremos a implementação da física dos pneus. Como é de se imaginar, eles ficarão um em cada canto e ligados no objeto rígido que representa o carro do jogador. Posteriormente, os objetos rígidos que representam os pneus serão indispensáveis para que a simulação física de movimentação do carro ocorra corretamente. Abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

b2Body* corpoRodaDianteiraEsquerda;

b2Body* corpoRodaDianteiraDireita;

b2Body* corpoRodaTraseiraEsquerda;

b2Body* corpoRodaTraseiraDireita;

b2RevoluteJoint* juntaRodaDianteiraEsquerda;

b2RevoluteJoint* juntaRodaDianteiraDireita;

b2PrismaticJoint* juntaRodaTraseiraEsquerda;

b2PrismaticJoint* juntaRodaTraseiraDireita;

logo abaixo dessa:

cocos2d::CCSprite* rodaTraseiraDireita;

Incluímos oito novas referências na classe “HelloWorld”. Precisamos delas para que possamos, posteriormente, fazer com que os Sprites das rodas acompanhem o posicionamento e angulação dos objetos rígidos que as representam no mundo do Box2D. Além disso, precisamos das referências das juntas rotativas que ligarão as duas rodas dianteiras e das referências das juntas prismáticas que ligarão as rodas traseiras. Para o caso das juntas rotativas, as utilizaremos quando virarmos o volante do carro. Quando isso acontecer, rotacionaremos as rodas dianteiras do carro para que ele assuma uma nova direção.

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

b2BodyDef definicaoRodaDianteiraEsquerda;

definicaoRodaDianteiraEsquerda.type = b2_dynamicBody;

HelloWorld::corpoRodaDianteiraEsquerda = HelloWorld::mundoFisico->CreateBody(&definicaoRodaDianteiraEsquerda);

HelloWorld::corpoRodaDianteiraEsquerda->SetTransform(b2Vec2(HelloWorld::rodaDianteiraEsquerda->getPositionX()/PTM_RATIO,HelloWorld::rodaDianteiraEsquerda->getPositionY()/PTM_RATIO),M_PI/2);

b2PolygonShape geometriaRodaDianteiraEsquerda;

geometriaRodaDianteiraEsquerda.SetAsBox((HelloWorld::rodaDianteiraEsquerda->boundingBox().size.height/PTM_RATIO)/2,(HelloWorld::rodaDianteiraEsquerda->boundingBox().size.width/PTM_RATIO)/2);

b2FixtureDef cascaRodaDianteiraEsquerda;

cascaRodaDianteiraEsquerda.shape = &geometriaRodaDianteiraEsquerda;

cascaRodaDianteiraEsquerda.density = 1.0f;

HelloWorld::corpoRodaDianteiraEsquerda->CreateFixture(&cascaRodaDianteiraEsquerda);

b2BodyDef definicaoRodaDianteiraDireita;

definicaoRodaDianteiraDireita.type = b2_dynamicBody;

HelloWorld::corpoRodaDianteiraDireita = HelloWorld::mundoFisico->CreateBody(&definicaoRodaDianteiraDireita);

HelloWorld::corpoRodaDianteiraDireita->SetTransform(b2Vec2(HelloWorld::rodaDianteiraDireita->getPositionX()/PTM_RATIO,HelloWorld::rodaDianteiraDireita->getPositionY()/PTM_RATIO),M_PI/2);

b2PolygonShape geometriaRodaDianteiraDireita;

geometriaRodaDianteiraDireita.SetAsBox((HelloWorld::rodaDianteiraDireita->boundingBox().size.height/PTM_RATIO)/2,(HelloWorld::rodaDianteiraDireita->boundingBox().size.width/PTM_RATIO)/2);

b2FixtureDef cascaRodaDianteiraDireita;

cascaRodaDianteiraDireita.shape = &geometriaRodaDianteiraDireita;

cascaRodaDianteiraDireita.density = 1.0f;

HelloWorld::corpoRodaDianteiraDireita->CreateFixture(&cascaRodaDianteiraDireita);

b2BodyDef definicaoRodaTraseiraEsquerda;

definicaoRodaTraseiraEsquerda.type = b2_dynamicBody;

HelloWorld::corpoRodaTraseiraEsquerda = HelloWorld::mundoFisico->CreateBody(&definicaoRodaTraseiraEsquerda);

HelloWorld::corpoRodaTraseiraEsquerda->SetTransform(b2Vec2(HelloWorld::rodaTraseiraEsquerda->getPositionX()/PTM_RATIO,HelloWorld::rodaTraseiraEsquerda->getPositionY()/PTM_RATIO),M_PI/2);

b2PolygonShape geometriaRodaTraseiraEsquerda;

geometriaRodaTraseiraEsquerda.SetAsBox((HelloWorld::rodaTraseiraEsquerda->boundingBox().size.height/PTM_RATIO)/2,(HelloWorld::rodaTraseiraEsquerda->boundingBox().size.width/PTM_RATIO)/2);

b2FixtureDef cascaRodaTraseiraEsquerda;

cascaRodaTraseiraEsquerda.shape = &geometriaRodaTraseiraEsquerda;

cascaRodaTraseiraEsquerda.density = 1.0f;

HelloWorld::corpoRodaTraseiraEsquerda->CreateFixture(&cascaRodaTraseiraEsquerda);

b2BodyDef definicaoRodaTraseiraDireita;

definicaoRodaTraseiraDireita.type = b2_dynamicBody;

HelloWorld::corpoRodaTraseiraDireita = HelloWorld::mundoFisico->CreateBody(&definicaoRodaTraseiraDireita);

HelloWorld::corpoRodaTraseiraDireita->SetTransform(b2Vec2(HelloWorld::rodaTraseiraDireita->getPositionX()/PTM_RATIO,HelloWorld::rodaTraseiraDireita->getPositionY()/PTM_RATIO),M_PI/2);

b2PolygonShape geometriaRodaTraseiraDireita;

geometriaRodaTraseiraDireita.SetAsBox((HelloWorld::rodaTraseiraDireita->boundingBox().size.height/PTM_RATIO)/2,(HelloWorld::rodaTraseiraDireita->boundingBox().size.width/PTM_RATIO)/2);

b2FixtureDef cascaRodaTraseiraDireita;

cascaRodaTraseiraDireita.shape = &geometriaRodaTraseiraDireita;

cascaRodaTraseiraDireita.density = 1.0f;

HelloWorld::corpoRodaTraseiraDireita->CreateFixture(&cascaRodaTraseiraDireita);

b2RevoluteJointDef definicaoJuntaRodaDianteiraEsquerda;

definicaoJuntaRodaDianteiraEsquerda.Initialize(HelloWorld::corpoCarroJogador,

HelloWorld::corpoRodaDianteiraEsquerda,HelloWorld::corpoRodaDianteiraEsquerda->GetWorldCenter());

definicaoJuntaRodaDianteiraEsquerda.maxMotorTorque = 10.0f;

definicaoJuntaRodaDianteiraEsquerda.motorSpeed = 10.0f;

definicaoJuntaRodaDianteiraEsquerda.enableMotor = true;

HelloWorld::juntaRodaDianteiraEsquerda = static_cast<b2RevoluteJoint*>(HelloWorld::mundoFisico->CreateJoint(&definicaoJuntaRodaDianteiraEsquerda));

b2RevoluteJointDef definicaoJuntaRodaDianteiraDireita;

definicaoJuntaRodaDianteiraDireita.Initialize(HelloWorld::corpoCarroJogador,

HelloWorld::corpoRodaDianteiraDireita,HelloWorld::corpoRodaDianteiraDireita->GetWorldCenter());

definicaoJuntaRodaDianteiraDireita.maxMotorTorque = 10.0f;

definicaoJuntaRodaDianteiraDireita.motorSpeed = 10.0f;

definicaoJuntaRodaDianteiraDireita.enableMotor = true;

HelloWorld::juntaRodaDianteiraDireita = static_cast<b2RevoluteJoint*>(HelloWorld::mundoFisico->CreateJoint(&definicaoJuntaRodaDianteiraDireita));

b2PrismaticJointDef definicaoJuntaRodaTraseiraEsquerda;

definicaoJuntaRodaTraseiraEsquerda.Initialize(HelloWorld::corpoCarroJogador,

HelloWorld::corpoRodaTraseiraEsquerda,HelloWorld::corpoRodaTraseiraEsquerda->GetWorldCenter(),b2Vec2(1,0));

definicaoJuntaRodaTraseiraEsquerda.lowerTranslation = 0.0f;

definicaoJuntaRodaTraseiraEsquerda.upperTranslation = 0.0f;

definicaoJuntaRodaTraseiraEsquerda.enableLimit = true;

HelloWorld::juntaRodaTraseiraEsquerda = static_cast<b2PrismaticJoint*>(HelloWorld::mundoFisico->CreateJoint(&definicaoJuntaRodaTraseiraEsquerda));

b2PrismaticJointDef definicaoJuntaRodaTraseiraDireita;

definicaoJuntaRodaTraseiraDireita.Initialize(HelloWorld::corpoCarroJogador,

HelloWorld::corpoRodaTraseiraDireita,HelloWorld::corpoRodaTraseiraDireita->GetWorldCenter(),b2Vec2(1,0));

definicaoJuntaRodaTraseiraDireita.lowerTranslation = 0.0f;

definicaoJuntaRodaTraseiraDireita.upperTranslation = 0.0f;

definicaoJuntaRodaTraseiraDireita.enableLimit = true;

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

logo abaixo dessa:

HelloWorld::corpoCarroJogador->CreateFixture(&cascaCarroJogador);

Foram adicionados bastantes códigos, mas, em suma, criamos os corpos rígidos dos quatro pneus dos carros. Criamos os corpos com a mesma geometria dos pneus e os posicionamos exatamente no local onde se encontram os Sprites deles. Logo após a criação dos quatro corpos rígidos, note que criamos as quatro juntas que interligam esses corpos no corpo rígido que representa o carro que o jogador estaciona. Duas juntas são rotativas, pelo fato do jogador virar o volante, e duas são prismáticas que não sofrem translação, para ligar as rodas traseiras. Se você não faz ideia sobre o que são juntas rotativas e prismáticas, dê uma olhada nesse tutorial.

Figura 1 - Execução do jogo

Figura 1 – Execução do jogo

Agora falta somente uma coisa para deixarmos o carro com as propriedades físicas completas para a simulação: especificar a densidade do material do carro. Como criamos o corpo rígido do carro no tutorial passado, então ainda falta especificar a densidade dele. Para isso, no mesmo arquivo editado por último, adicione a seguinte linha de código:

cascaCarroJogador.density = 1.0f;

logo abaixo dessa:

cascaCarroJogador.shape = &geometriaCarroJogador;

Se você compilar e executar o jogo, nada de diferente acontecerá. Porém, toda a parte lógica do mundo físico já está praticamente pronta. Como eu disse, se vocês forem um pouco perfeccionistas, notarão as rodas do carro do jogador enquanto que os outros carros estacionados não as possuem. Porém, essa diferença é pífia. A execução do jogo na sua versão atual pode ser vista na Figura 1 e os objetos rígidos e juntas adicionados no ambiente físico podem ser visualizados na Figura 2.

Figura 2 - Corpos rígidos e juntas adicionadas no mundo físico

Figura 2 – Corpos rígidos e juntas adicionadas no mundo físico

Vimos nesse tutorial como adicionar os objetos rígidos que representam as rodas do carro que o jogador estacionará. Vimos a necessidade de, além de criar esses objetos rígidos, criar as juntas que interligam esses objetos ao objeto rígido que representa o carro do jogador. Além da criação dos objetos rígidos e da interligação deles no objeto rígido do carro do jogador, criamos os Sprites das rodas para que seja adicionado mais realismo ao nosso game. Isso fará com que, quando viramos o volante do carro, as rodas dianteiras também virem de forma a mostrar para onde o carro se movimentará. No próximo tutorial nós implementaremos a interação do jogador com as setas indicativas na tela e incluiremos os efeitos físicos para que o carro possa começar a andar.

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