Box2D, Modo DebugDraw
Olá Pessoal,
Dando continuação ao último tutorial, será mostrado como utilizar o modo de debug do Box2D chamado de DebugDraw. Com esse modo você poderá analisar visualmente como estão ocorrendo as colisões, contatos, onde estão os centros de massa, que objetos estão “dormindo”, entre outras análises.
Para exemplificar esse modo será utilizado o mesmo código do artigo anterior que pode ser visualizado em http://marciosilva.net/2009/04/box2d-primeira-simulacao/. Um preview do que será feito está logo abaixo:
Algumas adaptações serão necessárias para colocar o DebugDraw no exemplo anteriormente visto. Primeiramente será necessário importar a classe MovieClip e demais modificações que podem ser vistas no código abaixo:
package { import flash.display.Sprite; /*novo import*/ import flash.display.MovieClip; 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(); debugDraw(); } /*Método que realiza o DebugDraw*/ private function debugDraw():void { var m_sprite:Sprite; m_sprite = new Sprite(); addChild(m_sprite); debug = new b2DebugDraw(); debug.m_sprite=m_sprite; /*escala padrão do Box2D 30px = 1m*/ debug.m_drawScale=30; debug.m_alpha = 1; debug.m_fillAlpha=0.5; debug.m_lineThickness=1; debug.m_drawFlags = b2DebugDraw.e_shapeBit |b2DebugDraw.e_jointBit |b2DebugDraw.e_coreShapeBit |b2DebugDraw.e_aabbBit |b2DebugDraw.e_obbBit |b2DebugDraw.e_pairBit |b2DebugDraw.e_centerOfMassBit; /*aplica ao mundo o modo debug*/ world.SetDebugDraw(debug); } /* Define os listeners da simulação/game*/ private function createListeners():void { timer = new Timer(2000); 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.4; /* 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 MovieClip(); 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 MovieClip(); 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; /*novo atributo para aplicar o DebugDraw*/ private var debug:b2DebugDraw; } } |
As modificações começaram com a adição da classe MovieClip que será usada como dado do usuário:
package { import flash.display.Sprite; /*novo import*/ import flash.display.MovieClip; |
Aos atributos foi adicionado um novo que serve para realizar o DebugDraw da classe b2DebugDraw:
private var physScale = 30.0; private var timer:Timer; /*novo atributo para aplicar o DebugDraw*/ private var debug:b2DebugDraw; } } |
Tanto para criar o círculo quanto para criar os poligonos a classe de dado de usuários é a classe MovieClip, com ela fica mais fácil visualizar o comportamento dos corpos quanto ao seu estado de atividade que veremos mais à frente no texto:
bodyDef.userData = new MovieClip(); |
A mudança mais importante foi a criação de um novo método chamado debugDraw():
private function debugDraw():void { var m_sprite:Sprite; m_sprite = new Sprite(); addChild(m_sprite); debug = new b2DebugDraw(); debug.m_sprite=m_sprite; /*escala padrão do Box2D 30px = 1m*/ debug.m_drawScale=30; debug.m_alpha = 1; debug.m_fillAlpha=0.5; debug.m_lineThickness=1; debug.m_drawFlags = b2DebugDraw.e_shapeBit |b2DebugDraw.e_jointBit /*mostra todos os Joints da Cena*/ |b2DebugDraw.e_coreShapeBit /*utilizaado para observar colisões contínuas*/ |b2DebugDraw.e_aabbBit /*mostra o eixo de coordenados dos corpos*/ |b2DebugDraw.e_obbBit /*mostra os bouding boxes*/ |b2DebugDraw.e_pairBit /*mostra um vetor que parte do centro dos corpos para os outros que estão em contado*/ |b2DebugDraw.e_centerOfMassBit ;/*mostra o centro de massa dos Corpos*/ /*aplica ao mundo o modo debug*/ world.SetDebugDraw(debug); } |
debug.m_drawFlags é um inteiro sem sinal com 7 bits que podem estar ligados ou não dependendo do OR (OU) resultante da combinação de uma mais flags unidos pelo OR bit a bit representado pelo ‘|’, comumente chamado de Pipe.
No debugdraw você também observa que alguns corpos possuem cores diferentes de outros. A cor vermelha indica que o corpo está dormindo, ou seja, o existem cálculos matemáticos/físicos sendo realizados para ele, esse fato é muito é muito importante para que haja uma maior performance reduzindo o consumo de processamento desnecessarimante. Os corpos cinzas estão em movimento, e os verdes são corpos estáticos, como por exemplo paredes e obstáculos.
Os arquivos deste tutorial podem ser baixados aqui:
Box2D DebugDraw
Faça diversas combinações dessas flags para você verificar visualmente como os corpos se comportam. Até a próxima.

Comentários