转载自:http://bbs.9ria.com/thread-87206-1-1.html
在本系列教程的第二部分,我们展示了如何切割一个Box2D对象。但是这些例子只是在Box2D的debug draw环境下实现,所以整个过程并不能在实际的例子应用除非在你发布游戏的时候仍然使用debug draw图形。那么我们如何才能切割自定义的sprite,这样你才可以完成最后一步。在切割debug draw多边形时你可以看到碎片会产生随即颜色并下落。这些碎片是sprite实时生成的。下面让我们看看源代码看看跟前一步比发生了什么改变。
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 debris:Sprite; private var canvas:Sprite; private var laserSegment:b2Segment; private var drawing:Boolean=false; private var affectedByLaser:Vector.<b2Body>; private var entryPoint:Vector.<b2Vec2>; public function Main() { debugDraw(); addStuff(); debris = new Sprite(); addChild(debris); canvas = new Sprite(); addChild(canvas); addEventListener(Event.ENTER_FRAME, updateWorld); stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed); stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoved); stage.addEventListener(MouseEvent.MOUSE_UP,mouseReleased); } private function mousePressed(e:MouseEvent):void { drawing=true; laserSegment=new b2Segment(); laserSegment.p1=new b2Vec2(mouseX/worldScale,mouseY/worldScale); } private function mouseMoved(e:MouseEvent):void { if (drawing) { canvas.graphics.clear(); canvas.graphics.lineStyle(1,0xff0000); canvas.graphics.moveTo(laserSegment.p1.x*worldScale,laserSegment.p1.y*worldScale); canvas.graphics.lineTo(mouseX,mouseY); } } private function mouseReleased(e:MouseEvent):void { drawing=false; laserSegment.p2=new b2Vec2(mouseX/worldScale,mouseY/worldScale); } 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 addStuff():void { var floorBody:b2BodyDef= new b2BodyDef(); floorBody.position.Set(11,29); floorBody.userData=new Object(); var floorShape:b2PolygonShape = new b2PolygonShape(); floorShape.SetAsBox(40,15); var floorFixture:b2FixtureDef = new b2FixtureDef(); floorFixture.shape=floorShape; var worldFloor:b2Body=world.CreateBody(floorBody); worldFloor.CreateFixture(floorFixture); // var squareBody:b2BodyDef= new b2BodyDef(); squareBody.position.Set(16,5); var squareShape:b2PolygonShape = new b2PolygonShape(); squareShape.SetAsBox(2.5,2.5); var squareFixture:b2FixtureDef = new b2FixtureDef(); squareFixture.shape=squareShape; var worldSquare:b2Body=world.CreateBody(squareBody); worldSquare.CreateFixture(squareFixture); // var circleVector:Vector.<b2Vec2>=new Vector.<b2Vec2>(); var circleSteps:int=12; var circleRadius:Number=3; for (var i:int=0; i<circleSteps; i++) { circleVector.push(new b2Vec2(circleRadius*Math.cos(2*Math.PI/circleSteps*i),circleRadius*Math.sin(2*Math.PI/circleSteps*i))); } var circleBody:b2BodyDef= new b2BodyDef(); circleBody.position.Set(5,5); var circleShape:b2PolygonShape = new b2PolygonShape(); circleShape.SetAsVector(circleVector,circleSteps); var circleFixture:b2FixtureDef = new b2FixtureDef(); circleFixture.shape=circleShape; var worldCircle:b2Body=world.CreateBody(circleBody); worldCircle.CreateFixture(circleFixture); } private function updateWorld(e:Event):void { world.Step(1/30,10,10); world.ClearForces(); if (laserSegment&&! drawing) { affectedByLaser=new Vector.<b2Body>(); entryPoint=new Vector.<b2Vec2>(); world.RayCast(laserFired,laserSegment.p1,laserSegment.p2); world.RayCast(laserFired,laserSegment.p2,laserSegment.p1); laserSegment=null; } for (var currentBody:b2Body=world.GetBodyList(); currentBody; currentBody=currentBody.GetNext()) { if (currentBody.GetUserData()!=null) { currentBody.GetUserData().x=currentBody.GetPosition().x*worldScale; currentBody.GetUserData().y=currentBody.GetPosition().y*worldScale; currentBody.GetUserData().rotation=currentBody.GetAngle()*180/Math.PI; } } world.DrawDebugData(); } private function laserFired(fixture:b2Fixture,point:b2Vec2,normal:b2Vec2,fraction:Number):Number { var affectedBody:b2Body=fixture.GetBody(); 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); if (affectedBody.GetUserData()!=null) { debris.removeChild(affectedBody.GetUserData()); } 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 createSlice(vertices:Vector.<b2Vec2>,numVertices:int):void { var centre:b2Vec2=findCentroid(vertices,vertices.length); var sliceBody:b2BodyDef=new b2BodyDef ; sliceBody.position.Set(centre.x,centre.y); sliceBody.type=b2Body.b2_dynamicBody; sliceBody.userData=new Sprite ; debris.addChild(sliceBody.userData); sliceBody.userData.graphics.lineStyle(2,Math.random()*0xFFFFFF); sliceBody.userData.graphics.beginFill(Math.random()*0xFFFFFF); for (var i:int=0; i<numVertices; i++) { vertices[i].Subtract(centre); if (i==0) { sliceBody.userData.graphics.moveTo(vertices[i].x*worldScale,vertices[i].y*worldScale); } else { sliceBody.userData.graphics.lineTo(vertices[i].x*worldScale,vertices[i].y*worldScale); } } sliceBody.userData.graphics.lineTo(vertices[0].x*worldScale,vertices[0].y*worldScale); sliceBody.userData.graphics.endFill(); 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); } } } }
第12行debris这个Sprite是实时产生碎片的容器。
第21-22行先把debris添加到显示列表中,然后添加canvas。canvas中包含切割光线,这样切割光线就会一直在debris上面。现在我们已经有了debris容器,让我们修改createSlice方法在debris中添加一些子对象。
第197行创建一个自定义的sprite作为切割体,并把它赋值给body的userData属性
第198行在debris这个sprite中添加这个sprite
第199-200行随即设置线和颜色的填充。
第203-207行在此刻我们添加for循环扫描所有的顶点。目的是画多边形,我们移动画笔从第一个顶点到第二个然后一直画下去。
第209-210行当我们连接最后一个顶点时。从最后一个顶点到第一个顶点形成闭合的矩形。然后调用endFill方法结束绘画。通过这种方式我们就产生了以(0,0)为起点开始的多边形。它们将不能在移动了。现在让多边形根据它们所代表部分移动和旋转。为了这样我们需要修改updateWorld方法。
第102-108行这是一段比较好的代码实例来自Box2D的hello world例子。让我们同步Box2D和flash显示对象。现在我们知道如何绘制和移动自定义sprite。一旦对象产生了碎片我们需要把这个对象移出。第162-164行来做这个工作。
现在任务已经完成了。在下一步我们将要看到如何在这些sprite加上不同的材质。