miércoles, 18 de septiembre de 2013

Comparando dos motores físicos: Box2D y Nape

 
NAPE

Box2D



Estos dos motores físicos me parecen ser los más usados para el desarrollo de juegos hoy en día, por lo menos los más nombrados en cuanto a sus características de desarrollo y aplicación, ya desde hace tiempo he estado practicando con Box2D lo que lo hacen uno de mis favoritos y que me parece se adapta fácilmente a distintos tipos de desarrollo tanto móvil como de escritorio. Por otro lado es importante citar características sobre Nape y una de las más interesantes propuestas que tiene es el lenguaje en el que ha sido desarrollado conocido como Haxe, básicamente Nape es un motor físico de código abierto desarrollado por Luca Deltodesco; escrito en Haxe, dispone de una librería compilada SWC para AS3 que esencialmente da efectos muy interesantes al desarrollo que tenemos pensando y una de las características que sobresalen es su “velocidad”; respecto a su análogo Box2D del que ya he leído y buscado bastante; Erin Catto su desarrollador centra sus impresiones alrededor de los “cuerpo rígidos” y que es lo que significa el crear estos cuerpos rígidos dentro de un mundo box2d. Veamos estas diferencias con algo de código:


// este es un ejemplo de prueba del manejo de Box2D
package  
{
 import Box2D.Collision.Shapes.b2PolygonShape;
 import Box2D.Common.Math.b2Vec2;
 import flash.display.Sprite;
 import flash.events.Event;
 import Box2D.Dynamics.*;
 import Box2D.Common.*;
 
  public class MainBox2D  extends Sprite
 {
  // definimos nuestro mundo y la escala que manejamos
  private var world:b2World = new b2World(new b2Vec2(0, 10), true);
  private var worldScale:Number = 30;
  
  public function MainBox2D() 
  {
   var debugDraw:b2DebugDraw = new b2DebugDraw();
   var debugSprite:Sprite = new Sprite();
   addChild(debugSprite);
   
   debugDraw.SetSprite(debugSprite);
   debugDraw.SetDrawScale(worldScale);
   debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
   debugDraw.SetFillAlpha(0.5);
   
   world.SetDebugDraw(debugDraw);
   // defincion de cuerpo rigido
   var bodyDef:b2BodyDef = new b2BodyDef();
   bodyDef.position.Set(320 / worldScale, 460 / worldScale);
   
   var polygonShape:b2PolygonShape = new b2PolygonShape();
   polygonShape.SetAsBox(320 / worldScale, 20 / worldScale);
   var fixtureDef:b2FixtureDef = new b2FixtureDef();
   fixtureDef.shape = polygonShape;
   var staticFloor:b2Body = world.CreateBody(bodyDef);
   staticFloor.CreateFixture(fixtureDef);
   bodyDef.position.Set(320 / worldScale, 0 / worldScale);
   bodyDef.type = b2Body.b2_dynamicBody;
   polygonShape.SetAsBox(25 / worldScale, 25 / worldScale);
   fixtureDef.shape = polygonShape;
   var dinamicBox:b2Body = world.CreateBody(bodyDef);
   dinamicBox.CreateFixture(fixtureDef);
   //--------------------------------------------------
   addEventListener(Event.ENTER_FRAME, update);
  }
  private function update(e:Event):void {
   
   world.Step(1 / 30, 10, 10);
   world.ClearForces();
   world.DrawDebugData();
  }
 }
}


Este código se encarga de establecer dos cuerpos en el escenario, primero un piso en donde interactúa una caja cayendo de una determinada altura. Mientras que la versión en Napa seria esta:


package {
 import flash.display.Sprite;
 import flash.events.Event;
 import nape.geom.Vec2;
 import nape.phys.Body;
 import nape.phys.BodyType;
 import nape.shape.Polygon;
 import nape.space.Space;
 import nape.util.ShapeDebug;
 public class MainNape extends Sprite {
  private var world:Space=new Space(new Vec2(0,500));
  private var debug:ShapeDebug=new ShapeDebug(640,480,0x00ff00);
  public function MainNape() {
   addChild(debug.display);
   var staticFloor:Body=new Body(BodyType.STATIC,new Vec2(320,460));
   staticFloor.shapes.add(new Polygon(Polygon.box(640,40)));
   staticFloor.space=world;
   var dynamicBox:Body=new Body(BodyType.DYNAMIC,new Vec2(320,0));
   var block:Polygon=new Polygon(Polygon.box(50,50));
   dynamicBox.shapes.add(block);
   dynamicBox.space=world;
   addEventListener(Event.ENTER_FRAME, update);
  }
  private function update(e:Event):void {
   debug.clear();
   world.step(1/30,10,10);
   debug.draw(world);
   debug.flush();
  }
 }
}
Además de las pocas líneas de código menos podemos notar que; por ejemplo para la creación del mundo se hacen las declaraciones de manera diferente; en Box2D se maneja el objeto b2World y para establecer el componente gravedad se utiliza la función b2Vec2; mientras que en Napa se crea un espacio con Space al que se le añade una gravedad con la función Vec2, notamos además que se maneja dos diferentes tipos de escalas. La depuración de dibujos es más sistemática dentro de box2D y requiere más especificaciones, mientras que en Nape simplemente se define un cuerpo depurado como ShapeDebug al cual ya se le establece los parámetros al declararlo y este ya está listo para mostrarse en escenario esto hace que por ejemplo para definir una caja no parezca tan intuitivo como debería de ser, pero son los pasos que se siguen para establecer cuerpos rígidos. Debemos tomar en cuenta también que Box2D trabaja en metros y hay que convertir esto a pixeles. En Nape las formas básicas son realmente fáciles de crear simplemente se define un cuerpo y luego la forma que tiene ese cuerpo mientras todo está trabajando en pixeles. La forma de actualizar el mundo es crítica, por el proceso de renderización que conlleva y como vemos en código en Box2D se lleva a cabo en 3 simples líneas de código, el núcleo de la función Step en Box2D lleva a cabo este trabajo en 1/30 segundos a la vez. Nape es básicamente lo mismo. No existe mucha diferencia entre los dos motores y la exploración hacia otro lenguaje que plantea Nape como es Haxe la hace una opción interesante que disfruta de muchas prestaciones que no tendría con otros lenguajes y supongo que es un paso importante en la realización de otro tipo de proyectos.