Tutorial: Utilizando o motor físico Box2D em conjunto com o Cocos2d-x – Parte 2

O que vocês acham de programarmos uma simulação física com vários objetos rígidos ao mesmo tempo na tela? Você acha que fazer isso é difícil? Não é, não. ;D

Vimos no último tutorial sobre o motor físico Box2D. Eu expliquei sobre a biblioteca e alguns conceitos básicos para que vocês possam utilizá-la. Criamos um aplicativo simples que realiza a simulação física de uma bola caindo.

Nesse tutorial eu não mostrarei nada muito novo. Na verdade, utilizaremos o que já sabemos sobre o motor físico para fazer uma simulação física mais legal. Será interessante para vocês terem uma ideia do realismo que a Box2D pode proporcionar sem demandar muito processamento do aparelho móvel. Esse tutorial é uma continuidade do último. Se você ainda não o fez, então se coça. ;D

 

Criando as paredes do ambiente

Vamos fazer um aplicativo de simulação física que descreve um ambiente fechado pela própria tela do celular. Assim sendo, teremos paredes em baixo, nos lados e em cima da tela do aparelho. Mas, antes de criarmos as paredes, vamos abrir o código do último tutorial e excluir algumas linhas desnecessárias. Abra o arquivo “HelloWorldScene.cpp” e remova o seguinte trecho de código:

b2BodyDef definicaoCorpo;

definicaoCorpo.type = b2_dynamicBody;

definicaoCorpo.position.Set((size.width/2)/PTM_RATIO,(size.height/2)/PTM_RATIO);

b2Body* corpo = HelloWorld::world->CreateBody(&definicaoCorpo);

b2CircleShape circulo;

circulo.m_radius = 1;

b2FixtureDef casca;

casca.shape = &circulo;

casca.density = 1.0f;

corpo->CreateFixture(&casca);

Basicamente, nós excluímos o trecho onde criamos a bola no tutorial passado. Agora vamos adicionar as paredes de forma que elas fiquem exatamente nos limites da tela do aparelho. No mesmo arquivo, adicione o seguinte trecho de código no mesmo lugar onde você excluiu o código acima.

b2BodyDef definicaoParedes;

definicaoParedes.type = b2_staticBody;

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

b2ChainShape formatoParedes;

b2Vec2* pontos = new b2Vec2[4];

pontos[0] = b2Vec2(0,0);

pontos[1] = b2Vec2(size.width/PTM_RATIO,0);

pontos[2] = b2Vec2(size.width/PTM_RATIO,size.height/PTM_RATIO);

pontos[3] = b2Vec2(0,size.height/PTM_RATIO);

formatoParedes.CreateLoop(pontos,4);

b2FixtureDef materialParedes;

materialParedes.shape = &formatoParedes;

paredes->CreateFixture(&materialParedes);

schedule(schedule_selector(HelloWorld::adicionaCorpo),0.5);

O que fizemos com o código acima? Primeiramente, criamos um objeto rígido estático, ou seja, ele não se movimenta na simulação física. Logo depois, eu defini os quatro pontos limitantes do objeto rígido como sendo os quatro cantos da tela do aparelho. Como é um corpo estático, ele atua como objeto de colisão, mas não sofre ação da gravidade nem de outros objetos, exatamente como uma parede. A última linha não faz referência às paredes, mas sim, aos objetos rígidos aleatórios que adicionaremos no ambiente. O método “adicionaCorpo” será chamado a cada meio segundo.

 

Gerador de objetos rígidos aleatórios

Agora, para podermos testar as nossas paredes, vamos adicionar vários objetos rígidos por meio do método “adicionaCorpo”. Cada vez que ele for chamado, ou seja, a cada meio segundo, um novo objeto rígido aleatório aparece dentro dos limites da tela. Com o tempo, a tela vai ficando cheia de objetos rígidos. Para adicionarmos esse método, abra o arquivo “HelloWorldScene.cpp” e copie esse código no final do arquivo:

void HelloWorld::adicionaCorpo() {

    static int valorAleatorio = time(NULL);

    CCSize size = CCDirector::sharedDirector()->getWinSize();

    srand(valorAleatorio);

    valorAleatorio = rand();

    if(valorAleatorio%2==0) {

        //Criar bola

        float posX,posY,raio,densidade;

        srand(valorAleatorio);

        valorAleatorio = rand();

        raio = (((float)valorAleatorio)/RAND_MAX)*0.7 + 0.3;

        srand(valorAleatorio);

        valorAleatorio = rand();

        posX = (((float)valorAleatorio)/RAND_MAX)*(size.width/PTM_RATIO – 2*raio) + raio;

        posY = size.height/PTM_RATIO – 1;

        srand(valorAleatorio);

        valorAleatorio = rand();

        densidade = ((float)valorAleatorio)/RAND_MAX + 1;

        b2BodyDef definicaoBola;

        definicaoBola.type = b2_dynamicBody;

        definicaoBola.position.Set(posX,posY);

        b2Body* bola = HelloWorld::world->CreateBody(&definicaoBola);

        b2CircleShape circulo;

        circulo.m_radius = raio;

        b2FixtureDef casca;

        casca.shape = &circulo;

        casca.density = densidade;

        bola->CreateFixture(&casca);

    }

    else {

        //Criar retangulo

        float posX,posY,largura,altura,densidade;

        srand(valorAleatorio);

        valorAleatorio = rand();

        largura = (((float)valorAleatorio)/RAND_MAX)*0.7 + 0.3;

        srand(valorAleatorio);

        valorAleatorio = rand();

        altura = (((float)valorAleatorio)/RAND_MAX)*0.7 + 0.3;

        posX = (((float)valorAleatorio)/RAND_MAX)*(size.width/PTM_RATIO – 2*largura) + largura;

        posY = size.height/PTM_RATIO – 1;

        srand(valorAleatorio);

        valorAleatorio = rand();

        densidade = ((float)valorAleatorio)/RAND_MAX + 1;

        b2BodyDef definicaoRetangulo;

        definicaoRetangulo.type = b2_dynamicBody;

        definicaoRetangulo.position.Set(posX,posY);

        b2Body* retangulo = HelloWorld::world->CreateBody(&definicaoRetangulo);

        b2PolygonShape formatoRetangulo;

        formatoRetangulo.SetAsBox(altura,largura);

        b2FixtureDef casca;

        casca.shape = &formatoRetangulo;

        casca.density = densidade;

        retangulo->CreateFixture(&casca);

    }

}

Para finalizar, abra o arquivo “HelloWorldScene.h” e adicione a seguinte linha de código:

void adicionaCorpo();

acima dessa linha de código:

void atualiza(float dt);

O que fizemos no método “adicionaCorpo”? Primeiramente, o método sorteia um número qualquer para definir se será adicionado um objeto em forma de bola ou em forma de retângulo. Se for adicionada uma bola, são sorteados novamente um lugar, um raio e um peso (densidade) aleatórios para que o objeto seja criado. Obviamente, após os sorteios, o respectivo objeto é adicionado no ambiente. Acontece a mesma coisa caso seja adicionado um retângulo, com exceção que o retângulo possui valores de altura e largura aleatórios no lugar de um valor de raio.

É possível notar que os valores de posição estão internos na tela do aparelho. Os valores de raio da bola e altura e largura do retângulo são valores entre 0,3 e 1. O valor de peso (densidade) varia entre 1 e 2. Após fazer isso, compile o código e execute o programa. Você verá que, a cada meio segundo, um novo objeto aleatório é inserido no ambiente e praticamente todo o cálculo de colisão, posicionamento e velocidade dos objetos são calculados pelo Box2D. Para os curiosos de plantão, veja no Vídeo 1, a simulação física executando no meu aparelho. Legal né? =]

Vídeo 1 – Simulação física com inserção de objetos rígidos

Vimos nesse tutorial como criar um objeto rígido estático, ou seja, aquele que não sofre efeito de gravidade nem de colisão. Vimos também como criar um método que insere objetos rígidos aleatórios em um ambiente. Por fim, vimos que a utilização de um motor físico é muito interessante pelo fato do programador não precisar ter conhecimentos técnicos em física para que ele possa programar uma simulação física completa.

No próximo tutorial vamos incluir juntas em objetos rígidos. Veremos como fazer objetos articulados no Box2D.

Muito obrigado pessoal 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