Box2D, Primeira Simulação

Olá Pessoal,

Vamos agora aprender como programar com a Engine de Física para Games em Flash Box2D. Neste primeiro exemplo prático iremos mostrar como criar um Mundo e corpos que sofrerão ações e restrições desse mundo.

Quem está lendo este tutorial e ainda não leu o primeiro recomendo que o leia neste link [http://www.mxstudio.com.br/flash/box2d-engine-de-fisica-para-games-em-flash/].

Pré-requisitos e Softwares utilizados:

Macromedia Flash CS4

Conhecimento em Física Básica (Cinemática pelo menos).

Conhecimento Intermediário em AS3.

API do Box2D que você pode baixar aqui [http://sourceforge.net/project/showfiles.php?group_id=210232&package_id=252417&release_id=642873]

Faremos algo como o exemplo abaixo:

Agora com os pré-requisitos estabelecidos, vamos criar nossa primeira aplicação. Baixe o arquivo ZIP contendo o código do Box2D e descompacte-o. Se por exemplo, você descompactou o box2D dentro de uma pasta chamada C:\game\, então o Box2D ficou com o seguinte caminho C:\game\Box2D\. Para que tudo funcione corretamente, todos os arquivos citados neste tutorial e nos próximos devem estar em C:\game\. Veja o exemplo:

Exemplo de organização básica

Exemplo de organização básica

Crie um novo arquivo chamado Box2DMX.as. Nele coloque o seguinte código:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	/*importação da API Box2D*/
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;

	public class Box2DMX extends Sprite
	{
		/* Construtor da classe	*/
		public function Box2DMX()
		{
			init();
		}

		/* Incializa a aplicação*/
		private function init():void
		{
			createWorld();

			createListeners();

			createGround();
		}

		/* Define os listeners da simulação/game*/
		private function createListeners():void
		{
			timer = new Timer(1000);
			timer.addEventListener(TimerEvent.TIMER, createBodies);
			timer.start();
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
		}

		/* Instancia o mundo onde a simulação ocorrerá*/
		private function createWorld():void
		{
			/* Definição dos limite do mundo*/
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-100.0, -100.0);
			worldAABB.upperBound.Set(100.0, 100.0);

			/* Difinição da gravidade atuante em todos os corpos desse mundo*/
			var gravity:b2Vec2=new b2Vec2(0.0,10.0);

			/* Indica que os corpos devem permanecerem inativos	*/
			var doSleep:Boolean=true;

			/* Construção do mundo*/
			world = new b2World(worldAABB, gravity, doSleep);

		}

		/* Cria o chão onde os corpos ficarão*/
		private function createGround():void
		{
			var body:b2Body;
			var bodyDef:b2BodyDef;
			var boxDef:b2PolygonDef;

			bodyDef = new b2BodyDef();
			bodyDef.position.Set(10, 12);
			bodyDef.angle=0.0;

			boxDef = new b2PolygonDef();
			boxDef.SetAsBox(90, 3);
			boxDef.friction = 0.1;

			/* para corpos que nunca se moverão o
			* valor da densidade deve ser zero
			*/
			boxDef.density = 0;
			bodyDef.userData = new PhysGround();
			bodyDef.userData.width = physScale * 2 * 30;
			bodyDef.userData.height = physScale * 2 * 3;

			addChild(bodyDef.userData);

			body=world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
		}
		/*cria os atores da simulação*/
		private function createBodies(e:Event):void
		{
			/* representa o corpo rígido*/
			var body:b2Body;

			/* será utilizado para representar
			* as características físicas do corpo rígido
			*/
			var bodyDef:b2BodyDef;

			/*define um polígono e suas características físicas*/
			var boxDef:b2PolygonDef;

			/*define um círculo e suas características físicas*/
			var circleDef:b2CircleDef;

			bodyDef = new b2BodyDef();
			bodyDef.position.Set(Math.random() * 15+5, 0);

			var rX:Number = Math.random() + 0.1;
			var rY:Number = Math.random() + 0.1;
			//Caixa
			if (Math.random() < 0.5)
			{
				boxDef = new b2PolygonDef();
				boxDef.SetAsBox(rX, rY);
				boxDef.density = 1.0;
				boxDef.friction = 0.5;
				boxDef.restitution = 0.2;

				bodyDef.userData = new PhysBox();
				bodyDef.userData.width = rX * 2 * physScale;
				bodyDef.userData.height = rY * 2 * physScale; 

				body = world.CreateBody(bodyDef);
				body.CreateShape(boxDef);
			}
			else //Círculo
			{
				circleDef = new b2CircleDef();
				circleDef.radius = rX;
				circleDef.density = 1.0;
				circleDef.friction = 0.5;
				circleDef.restitution = 0.2;

				bodyDef.userData = new PhysCircle();
				bodyDef.userData.width = rX * 2 * physScale;
				bodyDef.userData.height = rX * 2 * physScale; 

				body = world.CreateBody(bodyDef);
				body.CreateShape(circleDef);
			}

			bodyDef.userData.x = body.GetPosition().x *physScale ;
			body.SetMassFromShapes();
			addChild(bodyDef.userData);
		}

		/*atualiza o posicionamento dos atores*/
		private function Update(e:Event):void
		{
			world.Step(timeStep, iterations);

			// Pare cada corpo no mundo atualiza a sua posição e rotação
			for (var body:b2Body = world.GetBodyList (); body; body = body.GetNext())
			{
				if (body.GetUserData() is Sprite)
				{
					body.GetUserData().x = body.GetPosition().x * physScale;//metros para pixels
					body.GetUserData().y = body.GetPosition().y * physScale;//metros para pixels
					body.GetUserData ().rotation = body.GetAngle() * (180/Math.PI);
				}
			}
		}

		private var world:b2World;
		private var iterations:int = 10;
		private var timeStep:Number = 1.0/30.0;
		private var physScale = 30.0;
		private var timer:Timer;

	}
}

Neste primeiro código podemos destacar a criação do mundo que é realizado determinando seus limites pela classe b2AABB, além de delimitar o mundo você utilizar esta classe para determinar locais em seu game onde os personagens sofrem alguma alteração. Por exemplo, delimitar locais onde existem bombas que explodem se eles passarem por cima, checkpoints, etc.

Outro ponto que deve ser destacado é a criação de corpos (bodies), a criação de um body segue uma sequência de passos bem definida e lógica:

  1. Define um body com posição;
  2. Usa o obejto world para criar um body;
  3. Define os shapes com geometria, atrito, densidade, etc;
  4. Coloca os shapes no body;
  5. E opcionalmente ajusta a massa do body;

Podemos destacar também neste primeiro exemplo o atributo userData, usado por exemplo neste trecho de código:

bodyDef.userData = new PhysCircle();

O Box2D permite que você crie suas próprias classes de atores e vincule a um body do mundo criado. Por exemplo, você está desenvolvendo um jogo de guerra e uma das suas classes se chama Tanque e dentro da classe tanque existem variáveis que você controla como life, munição, quantidade de inimigos que matou, etc. Portanto, está classe são dados controlados por você. Se você percebeu quem define as propriedades físicas é a classe b2BodyDef, é com ela que você informa ao Box2D como ele deverá tratar o ator aplicando as restrições físicas do mundo criado.

E por fim, temos a linha abaixo:

world.Step(timeStep, iterations);

Esta linha deve aparecer no Main loop de seu Game, nesse momento o Box2D faz o trabalho duro de calcular colisões, prever contatos, tudo através de cálculos matemáticos. Você não precisa saber como ele faz isso, precisa apenas informar o quão preciso você quer esses cálculos através da variável iterations. Quanto maior o valor dessa variável mais perfeito são os cálculos físicos e matemáticos. No entanto, isso gera um overhead, ou seja, deixa seu jogo mais lento perdendo performance.

Games com Engines, ao contrário de games simples que não possuem engines de cálculos físicos e matemáticos, necessitam de um tempo adicional para que possam aplicar equações complexas para que a simulação possua um pouco de realidade. Esse tempo no Box2D é o timeStep, o game loop ou main loop é necessário apenas para aplicar as novas posições calculadas pelo Box2D.

Um fato importante que deve ser observado é o physScale que indica as escala utilizada pelo Box2D, em Box2D 1(um) metro é igual a 30px. Esse fato deve-se ao fato dos cálculos físicos serem feitos com unidades internacionais e convencionadas pelos físicos como o metro e não com coordenadas de tela. Dessa forma, no main loop (método update) é feita uma conversão de metros para pixel, pois após a chamada do método Step() as novas coordenadas calculdas pela Engine foram determinadas, no entanto elas foram calculadas em metros.

Como a tela do seu munitor tem como unidade de medida e posicionamento o pixel, devemos converter o valor em metros calculado pelo Box2D para pixel como vemos abaixo. E o mesmo vale para outras medidas como altura e largura de objetos na cena.

body.GetUserData().x = body.GetPosition().x * physScale;//metros para pixels
body.GetUserData().y = body.GetPosition().y * physScale;//metros para pixels

Agora crie um arquivo chamado Box2DMX.fla e na aba properties coloque o nome da classe que deseja linkar.

Aba properties

Aba properties

Agora vamos criar os atores da simulação. Pressione CTRL + F8 para adicionar um novo símbolo, crie um quadrado de 100px x100px com o nome de PhysBox. Veja figura abaixo:

Box

Box

Agora crie da mesma forma um círculo chamado PhysCircle com as dimensões e posicionamento mostrados abaixo:

Circle

Circle

E por fim crie um movieclip que será o chão de sua simulação, este se chamará PhysGround.

Ground

Ground

Para que os movie clips criados acima funcionem com o código, para cada um você irá até a library clicar com o botão direito em cima do PhysBox por exemplo, e depois escolher a opção properties. Uma janela de diálogo se abrirá como a que está ilustrada abaixo para você exportar para o Action Script é só confirmar clicando em OK. Lembre-se de fazer o mesmo para o PhysCircle e o PhysGround.

Exportanto para o Action Script 3.0

Exportanto para o Action Script 3.0

Agora é só salvar os dois arquivos e dar CTRL + ENTER.

Espero que tenham gostado, até a próxima.

Meu Website: www.marciosilva.net

Fontes:

http://www.emanueleferonato.com

http://www.box2d.org

Arquivos do tutorial:
http://www.mxstudio.com.br/wp-content/uploads/2009/04/exemplo1.zip

Escrito por Marcio Silva on abril 24, 2009. Arquivado em Flash. Você pode seguir as respostas a esse artigo pelo RSS 2.0. Você pode deixar respostas para esse artigo

6 respostas a Box2D, Primeira Simulação

  1. Em um if no código dá um erro. Poderia esclarecer, por favor?
    O código:
    if (Math.random() < 0.5) // o ponto e vírgula

  2. Aqui deu esses erros pode esclarecer ?

    if (Math.random() < 0.5)
    if (Math.random() < 0.5)
    else //Círculo

  3. Marcos e Vinicius, acredito que o problema do IF ocorreu por que vocês copiaram do código HTML do tutorial. Lá existe uma entidade html que gera um erro.

    Pegue o arquivo que vai ao fim do tutorial, é um zip com o código completo. Vai resolver o seu problema de vocês.

    Abraços.

  4. Ciáxeres Régio

    eu lí todo o tutorial e fiquei muito animado, porem ví que como requisito o CS4 e eu só tenho o CS3 tem possibilidade de fazer algo ao menos próximo apenas com o CS3 ?

    Obrigado
    t+

  5. Po Cara eu fiz tudo conforme manda o tutorial, porém não está reproduzindo nada aqui:

  6. Como eu faço para a os objetos quicarem?

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>