Tutorial: Simulação física com Cocos2d-x – Parte 2: Juntas

Ok, já demos nossos primeiros passos na implementação de simulações físicas com Cocos2d-x.

Começamos com uma simulação simples de objetos rígidos caindo em um ambiente.

E hoje implementaremos uma simulação física um pouco mais interessante.

Parte 1 – Parte 2 – Parte 3

Vimos no último tutorial como programar um simulador físico feito com o motor gráfico Cocos2d-x. Na verdade, nós não entramos muito a fundo na programação de simuladores físicos. Apenas criamos uma caixa lógica ao redor da tela do aparelho e criamos, com o passar do tempo, objetos rígidos com formatos aleatórios. Esses objetos caem e se movimentavam realisticamente na tela devido à ação da gravidade.

Hoje veremos a aplicação de outros conceitos de simulação física. Simularemos todo o sistema de dinâmica de movimentos de um pistão de motor de carro. Para isso, precisaremos entender o conceito de juntas.

 

Juntas … mas o que é isso?

joelhoComentamos no último tutorial o que seriam corpos rígidos. De forma simples, eles são objetos não deformáveis que possuem massa, formatos e cascas. A simulação física acontece quando um conjunto de objetos rígidos são animados com realismo. Fizemos uma simulação com vários objetos rígidos sendo lançados em um lugar fechado. Porém, o Chipmunk realiza simulações físicas mais complexas do que essa.

Juntas são interligações entre dois objetos rígidos. Existem diferentes tipos de juntas implementadas no Chipmunk. Seguem alguns exemplos:

Junta de fixação: interliga dois corpos como um parafuso. Não há rotação entre os corpos e ajudam a quebrar objetos complexos em vários corpos físicos distintos;

Junta de limite: interliga dois corpos rígidos como uma corda. Porém os corpos rígidos não sofrem rotação entre si;

Junta de pino: interliga dois corpos rígidos com um pino. Ambos os corpos podem sofrer rotação entre si;

Junta motorizada: interliga dois corpos rígidos de forma que a velocidade de rotação relativa entre os corpos seja mantida.

Como a biblioteca física Chipmunk está integrada na biblioteca gráfica Cocos2d-x, então todas essas juntas podem ser implementadas no Cocos2d-x. Se você quiser saber todos os tipos de juntas implementadas no Chipmunk, acesse essa página.

Conhecendo o conceito básico de corpo rígido e de juntas, agora podemos pensar em uma simulação física um pouco mais robusta. Faremos hoje a simulação do movimento das peças de um pistão de motor de carro. O projeto é simples, criaremos um corpo rígido circular que representa o virabrequim do motor, outro que representa a biela, outro que representa o pistão e outros dois que representam a parede do cilindro. Interligaremos todos eles com juntas de pino e faremos o virabrequim rotacionar. Isso fará com que todo o sistema se movimente exatamente como acontece no interior de um cilindro de motor de carro. A Figura 1 mostra o projeto rabiscado por mim, com seus respectivos valores de dimensão de geometria e de posicionamento na tela do aparelho.

Figura 1 - Projeto físico da simulação
Figura 1 – Projeto físico da simulação

Sabendo como funciona a simulação de hoje, partiremos para a implementação.

 

Implementando a simulação

Começaremos o tutorial de hoje criando um projeto Cocos2d-x com nome de pacote “com.Santy.SimulacaoFisica2” e com nome de projeto “SimulacaoFisica2”. Se você não sabe ou se lembra de como criar um projeto Cocos2d-x, veja esse tutorial. A Figura 2 ilustra a criação do projeto.

Figura 2 - Criando o projeto Cocos2d-x
Figura 2 – Criando o projeto Cocos2d-x

Deixe no projeto somente os arquivos de imagem necessários para a simulação física de hoje. Abra a pasta “Resources” e apague a imagem “HelloWorld.png”. Nessa mesma pasta, descompacte esse arquivo compactado com arquivos de sprite sheet. Esse sprite sheet eu mesmo criei com aquivos de imagens variados da Internet. Se você não sabe o que é um sprite sheet nem como fazer um, dê uma olhada nesse tutorial.

O Cocos2d-x inicializa todo o mundo físico somente no final da inicialização de um objeto da classe HelloWorld. Isso significa que os objetos rígidos até podem ser criados no método “init”, como vínhamos fazendo. Porém, não existe a possibilidade de criarmos juntas nesse método. Assim, será necessário criar todo o mundo físico no método “onEnterTransitionDidFinish”. Esse método é executado após o objeto “HelloWorld” já ter sido inicializado, propiciando, assim, a criação de juntas. Como utilizaremos tal método, precisaremos declará-lo na classe “HelloWorld”. Para isso, abra o arquivo “HelloWorldScene.h” e adicione a seguinte linha de código:

void onEnterTransitionDidFinish();

logo abaixo dessa:

virtual bool init();

Salve o arquivo e o feche que agora faremos modificações no arquivo “HelloWorldScene.cpp”. Com esse arquivo aberto, apague as linhas de código desnecessárias para a simulação de hoje. Se você não lembra, são aquelas linhas que adicionam na tela a imagem padrão do Cocos2d-x e uma etiqueta escrita “Hello World”. Elas iniciam nessas (inclusive):

/////////////////////////////

// 3. add your codes below…

// add a label shows “Hello World”

// create and initialize a label

auto label = Label::createWithTTF(“Hello World”, “fonts/Marker Felt.ttf”, 24);

e terminam nessa (inclusive):

this->addChild(sprite, 0);

Para começarmos a programar o mundo físico, primeiro precisamos criar o objeto da cena considerando a execução de cálculos físicos. Para isso, no mesmo arquivo, modifique essa linha de código:

auto scene = Scene::create();

de forma que ela fique assim:

auto scene = Scene::createWithPhysics();

socoandersonsilvaAgora, basta criar os sprites dos objetos citados na seção anterior, os seus respectivos objetos rígidos e os interligar por juntas. Tudo isso será implementado no método “onEnterTransitionDidFinish”. No final do arquivo, adicione as seguintes linhas de código.

void HelloWorld::onEnterTransitionDidFinish() {

    Size size = Director::getInstance()->getVisibleSize();

    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(“SpriteSheet.plist”);

    Sprite* vb = Sprite::createWithSpriteFrameName(“Virabrequim.png”);

    vb->setScale(((100.0/480.0)*size.height)/vb->getContentSize().height);

    vb->setPosition(size.width/2.0,(135.0/480.0)*size.height);

    addChild(vb);

    PhysicsBody* vbc = PhysicsBody::createCircle(vb->getContentSize().width/2.0,PhysicsMaterial(1.0,0.0,0.0));

    vbc->setCategoryBitmask(0);

    vb->addComponent(vbc);

    PhysicsBody* corpoParado = PhysicsBody::createBox(vb->getContentSize(),PhysicsMaterial(1.0,0.0,0.0));

    corpoParado->setPositionOffset(Vec2(0.0,vb->getPositionY()-size.height/2.0));

    corpoParado->setCategoryBitmask(0);

    corpoParado->setDynamic(false);

    addComponent(corpoParado);

    PhysicsJointPin* juntaVbCorpoParado = PhysicsJointPin::construct(corpoParado,vbc,vb->getPosition());

    getScene()->getPhysicsWorld()->addJoint(juntaVbCorpoParado);

    Sprite* biela = Sprite::createWithSpriteFrameName(“Biela.png”);

    biela->setScale(((120.0/480.0)*size.height)/biela->getContentSize().height);

    biela->setPosition(size.width/2.0,(145.0/480.0)*size.height);

    addChild(biela);

    PhysicsBody* bielac = PhysicsBody::createBox(biela->getContentSize(),PhysicsMaterial(1.0,0.0,0.0));

    bielac->setCategoryBitmask(0);

    biela->addComponent(bielac);

    PhysicsJointPin* juntaVbcBielac = PhysicsJointPin::construct(vbc,bielac,

      Vec2(size.width/2.0,(90.0/480.0)*size.height));

    getScene()->getPhysicsWorld()->addJoint(juntaVbcBielac);

    Sprite* pistao = Sprite::createWithSpriteFrameName(“Pistao.png”);

    pistao->setScale(((100.0/480.0)*size.height)/pistao->getContentSize().height);

    pistao->setPosition(size.width/2.0,(245.0/480.0)*size.height);

    addChild(pistao);

    PhysicsBody* pistaoc = PhysicsBody::createBox(pistao->getContentSize(),PhysicsMaterial(1.0,0.0,0.0));

    pistao->addComponent(pistaoc);

    PhysicsJointPin* juntaBielacPistaoc = PhysicsJointPin::construct(bielac,pistaoc,

      Vec2(size.width/2.0,(200.0/480.0)*size.height));

    getScene()->getPhysicsWorld()->addJoint(juntaBielacPistaoc);

    Sprite* cil1 = Sprite::createWithSpriteFrameName(“Cilindro.png”);

    cil1->setScale(((200.0/480.0)*size.height)/cil1->getContentSize().height);

    cil1->setPosition(

      (size.width-pistao->getBoundingBox().size.width-cil1->getBoundingBox().size.width)/2.0,

      (295.0/480.0)*size.height);

    addChild(cil1);

    PhysicsBody* cil1c = PhysicsBody::createBox(cil1->getContentSize(),PhysicsMaterial(1.0,0.0,0.0));

    cil1c->setDynamic(false);

    cil1->addComponent(cil1c);

    Sprite* cil2 = Sprite::createWithSpriteFrameName(“Cilindro.png”);

    cil2->setScale(((200.0/480.0)*size.height)/cil2->getContentSize().height);

    cil2->setPosition(

      (size.width+pistao->getBoundingBox().size.width+cil2->getBoundingBox().size.width)/2.0,

      (295.0/480.0)*size.height);

    addChild(cil2);

    PhysicsBody* cil2c = PhysicsBody::createBox(cil2->getContentSize(),PhysicsMaterial(1.0,0.0,0.0));

    cil2c->setDynamic(false);

    cil2->addComponent(cil2c);

    PhysicsJointMotor* juntaVbCorpoParado1 = PhysicsJointMotor::construct(vbc,corpoParado,10.0);

    getScene()->getPhysicsWorld()->addJoint(juntaVbCorpoParado1);

}

No código adicionado, nós criamos os sprites do virabrequim, da biela, do pistão e das duas paredes do cilindro. Criamos um objeto rígido estático invisível, nomeado “corpoParado”, para prender o virabrequim nele. Isso fará com que o virabrequim não caia com a ação da gravidade e que se torne possível fazê-lo girar posteriormente. Após criar o corpo estático invisível e o corpo rígido do virabrequim, nós os interligamos por uma junta de pino. Continuando, criamos o corpo rígido da biela e o interligamos com o corpo rígido do virabrequim por meio de outra junta de pino. Criamos o corpo rígido do pistão e o interligamos com o corpo rígido da biela por outra junta de pino. Para que o pistão não sofra um movimento semelhante ao das pás de um ventilador quando o virabrequim começar a rotacionar, nós incluímos dois corpos rígidos estáticos que representam as paredes do cilindro.

Figura 3 - Simulação física de um pistão de motor de carro
Figura 3 – Simulação física de um pistão de motor de carro

O sistema já está completo. Para adicionar dinamismo na simulação física nós incluímos uma junta motorizada que interliga o corpo rígido invisível ao corpo rígido do virabrequim. Isso fará o corpo rígido do virabrequim rotacionar, pelo fato do corpo rígido invisível ser estático, e adicionará movimentação no sistema como um todo. A estrutura é ilustrada na Figura 3 e a simulação, para os curiosos de plantão, ocorrerá como mostrado no vídeo a seguir.


Vimos no tutorial de hoje como simular a dinâmica de movimentos de um pistão de motor de carro. Inicialmente, foi explicado de forma rápida os conceitos de corpos rígidos e de juntas. Foi apresentado como funcionaria a simulação física com a explicação detalhada do sistema de corpos rígidos e suas interligações. Por último, nós criamos o projeto Cocos2d-x e implementamos o projeto.

Veremos no próximo tutorial como aplicar “momentos” em um corpo rígido e como implementar detecção e resposta de colisão.

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