Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Actionscript 3, Box2D and Flash.

Ok, you asked it, now you have it. Now the Box2D explosion prototype features bitmap textures and a commented source code.

I have to say a big thank you to Antoan Angelov for his version of the slicing engine as he allowed me to manage textures with no hassle.

So now basically an explosion is a number of cuts at a random angle, all passing for the same point, which is the origin of the explosion. Then, every resulting slice will have a linear velocity according to the distance between the center of the slice mass and the origin of the explosion.

So this is what you’ll get:

Click on a crate to make it explode.

And this is the full, commented source code:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.display.BitmapData;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class Main extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var enterPointsVec:Vector. = new Vector.();
		private var numEnterPoints:int=0;
		private var worldScale:Number=30;
		// variables used in the explosion process:
		// the vector of exploding bodies
		private var explodingBodies:Vector.;
		// the number of cuts for every explosion
		private var explosionCuts:Number=5;
		// explosion x and y center
		private var explosionX:Number;
		private var explosionY:Number;
		// explosion radius, useful to determine the velocity of debris
		private var explosionRadius:Number=50;
		public function Main() {
			// calling the debug draw. This is used to show you the bitmaps are correctly applied,
			// and because I did not want to draw the walls :)
			debugDraw();
			// this is the BitmapData representation of my 100x100 pixels crate image
			// check the library to see both the raw image and CrateImage Sprite
			var crateBitmap:BitmapData=new BitmapData(100,100);
			crateBitmap.draw(new CrateImage());
			// adding the four static, undestroyable walls
			addWall(320,480,640,20);
			addWall(320,0,640,20);
			addWall(0,240,20,480);
			addWall(640,240,20,480);
			// this vector stores the clockwise local coordinates of the 100x100 pixels crate
			var crateCoordVector:Vector.=new [new b2Vec2(-50,-50),new b2Vec2(50,-50),new b2Vec2(50,50),new b2Vec2(-50,50)];
			// then createBody builds the final body and applies the bitmap.
			// the first two arguments are the X and Y position of the center of the crate, in pixels
			createBody(95,420,crateCoordVector,crateBitmap);
			createBody(245,420,crateCoordVector,crateBitmap);
			createBody(395,420,crateCoordVector,crateBitmap);
			createBody(545,420,crateCoordVector,crateBitmap);
			createBody(170,320,crateCoordVector,crateBitmap);
			createBody(320,320,crateCoordVector,crateBitmap);
			createBody(470,320,crateCoordVector,crateBitmap);
			createBody(245,220,crateCoordVector,crateBitmap);
			createBody(395,220,crateCoordVector,crateBitmap);
			createBody(320,120,crateCoordVector,crateBitmap);
			// You can see the reason for creating the enterPointsVec in the coments in the intersection() method.
			enterPointsVec=new Vector.(numEnterPoints);
			// listeners
			stage.addEventListener(MouseEvent.MOUSE_DOWN, boom);
			addEventListener(Event.ENTER_FRAME, update);
		}
		// my old friend debugDraw function
		private function debugDraw():void {
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);
			debugDraw.SetSprite(debugSprite);
			debugDraw.SetDrawScale(worldScale);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			debugDraw.SetFillAlpha(0.5);
			world.SetDebugDraw(debugDraw);
		}
		// this function returns the body at a X,Y coordinate without using a temp body like the one in
		// the original Box2D distribution. It uses QueryPoint method.
		// returns the body ad X,Y coordinate or null
		private function GetBodyAtXY(coordinate:b2Vec2):b2Body {
			var touchedBody:b2Body=null;
			world.QueryPoint(GetBodyCallback,coordinate);
			function GetBodyCallback(fixture:b2Fixture):Boolean {
				var shape:b2Shape=fixture.GetShape();
				var inside:Boolean=shape.TestPoint(fixture.GetBody().GetTransform(),coordinate);
				if (inside) {
					touchedBody=fixture.GetBody();
					return false;
				}
				return true;
			}
			return touchedBody;
		}
		// simple function to add a static wall
		private function addWall(pX:Number,pY:Number,w:Number,h:Number):void {
			var wallShape:b2PolygonShape = new b2PolygonShape();
			wallShape.SetAsBox(w/worldScale/2,h/worldScale/2);
			var wallFixture:b2FixtureDef = new b2FixtureDef();
			wallFixture.density=0;
			wallFixture.friction=1;
			wallFixture.restitution=0.5;
			wallFixture.shape=wallShape;
			var wallBodyDef:b2BodyDef = new b2BodyDef();
			wallBodyDef.position.Set(pX/worldScale,pY/worldScale);
			var wall:b2Body=world.CreateBody(wallBodyDef);
			wall.CreateFixture(wallFixture);
			numEnterPoints++;
		}
		// function to create and texture a dynamic body
		private function createBody(xPos:Number, yPos:Number, verticesArr:Vector., texture:BitmapData) {
			// I need this temp vector to convert pixels coordinates to Box2D meters coordinates
			var vec:Vector.=new Vector.();
			for (var i:Number=0; i();
				explodingBodies.push(clickedBody);
				// the explosion begins!
				for (var i:Number=1; i<=explosionCuts; i++) {
					// choosing a random angle
					cutAngle=Math.random()*Math.PI*2;
					// creating the two points to be used for the raycast, according to the random angle and mouse position
					// also notice how I need to add a little offset (i/10) or Box2D will crash. Probably it's not able to 
					// determine raycast on objects whose area is very very close to zero (or zero)
					var p1:b2Vec2=new b2Vec2((explosionX+i/10-2000*Math.cos(cutAngle))/worldScale,(explosionY-2000*Math.sin(cutAngle))/worldScale);
					var p2:b2Vec2=new b2Vec2((explosionX+2000*Math.cos(cutAngle))/worldScale,(explosionY+2000*Math.sin(cutAngle))/worldScale);
					world.RayCast(intersection, p1, p2);
					world.RayCast(intersection, p2, p1);
					enterPointsVec=new Vector.(numEnterPoints);
				}
			}
		}
		// update function to simulate and render the world
		public function update(e:Event):void {
			world.Step(1/30, 10, 10);
			world.ClearForces();
			var spr:Sprite;
			for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
				spr=b.GetUserData();
				if (spr) {
					spr.x=b.GetPosition().x*worldScale;
					spr.y=b.GetPosition().y*worldScale;
					spr.rotation=b.GetAngle()*180/Math.PI;
				}
			}
			world.DrawDebugData();
		}
		private function intersection(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:Number):Number {
			if (explodingBodies.indexOf(fixture.GetBody())!=-1) {
				var spr:Sprite=fixture.GetBody().GetUserData();
				// Throughout this whole code I use only one global vector, and that is enterPointsVec. Why do I need it you ask? 
				// Well, the problem is that the world.RayCast() method calls this function only when it sees that a given line gets into the body - it doesnt see when the line gets out of it.
				// I must have 2 intersection points with a body so that it can be sliced, thats why I use world.RayCast() again, but this time from B to A - that way the point, at which BA enters the body is the point at which AB leaves it!
				// For that reason, I use a vector enterPointsVec, where I store the points, at which AB enters the body. And later on, if I see that BA enters a body, which has been entered already by AB, I fire the splitObj() function!
				// I need a unique ID for each body, in order to know where its corresponding enter point is - I store that id in the userData of each body.
				if (spr is userData) {
					var userD:userData=spr as userData;
					if (enterPointsVec[userD.id]) {
						// If this body has already had an intersection point, then it now has two intersection points, thus it must be split in two - thats where the splitObj() method comes in.
						splitObj(fixture.GetBody(), enterPointsVec[userD.id], point.Copy());
					}
					else {
						enterPointsVec[userD.id]=point;
					}
				}
			}
			return 1;
		}
		// function to get the area of a shape. I will remove tiny shape to increase performance
		private function getArea(vs:Vector., count:uint):Number {
			var area:Number=0.0;
			var p1X:Number=0.0;
			var p1Y:Number=0.0;
			var inv3:Number=1.0/3.0;
			for (var i:int = 0; i < count; ++i) {
				var p2:b2Vec2=vs[i];
				var p3:b2Vec2=i+1=poly.GetVertices(),numVertices:int=poly.GetVertexCount();
			var shape1Vertices:Vector. = new Vector.(), shape2Vertices:Vector. = new Vector.();
			var origUserData:userData=sliceBody.GetUserData(),origUserDataId:int=origUserData.id,d:Number;
			var polyShape:b2PolygonShape=new b2PolygonShape();
			var body:b2Body;
			// First, I destroy the original body and remove its Sprite representation from the childlist.
			world.DestroyBody(sliceBody);
			removeChild(origUserData);
			// The world.RayCast() method returns points in world coordinates, so I use the b2Body.GetLocalPoint() to convert them to local coordinates.;
			A=sliceBody.GetLocalPoint(A);
			B=sliceBody.GetLocalPoint(B);
			// I use shape1Vertices and shape2Vertices to store the vertices of the two new shapes that are about to be created. 
			// Since both point A and B are vertices of the two new shapes, I add them to both vectors.
			shape1Vertices.push(A, B);
			shape2Vertices.push(A, B);
			// I iterate over all vertices of the original body. ;
			// I use the function det() ("det" stands for "determinant") to see on which side of AB each point is standing on. The parameters it needs are the coordinates of 3 points:
			// - if it returns a value >0, then the three points are in clockwise order (the point is under AB)
			// - if it returns a value =0, then the three points lie on the same line (the point is on AB)
			// - if it returns a value <0, then the three points are in counter-clockwise order (the point is above AB). 
			for (var i:Number=0; i0) {
					shape1Vertices.push(verticesVec[i]);
				}
				else {
					shape2Vertices.push(verticesVec[i]);
				}
			}
			// In order to be able to create the two new shapes, I need to have the vertices arranged in clockwise order.
			// I call my custom method, arrangeClockwise(), which takes as a parameter a vector, representing the coordinates of the shape's vertices and returns a new vector, with the same points arranged clockwise.
			shape1Vertices=arrangeClockwise(shape1Vertices);
			shape2Vertices=arrangeClockwise(shape2Vertices);
			// setting the properties of the two newly created shapes
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.type=b2Body.b2_dynamicBody;
			bodyDef.position=sliceBody.GetPosition();
			var fixtureDef:b2FixtureDef = new b2FixtureDef();
			fixtureDef.density=origFixture.GetDensity();
			fixtureDef.friction=origFixture.GetFriction();
			fixtureDef.restitution=origFixture.GetRestitution();
			// creating the first shape, if big enough
			if (getArea(shape1Vertices,shape1Vertices.length)>=0.05) {
				polyShape.SetAsVector(shape1Vertices);
				fixtureDef.shape=polyShape;
				bodyDef.userData=new userData(origUserDataId,shape1Vertices,origUserData.texture);
				addChild(bodyDef.userData);
				enterPointsVec[origUserDataId]=null;
				body=world.CreateBody(bodyDef);
				body.SetAngle(sliceBody.GetAngle());
				body.CreateFixture(fixtureDef);
				// setting a velocity for the debris
				body.SetLinearVelocity(setExplosionVelocity(body));
				// the shape will be also part of the explosion and can explode too
				explodingBodies.push(body);
			}
			// creating the second shape, if big enough
			if (getArea(shape2Vertices,shape2Vertices.length)>=0.05) {
				polyShape.SetAsVector(shape2Vertices);
				fixtureDef.shape=polyShape;
				bodyDef.userData=new userData(numEnterPoints,shape2Vertices,origUserData.texture);
				addChild(bodyDef.userData);
				enterPointsVec.push(null);
				numEnterPoints++;
				body=world.CreateBody(bodyDef);
				body.SetAngle(sliceBody.GetAngle());
				body.CreateFixture(fixtureDef);
				// setting a velocity for the debris
				body.SetLinearVelocity(setExplosionVelocity(body));
				// the shape will be also part of the explosion and can explode too
				explodingBodies.push(body);
			}
		}
		// this function will determine the velocity of the debris according
		// to the center of mass of the body and the distance from the explosion point
		private function setExplosionVelocity(b:b2Body):b2Vec2 {
			var distX:Number=b.GetWorldCenter().x*worldScale-explosionX;
			if (distX<0) {
				if (distX<-explosionRadius) {
					distX=0;
				}
				else {
					distX=- explosionRadius-distX;
				}
			}
			else {
				if (distX>explosionRadius) {
					distX=0;
				}
				else {
					distX=explosionRadius-distX;
				}
			}
			var distY:Number=b.GetWorldCenter().y*worldScale-explosionY;
			if (distY<0) {
				if (distY<-explosionRadius) {
					distY=0;
				}
				else {
					distY=- explosionRadius-distY;
				}
			}
			else {
				if (distY>explosionRadius) {
					distY=0;
				}
				else {
					distY=explosionRadius-distY;
				}
			}
			distX*=0.25;
			distY*=0.25;
			return new b2Vec2(distX,distY);
		}
		private function arrangeClockwise(vec:Vector.):Vector. {
			// The algorithm is simple: 
			// First, it arranges all given points in ascending order, according to their x-coordinate.
			// Secondly, it takes the leftmost and rightmost points (lets call them C and D), and creates tempVec, where the points arranged in clockwise order will be stored.
			// Then, it iterates over the vertices vector, and uses the det() method I talked about earlier. It starts putting the points above CD from the beginning of the vector, and the points below CD from the end of the vector. 
			// That was it!
			var n:int=vec.length,d:Number,i1:int=1,i2:int=n-1;
			var tempVec:Vector.=new Vector.(n),C:b2Vec2,D:b2Vec2;
			vec.sort(comp1);
			tempVec[0]=vec[0];
			C=vec[0];
			D=vec[n-1];
			for (var i:Number=1; ib.x) {
				return 1;
			}
			else if (a.x

and this is userData class:

package {
	import Box2D.Common.Math.b2Vec2;
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.geom.Matrix;
	public class userData extends Sprite {
		var id:int,texture:BitmapData;
		public function userData(id:int, verticesVec:Vector., texture:BitmapData) {
			this.id=id;
			this.texture=texture;
			// I use the matrix so that I can have the center of the shape I'm drawing match the center of the BitmapData image - I "move" the BitmapData projection left by half its width and up by half its height.
			var m:Matrix = new Matrix();
			m.tx=- texture.width*0.5;
			m.ty=- texture.height*0.5;
			// I then draw lines from each vertex to the next, in clockwise order and use the beginBitmapFill() method to add the texture.
			this.graphics.lineStyle(1);
			this.graphics.beginBitmapFill(texture, m, true, true);
			this.graphics.moveTo(verticesVec[0].x*30, verticesVec[0].y*30);
			for (var i:int=1; i

And now let me see what kind of games can you make using this concept.

Download the source code.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.