Now that we have created our small world using the Box2D physic engine let’s start creating shapes! The shape factory will allow you to define any shapes you would like to add to your world. In this example we will create polygons.

Polygons in this example will be defined as a shape with 3 or more vertices with sides of equal length.

There are three steps in creating the object. Create the sprite, assign it to a body and generate a physics object. Keep in mind the sprite and the physics object to not need to be the same shape but they generally are. You could have a square displayed that acts in the physics world as a circle.

Create your sprite

public static function generatePolySprite(polyVO:PolyShapeVO):Sprite
{
	var polyClip:Sprite = new Sprite();
	polyClip.graphics.beginFill(polyVO.color);
	var tempPoints:Array = new Array()
	var ratio:Number = 360/polyVO.numSides;
	for(var i:int = 0;i <= 360;i += ratio)
	{
		var xx:Number=Math.sin(radians(i))*polyVO.radius;
		var yy:Number=(polyVO.radius-Math.cos(radians(i))*polyVO.radius);
		tempPoints.push(new Point(xx,yy));
	}
	
	polyClip.graphics.moveTo(tempPoints[tempPoints.length - 1].x, tempPoints[tempPoints.length - 1].y);
	for(var n:int = 0; n < tempPoints.length; n++){
		polyClip.graphics.lineTo(tempPoints[n].x, tempPoints[n].y);
	}
	return polyClip;
}

public static function radians(n:Number):Number 
{
	return(Math.PI/180*n);
}

Body definition.
The next function we will want is a body definition which will create an object in the physics world and assign our sprite to that object.

public static function generateBodyDef(shapeSprite:Sprite, box_width:int, box_height:int):b2BodyDef
{
	var result:b2BodyDef = new b2BodyDef();
	
	// Randomly place the shape.
	result.position.Set((Math.random() * (box_width - 200) + 100)/Constants.PHYS_SCALE, (Math.random() * (box_height - 200) + 100)/ Constants.PHYS_SCALE);
	
	// Set the userData (object Sprite)
	result.userData = shapeSprite;
	
	return result;
}		

And finally create the polygon definition.

public static function generatePolyDef(polyVO:PolyShapeVO):b2PolygonDef
{
	var result:b2PolygonDef = new b2PolygonDef();
	result.vertexCount = polyVO.numSides;
	
	var xf1:b2XForm = new b2XForm();
	
	var tempPoints:Array = new Array()
	var ratio:Number = 360/polyVO.numSides;
	for(var i:int = 0;i <= 360;i += ratio)
	{
		var xx:Number=Math.sin(radians(i))*polyVO.radius;
		var yy:Number=(polyVO.radius-Math.cos(radians(i))*polyVO.radius);
		tempPoints.push(new Point(xx,yy));
	}
	
	for(var n:int = 0; n < tempPoints.length; n++){
		result.vertices[n] = b2Math.b2MulX(xf1, new b2Vec2(tempPoints[n].x/Constants.PHYS_SCALE, tempPoints[n].y/Constants.PHYS_SCALE));
	}
				
	result.density = 10;
	result.restitution = Constants.RESTITUTION;
	result.friction = Constants.FRICTION;
	
	return result;
}

Now let’s extend our base environment from part 1 to allow for the addition of shapes.

public function addPoly(shapeVO:PolyShapeVO):void
{
	var squareClip:Sprite = ShapeFactory.generatePolySprite(shapeVO);
	
	if(debug_mode)
	{squareClip.alpha = 0.5;}
	
	addChild(squareClip);

	var bd:b2BodyDef = ShapeFactory.generateBodyDef(squareClip, box_width, box_height);
	var cd:b2PolygonDef = ShapeFactory.generatePolyDef(shapeVO);
	
	addShape(bd, cd);			
}

override protected function update(e:Event):void
{
	super.update(e);

 	for (var bb:b2Body = world.m_bodyList; bb; bb=bb.m_next) {
		if (bb.m_userData is Sprite) {
			bb.m_userData.x = bb.GetPosition().x * Constants.PHYS_SCALE;
			bb.m_userData.y = bb.GetPosition().y * Constants.PHYS_SCALE;
			bb.m_userData.rotation = bb.GetAngle() * 180 / Math.PI;
		}
	}  
}

private function addShape(bd:b2BodyDef, cd:b2ShapeDef):void
{
	var x_speed:Number = Math.random() * 10;
	
	body = world.CreateBody(bd);
	body.CreateShape(cd);
	body.SetMassFromShapes();
	
	// Assign a random speed to the shape
	body.m_linearVelocity.x = x_speed;
	body.m_linearVelocity.y = 10 - x_speed;						
}

Finally we will add logic to our main class to create the shapes.

private var _colors:Array = new Array(0xFF0000, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF);
private var _shape2d:ShapeEnvironment;

public function Box2DQuickStart()
{
	stage.scaleMode = StageScaleMode.NO_SCALE;
	stage.align = StageAlign.TOP_LEFT;
				
	_shape2d = new ShapeEnvironment(380, 280, true);
	_shape2d.x = 10;
	_shape2d.y = 10;
	
	//Add polygons
	for(var i:int = 1;i <=45; i++){
		var tempShapeVO:PolyShapeVO = new PolyShapeVO();
		tempShapeVO.color = _colors[i % _colors.length];
		tempShapeVO.numSides = 3 + i % 10;
		tempShapeVO.radius = 5 + Math.random()*20;
		_shape2d.addPoly(tempShapeVO);
	}
	
 	
	_shape2d.start();
	
	this.addChild(_shape2d);
}

Check out the final product along with some Anti-Gravity in part 3.

2 thoughts on “Box2d Part 2: Shape Factory

Comments are closed.