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://marciosilva.net/2009/04/box2d-a-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:
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:
- Define um body com posição;
- Usa o obejto world para criar um body;
- Define os shapes com geometria, atrito, densidade, etc;
- Coloca os shapes no body;
- 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.
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:
Agora crie da mesma forma um círculo chamado PhysCircle com as dimensões e posicionamento mostrados abaixo:
E por fim crie um movieclip que será o chão de sua simulação, este se chamará PhysGround.
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.
Agora é só salvar os dois arquivos e dar CTRL + ENTER.
Espero que tenham gostado, até a próxima.
Acesse o link abaixo e baixe os arquivos do tutorial.







Comentários