• flash 切割游戏


    引用:http://bbs.9ria.com/thread-114856-1-1.html

    Box2D切割引擎,这个引擎是从Box2D爆炸引擎演化而来的。

     

    现在就要使用相同的思想以最真实的方式来切割刚体。

     

    这是我做的效果:

          

    在舞台上点击鼠标,会有一个小球以随机的速度从边上射出,看看它是怎么切割这块木桩的。我使用了“bullet  time”来做这个东西,所以你可以看看它是怎么工作的,并且由于当这个小球以高速度飞行的时候,我有的时候会遇到一些问题,但是现在这里只是一个原型。

     

    不管怎样,它是按下面的方式运行的:

     

    当小球触碰到木桩的时候,我自定义的接触监听器对它进行了监听;

     

    这时,我知道小球的位置和速度。假设小球可以切断这块木桩,通过小球的位置以及方向,我可以确定一条切割线;

     

    将切割线应用到木桩上面,将它切成两半;

     

    最后我将这个小球克隆,创建另外一个新的小球,这个小球与原来的小球有相同的速度和方向,这个小球将会和木桩相碰,用来展现小球和木桩碎片相碰撞的效果。

     

    基本上就是这样了。

     

    我将所有带有注释的源码贴出来:

    1. package {
    2.         import flash.display.Sprite;
    3.         import flash.events.Event;
    4.         import flash.events.MouseEvent;
    5.         import flash.geom.Matrix;
    6.         import flash.display.BitmapData;
    7.         import Box2D.Dynamics.*;
    8.         import Box2D.Collision.*;
    9.         import Box2D.Collision.Shapes.*;
    10.         import Box2D.Common.Math.*;
    11.         public class Main extends Sprite {
    12.                 private var world:b2World=new b2World(new b2Vec2(0,10),true);
    13.                 // You can see the reason for creating the enterPointsVec in the coments in the intersection() method.
    14.                 private var enterPointsVec:Vector.<b2Vec2> = new Vector.<b2Vec2>();
    15.                 private var numEnterPoints:int=0;
    16.                 private var worldScale:Number=30;
    17.                 // custom contact listener
    18.                 private var customContact=new CustomContactListener();
    19.                 public function Main() {
    20.                         // defining the custom contact listener
    21.                         world.SetContactListener(customContact);
    22.                         // calling the debug draw. This is used to show you the bitmaps are correctly applied,
    23.                         // and because I did not want to draw the walls :)
    24.                         debugDraw();
    25.                         // this is the BitmapData representation of the wood
    26.                         // check the library to see both the raw image and the sprite
    27.                         var woodBitmap:BitmapData=new BitmapData(50,400);
    28.                         woodBitmap.draw(new WoodImage());
    29.                         // adding the four static, undestroyable walls;
    30.                         addWall(320,480,640,20);
    31.                         addWall(320,0,640,20);
    32.                         addWall(0,240,20,480);
    33.                         addWall(640,240,20,480);
    34.                         // createBody builds the final body and applies the bitmap.
    35.                         createBody(400,270,new <b2Vec2>[new b2Vec2(-25,-200),new b2Vec2(25,-200),new b2Vec2(25,200),new b2Vec2(-25,200)],woodBitmap);
    36.                         // You can see the reason for creating the enterPointsVec in the coments in the intersection() method.
    37.                         enterPointsVec=new Vector.<b2Vec2>(numEnterPoints);
    38.                         // listeners
    39.                         stage.addEventListener(MouseEvent.MOUSE_DOWN, shoot);
    40.                         addEventListener(Event.ENTER_FRAME, update);
    41.                 }
    42.                 // my old friend debugDraw function
    43.                 private function debugDraw():void {
    44.                         var debugDraw:b2DebugDraw = new b2DebugDraw();
    45.                         var debugSprite:Sprite = new Sprite();
    46.                         addChild(debugSprite);
    47.                         debugDraw.SetSprite(debugSprite);
    48.                         debugDraw.SetDrawScale(worldScale);
    49.                         debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
    50.                         debugDraw.SetFillAlpha(0.5);
    51.                         world.SetDebugDraw(debugDraw);
    52.                 }
    53.                 // function that will add the sphere and launch it
    54.                 // arguments are respectively x position, y position, radius, x velocity and y velocity
    55.                 private function addSphere(pX:Number,pY:Number,r:Number,vX:Number,vY:Number):void {
    56.                         var theSphere:b2Body;
    57.                         var sphereShape:b2CircleShape=new b2CircleShape(r/worldScale);
    58.                         var sphereFixture:b2FixtureDef = new b2FixtureDef();
    59.                         sphereFixture.density=1;
    60.                         sphereFixture.friction=3;
    61.                         sphereFixture.restitution=0.1;
    62.                         sphereFixture.shape=sphereShape;
    63.                         var sphereBodyDef:b2BodyDef = new b2BodyDef();
    64.                         sphereBodyDef.type=b2Body.b2_dynamicBody;
    65.                         sphereBodyDef.position.Set(pX/worldScale,pY/worldScale);
    66.                         sphereBodyDef.bullet=true;
    67.                         sphereBodyDef.userData={assetName:"sphere",collided:false};
    68.                         theSphere=world.CreateBody(sphereBodyDef);
    69.                         theSphere.CreateFixture(sphereFixture);
    70.                         theSphere.SetLinearVelocity(new b2Vec2(vX,vY));
    71.                 }
    72.                 // simple function to add a static wall
    73.                 private function addWall(pX:Number,pY:Number,w:Number,h:Number):void {
    74.                         var wallShape:b2PolygonShape = new b2PolygonShape();
    75.                         wallShape.SetAsBox(w/worldScale/2,h/worldScale/2);
    76.                         var wallFixture:b2FixtureDef = new b2FixtureDef();
    77.                         wallFixture.density=0;
    78.                         wallFixture.friction=1;
    79.                         wallFixture.restitution=0.5;
    80.                         wallFixture.shape=wallShape;
    81.                         var wallBodyDef:b2BodyDef = new b2BodyDef();
    82.                         wallBodyDef.position.Set(pX/worldScale,pY/worldScale);
    83.                         wallBodyDef.userData={assetName:"wall"};
    84.                         var wall:b2Body=world.CreateBody(wallBodyDef);
    85.                         wall.CreateFixture(wallFixture);
    86.                         numEnterPoints++;
    87.                 }
    88.                 // function to create and texture a dynamic body
    89.                 private function createBody(xPos:Number, yPos:Number, verticesArr:Vector.<b2Vec2>, texture:BitmapData) {
    90.                         // I need this temp vector to convert pixels coordinates to Box2D meters coordinates
    91.                         var vec:Vector.<b2Vec2>=new Vector.<b2Vec2>();
    92.                         for (var i:Number=0; i<verticesArr.length; i++) {
    93.                                 vec.push(new b2Vec2(verticesArr[i].x/worldScale,verticesArr[i].y/worldScale));
    94.                         }
    95.                         var bodyDef:b2BodyDef = new b2BodyDef();
    96.                         bodyDef.type=b2Body.b2_dynamicBody;
    97.                         var boxDef:b2PolygonShape = new b2PolygonShape();
    98.                         boxDef.SetAsVector(vec);
    99.                         bodyDef.position.Set(xPos/worldScale, yPos/worldScale);
    100.                         // custom userData used to map the texture;
    101.                         bodyDef.userData={assetName:"wood",textureData:new userData(numEnterPoints,vec,texture)};
    102.                         addChild(bodyDef.userData.textureData);
    103.                         var fixtureDef:b2FixtureDef = new b2FixtureDef();
    104.                         fixtureDef.density=1;
    105.                         fixtureDef.friction=0.2;
    106.                         fixtureDef.restitution=0.5;
    107.                         fixtureDef.shape=boxDef;
    108.                         var tempBox:b2Body=world.CreateBody(bodyDef);
    109.                         tempBox.CreateFixture(fixtureDef);
    110.                         numEnterPoints++;
    111.                 }
    112.                 // function to shoot the ball
    113.                 private function shoot(e:MouseEvent) {
    114.                         addSphere(20,240,15,50,Math.random()*40-20);
    115.                         stage.removeEventListener(MouseEvent.MOUSE_DOWN,shoot);
    116.                 }
    117.                 // update function to simulate and render the world
    118.                 public function update(e:Event):void {
    119.                         world.Step(1/300, 10, 10);
    120.                         world.ClearForces();
    121.                         var spr:Sprite;
    122.                         for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
    123.                                 if (b.GetUserData()!=null) {
    124.                                         // according to the collision listener, I am here when the sphere hits the wood
    125.                                         if (b.GetUserData().assetName=="sphere"&&b.GetUserData().collided) {
    126.                                                 // retrieving sphere velocity
    127.                                                 var sphereVelocity:b2Vec2=b.GetLinearVelocity();
    128.                                                 // getting direction according to velocity
    129.                                                 var sphereDirection:Number=Math.atan2(sphereVelocity.y,sphereVelocity.x);
    130.                                                 // getting sphere position
    131.                                                 var colX:Number=b.GetPosition().x;
    132.                                                 var colY:Number=b.GetPosition().y;
    133.                                                 // clone the sphere
    134.                                                 addSphere(colX*worldScale,colY*worldScale,15,sphereVelocity.x,sphereVelocity.y);
    135.                                                 // perform the raycast
    136.                                                 world.RayCast(intersection, new b2Vec2(colX,colY), new b2Vec2(colX+5*Math.cos(sphereDirection),colY+5*Math.sin(sphereDirection)));
    137.                                                 world.RayCast(intersection, new b2Vec2(colX+5*Math.cos(sphereDirection),colY+5*Math.sin(sphereDirection)), new b2Vec2(colX,colY));
    138.                                                 // destroy the original sphere
    139.                                                 world.DestroyBody(b);
    140.                                         }
    141.                                 }
    142.                         }
    143.                         // update graphics on stage
    144.                         for (b = world.GetBodyList(); b; b = b.GetNext()) {
    145.                                 if (b.GetUserData()!=null) {
    146.                                         spr=b.GetUserData().textureData;
    147.                                         if (spr) {
    148.                                                 spr.x=b.GetPosition().x*worldScale;
    149.                                                 spr.y=b.GetPosition().y*worldScale;
    150.                                                 spr.rotation=b.GetAngle()*180/Math.PI;
    151.                                         }
    152.                                 }
    153.                         }
    154.                         world.DrawDebugData();
    155.                 }
    156.                 private function intersection(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:Number):Number {
    157.                         if (fixture.GetBody().GetUserData()) {
    158.                                 if (fixture.GetBody().GetUserData().textureData!=null) {
    159.                                         var spr:Sprite=fixture.GetBody().GetUserData().textureData;
    160.                                         // Throughout this whole code I use only one global vector, and that is enterPointsVec. Why do I need it you ask? 
    161.                                         // 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.
    162.                                         // 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!
    163.                                         // 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!
    164.                                         // 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.
    165.                                         if (spr is userData) {
    166.                                                 var userD:userData=spr as userData;
    167.                                                 if (enterPointsVec[userD.id]) {
    168.                                                         // 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.
    169.                                                         splitObj(fixture.GetBody(), enterPointsVec[userD.id], point.Copy());
    170.                                                 }
    171.                                                 else {
    172.                                                         enterPointsVec[userD.id]=point;
    173.                                                 }
    174.                                         }
    175.                                 }
    176.                         }
    177.                         return 1;
    178.                 }
    179.                 // function to get the area of a shape. I will remove tiny shape to increase performance
    180.                 private function getArea(vs:Vector.<b2Vec2>, count:uint):Number {
    181.                         var area:Number=0.0;
    182.                         var p1X:Number=0.0;
    183.                         var p1Y:Number=0.0;
    184.                         var inv3:Number=1.0/3.0;
    185.                         for (var i:int = 0; i < count; ++i) {
    186.                                 var p2:b2Vec2=vs[i];
    187.                                 var p3:b2Vec2=i+1<count?vs[int(i+1)]:vs[0];
    188.                                 var e1X:Number=p2.x-p1X;
    189.                                 var e1Y:Number=p2.y-p1Y;
    190.                                 var e2X:Number=p3.x-p1X;
    191.                                 var e2Y:Number=p3.y-p1Y;
    192.                                 var D:Number = (e1X * e2Y - e1Y * e2X);
    193.                                 var triangleArea:Number=0.5*D;
    194.                                 area+=triangleArea;
    195.                         }
    196.                         return area;
    197.                 }
    198.                 private function splitObj(sliceBody:b2Body, A:b2Vec2, B:b2Vec2):void {
    199.                         var origFixture:b2Fixture=sliceBody.GetFixtureList();
    200.                         var poly:b2PolygonShape=origFixture.GetShape() as b2PolygonShape;
    201.                         var verticesVec:Vector.<b2Vec2>=poly.GetVertices(),numVertices:int=poly.GetVertexCount();
    202.                         var shape1Vertices:Vector.<b2Vec2> = new Vector.<b2Vec2>(), shape2Vertices:Vector.<b2Vec2> = new Vector.<b2Vec2>();
    203.                         var origUserData:userData=sliceBody.GetUserData().textureData,origUserDataId:int=origUserData.id,d:Number;
    204.                         var polyShape:b2PolygonShape=new b2PolygonShape();
    205.                         var body:b2Body;
    206.                         // First, I destroy the original body and remove its Sprite representation from the childlist.
    207.                         world.DestroyBody(sliceBody);
    208.                         removeChild(origUserData);
    209.                         // The world.RayCast() method returns points in world coordinates, so I use the b2Body.GetLocalPoint() to convert them to local coordinates.;
    210.                         A=sliceBody.GetLocalPoint(A);
    211.                         B=sliceBody.GetLocalPoint(B);
    212.                         // I use shape1Vertices and shape2Vertices to store the vertices of the two new shapes that are about to be created. 
    213.                         // Since both point A and B are vertices of the two new shapes, I add them to both vectors.
    214.                         shape1Vertices.push(A, B);
    215.                         shape2Vertices.push(A, B);
    216.                         // I iterate over all vertices of the original body. ;
    217.                         // 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:
    218.                         // - if it returns a value >0, then the three points are in clockwise order (the point is under AB)
    219.                         // - if it returns a value =0, then the three points lie on the same line (the point is on AB)
    220.                         // - if it returns a value <0, then the three points are in counter-clockwise order (the point is above AB). 
    221.                         for (var i:Number=0; i<numVertices; i++) {
    222.                                 d=det(A.x,A.y,B.x,B.y,verticesVec[i].x,verticesVec[i].y);
    223.                                 if (d>0) {
    224.                                         shape1Vertices.push(verticesVec[i]);
    225.                                 }
    226.                                 else {
    227.                                         shape2Vertices.push(verticesVec[i]);
    228.                                 }
    229.                         }
    230.                         // In order to be able to create the two new shapes, I need to have the vertices arranged in clockwise order.
    231.                         // 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.
    232.                         shape1Vertices=arrangeClockwise(shape1Vertices);
    233.                         shape2Vertices=arrangeClockwise(shape2Vertices);
    234.                         // setting the properties of the two newly created shapes
    235.                         var bodyDef:b2BodyDef = new b2BodyDef();
    236.                         bodyDef.type=b2Body.b2_dynamicBody;
    237.                         bodyDef.position=sliceBody.GetPosition();
    238.                         var fixtureDef:b2FixtureDef = new b2FixtureDef();
    239.                         fixtureDef.density=origFixture.GetDensity();
    240.                         fixtureDef.friction=origFixture.GetFriction();
    241.                         fixtureDef.restitution=origFixture.GetRestitution();
    242.                         // creating the first shape, if big enough
    243.                         if (getArea(shape1Vertices,shape1Vertices.length)>=0.05) {
    244.                                 polyShape.SetAsVector(shape1Vertices);
    245.                                 fixtureDef.shape=polyShape;
    246.                                 bodyDef.userData={assetName:"debris",textureData:new userData(origUserDataId,shape1Vertices,origUserData.texture)};
    247.                                 addChild(bodyDef.userData.textureData);
    248.                                 enterPointsVec[origUserDataId]=null;
    249.                                 body=world.CreateBody(bodyDef);
    250.                                 body.SetAngle(sliceBody.GetAngle());
    251.                                 body.CreateFixture(fixtureDef);
    252.                         }
    253.                         // creating the second shape, if big enough;
    254.                         if (getArea(shape2Vertices,shape2Vertices.length)>=0.05) {
    255.                                 polyShape.SetAsVector(shape2Vertices);
    256.                                 fixtureDef.shape=polyShape;
    257.                                 bodyDef.userData={assetName:"debris",textureData:new userData(origUserDataId,shape2Vertices,origUserData.texture)};
    258.                                 addChild(bodyDef.userData.textureData);
    259.                                 enterPointsVec.push(null);
    260.                                 numEnterPoints++;
    261.                                 body=world.CreateBody(bodyDef);
    262.                                 body.SetAngle(sliceBody.GetAngle());
    263.                                 body.CreateFixture(fixtureDef);
    264.                         }
    265.                 }
    266.                 private function arrangeClockwise(vec:Vector.<b2Vec2>):Vector.<b2Vec2> {
    267.                         // The algorithm is simple: 
    268.                         // First, it arranges all given points in ascending order, according to their x-coordinate.
    269.                         // 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.
    270.                         // 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. 
    271.                         // That was it!
    272.                         var n:int=vec.length,d:Number,i1:int=1,i2:int=n-1;
    273.                         var tempVec:Vector.<b2Vec2>=new Vector.<b2Vec2>(n),C:b2Vec2,D:b2Vec2;
    274.                         vec.sort(comp1);
    275.                         tempVec[0]=vec[0];
    276.                         C=vec[0];
    277.                         D=vec[n-1];
    278.                         for (var i:Number=1; i<n-1; i++) {
    279.                                 d=det(C.x,C.y,D.x,D.y,vec[i].x,vec[i].y);
    280.                                 if (d<0) {
    281.                                         tempVec[i1++]=vec[i];
    282.                                 }
    283.                                 else {
    284.                                         tempVec[i2--]=vec[i];
    285.                                 }
    286.                         }
    287.                         tempVec[i1]=vec[n-1];
    288.                         return tempVec;
    289.                 }
    290.                 private function comp1(a:b2Vec2, b:b2Vec2):Number {
    291.                         // This is a compare function, used in the arrangeClockwise() method - a fast way to arrange the points in ascending order, according to their x-coordinate.
    292.                         if (a.x>b.x) {
    293.                                 return 1;
    294.                         }
    295.                         else if (a.x<b.x) {
    296.                                 return -1;
    297.                         }
    298.                         return 0;
    299.                 }
    300.                 private function det(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number):Number {
    301.                         // This is a function which finds the determinant of a 3x3 matrix.
    302.                         // If you studied matrices, you'd know that it returns a positive number if three given points are in clockwise order, negative if they are in anti-clockwise order and zero if they lie on the same line.
    303.                         // Another useful thing about determinants is that their absolute value is two times the face of the triangle, formed by the three given points.
    304.                         return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
    305.                 }
    306.         }
    307. }
    复制代码

    下面的是自定义的接触监听器:

    1. package {
    2.         import Box2D.Dynamics.*;
    3.         import Box2D.Collision.*;
    4.         import Box2D.Dynamics.Contacts.*;
    5.         import Box2D.Common.Math.*;
    6.         class CustomContactListener extends b2ContactListener {
    7.                 override public function PreSolve(contact:b2Contact, oldManifold:b2Manifold):void {
    8.                         var nameA:String=contact.GetFixtureA().GetBody().GetUserData().assetName.toString();
    9.                         var nameB:String=contact.GetFixtureB().GetBody().GetUserData().assetName.toString();
    10.                         if ((nameA=="wood" && nameB=="sphere")||(nameA=="sphere" && nameB=="wood")) {
    11.                                 if (nameA=="sphere") {
    12.                                         contact.GetFixtureA().GetBody().GetUserData().collided=true
    13.                                 }
    14.                                 else {
    15.                                         contact.GetFixtureB().GetBody().GetUserData().collided=true
    16.                                 }
    17.                         }
    18.                 }
    19.         }
    20. }
    复制代码


    我希望这会给你设计自己的游戏带来一些灵感。源码下载

  • 相关阅读:
    解题:POI 2006 Periods of Words
    解题:NOI 2014 动物园
    1483. 最高平均分
    1438. 较大分组的位置(回顾)
    1258. 漂亮子数组
    1903. 部门统计(回顾)
    1509. 柠檬水找零
    1451. 到最近的人的最大距离
    1425. 比较含退格的字符串
    1394. 山羊拉丁文
  • 原文地址:https://www.cnblogs.com/sode/p/2728008.html
Copyright © 2020-2023  润新知