用Box2D创建真正的爆炸—爆炸的物体以及用鼠标指定爆炸的中心
之前我发表了题为怎样使用Box2D来创建真正的爆炸的文章,然后我收到了很多的提问,其中大部分问到怎样实现在单击物体的时候发生爆炸,并且用鼠标指针设定爆炸的中心。
所以我稍微改了一点脚本,现在你已经可以实现这样的功能了,并且为了保持速度,我移除了所有的形状很小的物体。它们只会减慢原型的速度,而没有任何的好处。
所以这就是你要的了:
[flash]http://www.emanueleferonato.com/wp-content/uploads/2012/01/boom.swf[/flash]
单击任何一个对象(静态的或者是动态的),就可以让它爆炸了。
下面是没有注释的,并且还将要进行优化的代码:
- 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.<b2Body>;
- private var entryPoint:Vector.<b2Vec2>;
- private var explodingBodies:Vector.<b2Body>;
- 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.<b2Body>();
- if (clickedBody!=null) {
- explodingBodies.push(clickedBody);
- for (var i:Number=0; i<chunks; i++) {
- cutAngle=Math.random()*Math.PI*2;
- laserSegment=new b2Segment();
- laserSegment.p1=new b2Vec2((explosionCenterX+i/10-200*Math.cos(cutAngle))/worldScale,(explosionCenterY-200*Math.sin(cutAngle))/worldScale);
- laserSegment.p2=new b2Vec2((explosionCenterX+200*Math.cos(cutAngle))/worldScale,(explosionCenterY+200*Math.sin(cutAngle))/worldScale);
- affectedByLaser=new Vector.<b2Body>();
- entryPoint=new Vector.<b2Vec2>();
- 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.<b2Vec2>=affectedPolygon.GetVertices();
- var newPolyVertices1:Vector.<b2Vec2>=new Vector.<b2Vec2>();
- var newPolyVertices2:Vector.<b2Vec2>=new Vector.<b2Vec2>();
- var currentPoly:int=0;
- var cutPlaced1:Boolean=false;
- var cutPlaced2:Boolean=false;
- for (var i:int=0; i<polyVertices.length; i++) {
- var worldPoint:b2Vec2=affectedBody.GetWorldPoint(polyVertices[i]);
- var cutAngle:Number=Math.atan2(worldPoint.y-rayCenter.y,worldPoint.x-rayCenter.x)-rayAngle;
- if (cutAngle<Math.PI*-1) {
- cutAngle+=2*Math.PI;
- }
- if (cutAngle>0&&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.<b2Vec2>, 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?vs[int(i+1)]:vs[0];
- var e1X:Number=p2.x-p1X;
- var e1Y:Number=p2.y-p1Y;
- var e2X:Number=p3.x-p1X;
- var e2Y:Number=p3.y-p1Y;
- var D:Number = (e1X * e2Y - e1Y * e2X);
- var triangleArea:Number=0.5*D;
- area+=triangleArea;
- c.x += triangleArea * inv3 * (p1X + p2.x + p3.x);
- c.y += triangleArea * inv3 * (p1Y + p2.y + p3.y);
- }
- c.x*=1.0/area;
- c.y*=1.0/area;
- return c;
- }
- private function getArea(vs:Vector.<b2Vec2>, 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<count?vs[int(i+1)]:vs[0];
- var e1X:Number=p2.x-p1X;
- var e1Y:Number=p2.y-p1Y;
- var e2X:Number=p3.x-p1X;
- var e2Y:Number=p3.y-p1Y;
- var D:Number = (e1X * e2Y - e1Y * e2X);
- var triangleArea:Number=0.5*D;
- area+=triangleArea;
- }
- return area;
- }
- private function createSlice(vertices:Vector.<b2Vec2>,numVertices:int):void {
- if (getArea(vertices,vertices.length)>=0.05) {
- var centre:b2Vec2=findCentroid(vertices,vertices.length);
- for (var i:int=0; i<numVertices; i++) {
- vertices[i].Subtract(centre);
- }
- var sliceBody:b2BodyDef= new b2BodyDef();
- sliceBody.position.Set(centre.x, centre.y);
- sliceBody.type=b2Body.b2_dynamicBody;
- var slicePoly:b2PolygonShape = new b2PolygonShape();
- slicePoly.SetAsVector(vertices,numVertices);
- var sliceFixture:b2FixtureDef = new b2FixtureDef();
- sliceFixture.shape=slicePoly;
- sliceFixture.density=1;
- var worldSlice:b2Body=world.CreateBody(sliceBody);
- worldSlice.CreateFixture(sliceFixture);
- for (i=0; i<numVertices; i++) {
- vertices[i].Add(centre);
- }
- var distX:Number=(centre.x*worldScale-explosionCenterX);
- if (distX<0) {
- if (distX<-explosionRadius) {
- distX=0;
- }
- else {
- distX=-50-distX;
- }
- }
- else {
- if (distX>explosionRadius) {
- 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;
- }
- }
- }
一旦我完成了最后一步:给爆炸添加纹理,我将会发上完整的教程。如果你想试试这个类,你可以复制/粘贴代码到你的例子中,例子可以在这里下载:使用Box2D来实现对象的分片,爆炸以及切割。