Física em jogos com Chipmunk2D

Uma dúvida de muitos que iniciam no desenvolvimento de jogos é: “Como farei para simular a física?” Lidar com física nunca é uma tarefa simples. Temos que considerar gravidade, como ela afeta os corpos presentes no espaço, colisão entre os corpos, força de impulso, formas geométricas etc.

chipmunk

Para que o desenvolvedor não precise se preocupar com todos esses aspectos, diversas engines de física para jogos foram criados. Veremos o quão fácil é trabalhar com a engine Chipmunk2D, usando o framework Cocos2d-JS para criar nosso jogo! Você pode conferir o resultado final do que faremos nesse link.

jogo

Diversos tutoriais básicos sobre Cocos2d podem ser encontrados na página da documentação e o download está disponível aqui.

Para começarmos a usar a engine de física do Chipmunk2D, temos que declarar nas configurações do Cocos2d-JS que usaremos o chipmunk. Para isso, alteramos o arquivo cocos2d.js na pasta cocos2d e adicionamos a seguinte instrução:

chipmunk:true,

Agora toda vez que carregarmos o Cocos2d, o jsloader carregará também a biblioteca do Chipmunk2D.

Para de fato usarmos a física em uma cena, precisamos de um objeto chamado Space. Nesse objeto definiremos todas as regras de física do nosso “espaço”, como a gravidade, por exemplo. É comum criarmos um método (initPhysics) na cena (Scene) que iniciará todas as configurações de física:

var PhysicScene = cc.Scene.extend({
    space: null,

    initPhysics:function() {
        //criação do objeto Space
        this.space = new cp.Space();
    },

    onEnter:function () {
        this._super();
        this.initPhysics();
        var layer = new GameLayer(this.space);
        this.addChild(layer);

        this.scheduleUpdate();
    },
    update: function(dt) {
        this.space.step(dt);
    }
});

Pronto! Agora já temos um objeto representando o espaço. Um vez criado podemos definir a força da gravidade nele. Para tal, o objeto space possui uma propriedade gravity que recebe um vetor no plano cartesiano:

//dentro da função initPhysics, logo após ter criado o space
this.space.gravity = cp.v(0, -650);

O próximo passo é criar um GameLayer. Nesse Layer (ou camada da cena) acontecerão todas as animações. É a camada principal que junta todos os elementos do jogo.

O GameLayer recebe o objeto space criado anteriormente como parâmetro no construtor. O código seguinte initializa o GameLayer:

var GameLayer = cc.Layer.extend({

    ctor : function(space){
        this._super();
        this.space = space;
        this.init();
    },

    init:function(){
        this._super();
        //aqui vamos fazer mais coisas ainda
    },
});

Vamos simular um corpo sendo afetado pela gravidade em nosso espaço. Representaremos esse corpo com o sprite de uma bola que fica salvo dentro de um diretório especial chamado res/Normal/:

ball

Para a simulação, no GameLayer dentro da função init criaremos o sprite da bola e o associaremos a um corpo físico:

init:function(){
    this._super();

    // cc.PhysicsSprite cria um sprite que representará um corpo
    var bola = cc.PhysicsSprite.create("ball.png");
    this.addChild(bola);

    var raio = 32;

    // Criando o corpo físico que obedecerá as leis do espaço
    var corpo = this.space.addBody(new cp.Body(10,
    	cp.momentForCircle(10, 0, raio, cp.v(0,0))));

    // Podemos escolher a forma do corpo, a elasticidade e a fricção dele!
    forma = this.space.addShape(new cp.CircleShape(corpo, raio, cp.v(0,0)));
    forma.setElasticity(0.6);
    forma.setFriction(1);

    var s = cc.Director.getInstance().getWinSize();
    corpo.setPos(cp.v(s.width/2, s.height/2));

    bola.setBody(corpo)
},

Rode a aplicação e verifique o resultado. A bola está passando desaparecendo tela! Precisamos criar um chão para que ela permaneça na área visível.

Vamos criar 4 paredes, uma em cada lado da tela, transformando a área jogável em uma espécie de “caixa”. No método initPhysics, que está no PhysicScene:

var winSize = cc.Director.getInstance().getWinSize();

var staticBody = this.space.staticBody;

var paredes = [ new cp.SegmentShape(staticBody, cp.v(0,0),
					cp.v(winSize.width,0), 0 ),               // bottom

    new cp.SegmentShape(staticBody, cp.v(0,winSize.height),
    				cp.v(winSize.width,winSize.height), 0),    // top

    new cp.SegmentShape(staticBody, cp.v(0,0),
    				cp.v(0,winSize.height), 0),             // left

    new cp.SegmentShape(staticBody, cp.v(winSize.width,0),
    				cp.v(winSize.width,winSize.height), 0)  // right
];

for( var i=0; i < paredes.length; i++ ) {
    var forma = paredes[i];
    forma.setElasticity(1);
    forma.setFriction(1);
    this.space.addStaticShape( forma );
}

Agora nossa bola bate no chão e quica! Podemos facilmente aplicar um impulso ao corpo. Na função init do GameLayer:

var forcaX = -4000;
var forcaY = 3000;

corpo.applyImpulse(cp.v(forcaX, forcaY), cp.v(0, 0));

Agora a bola começa já com um impulso. O primeiro parâmetro da função applyImpulse é um vetor com a direção do impulso. O segundo parâmetro é um offset indicando (a partir do centro do corpo) onde o impulso será aplicado. Poderíamos usar valores diferentes de 0 se quiséssemos causar uma rotação na bola.

Vimos que é muito simples simularmos física em nossos jogos do Cocos2d-html5 usando a engine Chipmunk2D. Só precisamos de um objeto space, onde podemos configurar, por exemplo, a gravidade. E nele teremos os corpos que serão afetados pela física.

Falta mencionar que Cocos2d já vem com o Cocos2d-html5 e o Cocos2d-x Javascript Binding, para podermos compilar nosso código para rodar nativamente em dispositivos mobile. Além disso, a biblioteca do Cocos2d também possui suporte para outras engines de física, como por exemplo o Box2D.

O código completo desse projeto pode ser encontrado no Github e se quiser saber mais sobre Cocos2d-html5, não deixe de visitar a MobileConf RJ 2014!

1 Comentário

  1. Luiz Fagundes 07/05/2014 at 14:12 #

    Legal esses posts mais focados em jogos e coisas divertidas!

Deixe uma resposta