Fala filhotes! Depois de muito tempo ( estava ocupado com outros projetos ) estou postando de volta. Dessa vez vou falar sobre a Farseer Physics, uma ótima biblioteca de física para se usar nos nossos projetos .NET, tanto silverlight como XNA. Sendo uma das maneiras mais fáceis de se criar jogos com física.

Sobre

Usando essa biblioteca você consegue simular coisas como: gravidade, contato entre objetos, atrito, elasticidade e muito mais coisas. A Farseer Physics foi criada baseada na biblioteca Box2D XNA, então se você já esta familiarizado com a Box2D vai ficar mais fácil de entender o Farseer, apesar das versões 3.x terem muitas alterações.

Versão 3.x e documentação

Hoje (27/06) o Farseer esta na versão 3.3.1 mas a partir da versão 3.0 a documentação do Farseer está muito fraca (a maior parte dela se refere a versão 2.x) e por isso você deve contar mais com os exemplos e discussões do que na documentação em si. A versão 3.x tem muita mudanças e melhorias com relação a 2.x mas eles seguem o mesmo padrão por isso a documentação antiga ainda pode servir de referência assim como a documentação do Box2D XNA.

Baixando a Biblioteca

No meu exemplo eu vou usar XNA para criar o jogo, então você ira precisar do XNA Game Studio (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9ac86eca-206f-4274-97f2-ef6c8b1f478f). Agora temos que baixar a biblioteca do Farseer (http://farseerphysics.codeplex.com) lembrando que você também pode baixar os exemplos no próprio site (altamente recomendável).  Pronto, depois de baixar a biblioteca vamos entender como ela funciona:

Conhecendo o Farseer

Antes de usar o Farseer você precisa conhecer esses 4 objetos:

World – Esse objeto controla tudo no Farseer, gravidade, colisão, etc… A função Step que faz toda a movimentação e o controle para checar se tudo esta em ordem entre os objetos.

Body – É o corpo do objeto, contendo posição e podendo ser afetados por forças (gravidade, colisões, etc..)

Shape – É forma do objeto. Inercia, área, massa e o centróide são calculados a partir da Shape.

Fixture – A Fixture fixa uma Shape ao Body, graças a isso sempre que você move um Body a Shape também é movida. Se houver alguma colisão na Shape a força dessa colisão será aplicada ao Body.

Outra coisa importante, o Farseer não lida com pixels mas com Metros, por isso você varias vezes ira precisar converter de Pixeis para Metros.

Ótimo! Agora estamos prontos para usar o Farseer no nosso projeto!

Nosso primeiro projeto

Então vamos lá! Eu vou criar um WindowsGame Project (na aba XNA). Depois de criado ele gera uma solução com o seu projeto windows game e outro projeto com o Content (para as imagens e áudios do seu jogo). Beleza! Agora vamos utilizar a Farseer! Como dito lá em cima, para criar um objeto que seja influenciado pela gravidade nós vamos precisar de um objeto World(que irá controlar tudo), um Body (O objeto em si), um Shape (o desenho e limitador do objeto) e uma Fixture (que irá ligar a shape ao body) agora vamos ao que importa, ao código:

Lembra que o Farseer trabalha com metro em vez de pixel? então, temos que criar uma constante e alguns métodos de conversão para o nosso jogo, então na classe game1 (a classe principal) vamos fazer isso:

        // Nossa constante dizendo que um metro vale 64 pixels
        private const float PixelInMeter = 64f;

        /// <summary>
        /// Função que converte um numero inteiro de pixel para metros
        /// </summary>
        public float ToMeter(int pixel)
        {
            return pixel / PixelInMeter;
        }

        /// <summary>
        /// Função que converte um Vector2 de pixel para metros
        /// </summary>
        public Vector2 ToMeter(Vector2 pixel)
        {
            return pixel / PixelInMeter;
        }

Agora vamos começar a criar os objetos do Farseer, vamos criar todos os objetos na classe Game1 e inicializaremos eles dentro do método Initialize. Primeiro criaremos o objeto World:

 protected override void Initialize()
        {
            // Esse é o objeto que controla todos os outros objetos do farseer, controla a movimentação, gravidade, toda a fisica.
            // Passamos para ele o valor da gravidade, nesse caso 3 metros por segundo e apenas para baixo.
            objWorld = new World(new Vector2(0, 3f));

Veja que passei um Vector2 como parâmetro, ele será o valor da gravidade. Agora que temos o mundo precisamos de um objeto que será influenciado por ele, vou usar esse circulo que acabei de desenhar:

bola

Você deve adicionar essa imagem dentro do projeto WindowsGame1Content. E agora para criar o body e etc.. podemos usar o ObjectFactory do próprio Farseer dessa maneira:

            // Uso o BodyFactory para criar um body ja com uma Shape ( nesse caso um circulo ) e a fixture. 
            // Passamos o World, o raio do circulo, a densidade dele e a posição (em metros) como parâmetros.
            objBola = BodyFactory.CreateCircle(objWorld, ToMeter(30), 1f, ToMeter(new Vector2(400, 100)));
            
            // um objeto Dynamic é influenciado pela gravidade e por colisões de qualquer outro objeto.
            objBola.BodyType = BodyType.Dynamic;

            // Restitution é a Elasticidade, sendo 0 nada e 1 volta com a mesma força. Podendo ser qualquer outro numero não negativo
            objBola.Restitution = 0.4f;

            // Seta a fricção, onde 0 desliga ela e 1 a deixa forte. Podendo ser qualquer outro numero não negativo
            objBola.Friction = 1f;

Perceba que existem varias outras propriedades que podemos preencher para deixar a simulação ainda mais perfeita. Pronto! Agora vamos criar uma variável para pegar a textura do Body ( a imagem da bola):

// Carrego a textura "bola" (bola.png) que esta dentro da Content
txtBody = Content.Load<Texture2D>("bola");

Agora só falta mais uma coisa, vamos criar uma borda para evitar que a bola saia da tela. Precisaremos de mais um body, sendo que a shape fica sendo as bordas da tela:

            // Crio 4 pontos que serão a borda da tela
            var point1 = ToMeter(new Vector2(1, 1));
            var point2 = ToMeter(new Vector2(graphics.PreferredBackBufferWidth, 1));
            var point3 = ToMeter(new Vector2(1, graphics.PreferredBackBufferHeight));
            var point4 = ToMeter(new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight));
            
            // Lista de Vertices ( uma Vertices é uma lista de Vector2 )
            List<Vertices> lstVert = new List<Vertices>();

            // Uso a biblioteca PolygonTools para criar uma linha entre os pontos
            lstVert.Add(PolygonTools.CreateLine(point1,point2));
            lstVert.Add(PolygonTools.CreateLine(point1, point3));
            lstVert.Add(PolygonTools.CreateLine(point2, point4));
            lstVert.Add(PolygonTools.CreateLine(point3, point4));

            // Usando o BodyFactory criamos um Poligono composto, passando as vertices e o mundo
            borda = BodyFactory.CreateCompoundPolygon(objWorld, lstVert, 1f);
            
            // Kinematic faz o objeto não ser influenciado pela gravidade nem pelas forças 
            // Mas todos os objetos Dynamic conseguem "bater" neles
            borda.BodyType = BodyType.Kinematic;

O PolygonTools te da uma maneira de transformar simples pontos em formas geométricas, como por exemplo criar um retângulo a partir de apenas um ponto central. Bom!Agora que criamos todos os objetos só faltam poucas coisas, agora no método Update vamos adicionar isso:

           protected override void Update(GameTime gameTime)
        {
            // Update do 'World' do farseer, checa colisão, movimenta os objetos, aqui é a parte mais importante do farseer. Estamos usando um Timestep de 1/60 
            objWorld.Step(1.0f / 60.0f);
            
            // Pega o estado da tecla 
            var kstate = Keyboard.GetState();
            
            // Verifica qual tecla foi pressionado
            if (kstate.IsKeyDown(Keys.Right))
                // Aplica a rotação
                objBola.ApplyTorque(1f);

            if (kstate.IsKeyDown(Keys.Left))
                objBola.ApplyTorque(-1f);
            base.Update(gameTime);
            }

Precisamos do Step para fazermos uma iteração com os objetos ligados ao World. Agora só falta mais uma coisa, desenhar a nossa textura! Então no método Draw:

        spriteBatch.Begin();
        // Desenho a textura da bola na posição do Body (agora em pixel) usando a origem como sendo o meio do objeto
        spriteBatch.Draw(txtBody, objBola.Position * PixelInMeter, null, Color.White, objBola.Rotation, new Vector2(txtBody.Width, txtBody.Height) / 2f,1f,SpriteEffects.None,0);
        spriteBatch.End();

E Pronto! agora você já deve ter uma bola que sofre com a gravidade e que gira para o lado esquerdo e direito! Algo como isso:

bolascreen1 

bolascreen2

 

Bom, por hoje é só! Se você não conseguiu seguir o tutorial você pode baixar o código fonte aqui !

E já que nós estamos falando de jogos se alguém jogar Xbox 360 pode me adicionar na Live, minha Gamertag é BloodGocks. Espero voltar a postar no blog rápido!

Abraços!

 

Referências:

http://farseerphysics.codeplex.com/documentation