Talking about Actionscript 3, Box2D and Flash.
I got a lot of requests once I published how to create REAL explosions with Box2D and most of them asked for exploding objects clicking on them, setting the center of explosion with mouse pointer.
So I changed a bit the script and now you have such features, and to preserve speed I also removed all chunks whose area is too small. They just slow down the prototype with no advantage.
So that’s what you have now:
Click on any object (static or dynamic) to explode it.
And this is the uncommented, yet to be optimized script:
package {
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var laserSegment:b2Segment;
private var drawing:Boolean=false;
private var affectedByLaser:Vector.;
private var entryPoint:Vector.;
private var explodingBodies:Vector.;
private var explosionCenterX:Number;
private var explosionCenterY:Number;
private var chunks:Number=5;
private var explosionRadius:Number=50;
public function Main() {
debugDraw();
addWall(320,480,640,20);
addWall(320,0,640,20);
addWall(0,240,20,480);
addWall(640,240,20,480);
addWall(320,240,200,200);
addWall(250,110,60,60);
addWall(390,110,60,60);
addEventListener(Event.ENTER_FRAME, updateWorld);
stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed);
}
private function mousePressed(e:MouseEvent):void {
var cutAngle:Number;
explosionCenterX=mouseX;
explosionCenterY=mouseY;
var clickedBody:b2Body=GetBodyAtXY(new b2Vec2(explosionCenterX/worldScale,explosionCenterY/worldScale));
explodingBodies=new Vector.();
if (clickedBody!=null) {
explodingBodies.push(clickedBody);
for (var i:Number=0; i();
entryPoint=new Vector.();
world.RayCast(laserFired,laserSegment.p1,laserSegment.p2);
world.RayCast(laserFired,laserSegment.p2,laserSegment.p1);
}
}
}
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);
}
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);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
private function laserFired(fixture:b2Fixture,point:b2Vec2,normal:b2Vec2,fraction:Number):Number {
var affectedBody:b2Body=fixture.GetBody();
if (explodingBodies.indexOf(affectedBody)!=-1) {
var affectedPolygon:b2PolygonShape=fixture.GetShape() as b2PolygonShape;
var fixtureIndex:int=affectedByLaser.indexOf(affectedBody);
if (fixtureIndex==-1) {
affectedByLaser.push(affectedBody);
entryPoint.push(point);
}
else {
var rayCenter:b2Vec2=new b2Vec2((point.x+entryPoint[fixtureIndex].x)/2,(point.y+entryPoint[fixtureIndex].y)/2);
var rayAngle:Number=Math.atan2(entryPoint[fixtureIndex].y-point.y,entryPoint[fixtureIndex].x-point.x);
var polyVertices:Vector.=affectedPolygon.GetVertices();
var newPolyVertices1:Vector.=new Vector.();
var newPolyVertices2:Vector.=new Vector.();
var currentPoly:int=0;
var cutPlaced1:Boolean=false;
var cutPlaced2:Boolean=false;
for (var i:int=0; i0&&cutAngle<=Math.PI) {
if (currentPoly==2) {
cutPlaced1=true;
newPolyVertices1.push(point);
newPolyVertices1.push(entryPoint[fixtureIndex]);
}
newPolyVertices1.push(worldPoint);
currentPoly=1;
}
else {
if (currentPoly==1) {
cutPlaced2=true;
newPolyVertices2.push(entryPoint[fixtureIndex]);
newPolyVertices2.push(point);
}
newPolyVertices2.push(worldPoint);
currentPoly=2;
}
}
if (! cutPlaced1) {
newPolyVertices1.push(point);
newPolyVertices1.push(entryPoint[fixtureIndex]);
}
if (! cutPlaced2) {
newPolyVertices2.push(entryPoint[fixtureIndex]);
newPolyVertices2.push(point);
}
createSlice(newPolyVertices1,newPolyVertices1.length);
createSlice(newPolyVertices2,newPolyVertices2.length);
world.DestroyBody(affectedBody);
}
}
return 1;
}
private function findCentroid(vs:Vector., count:uint):b2Vec2 {
var c:b2Vec2 = new b2Vec2();
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, 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,numVertices:int):void {
if (getArea(vertices,vertices.length)>=0.05) {
var centre:b2Vec2=findCentroid(vertices,vertices.length);
for (var i:int=0; iexplosionRadius) {
distX=0;
}
else {
distX=50-distX;
}
}
var distY:Number=(centre.y*worldScale-explosionCenterY);
if (distY<0) {
if (distY<-explosionRadius) {
distY=0;
}
else {
distY=-50-distY;
}
}
else {
if (distY>explosionRadius) {
distY=0;
}
else {
distY=50-distY;
}
}
distX*=0.25;
distY*=0.25;
worldSlice.SetLinearVelocity(new b2Vec2(distX,distY));
explodingBodies.push(worldSlice);
}
}
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;
}
}
}
I will post the complete tutorial once I’ll complete the final step: adding textures to explosions. If you want to try the class, you can copy/paste it in the example you can download at slicing, splitting and cutting objects with Box2D.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.