11RIA 闪客社区 - 最赞 Animate Flash 论坛

搜索
查看: 1900|回复: 0
上一主题 下一主题

[2D 物理引擎] 【9RIA—ladeng6666】—【Box2D系列教程 篇外篇】切割Box2D对象(一)

[复制链接] TA的其它主题
发表于 2018-2-6 13:25:00 | 显示全部楼层 |阅读模式

【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
转载:9RIA游戏开发者社区(天地会)
作者:ladeng6666(拉登大叔)
作者博客:http://www.ladeng6666.com/blog/


【Box2D系列教程-导航帖】—拉登大叔出品(总贴)


向你展示了我的video of my work in progress engine后,是时候教你用Box2D ver2.1来实现对象切割了
内容比较多,废话少说,马上开始

绘制激光刀
绘制激光飞船简单,它是成千上万个绘画游戏中的一个古老的话题“按下鼠标、移动鼠标、释放鼠标”。
当玩家按下鼠标时,从起点到当前鼠标位置绘制一条线段,表示激光线,当玩家松开鼠标时,显示最终的激光。
也就是说激光有两个端点:起点和终点。这些点存放在b2Segment变量中。当然变量名没有定性的要求,但是既然Box2D为我定义好了segment,为什么不用呢?
下面是代码:
[Actionscript3] 纯文本查看 复制代码
package {
        import Box2D.Dynamics.*;
        import Box2D.Collision.*;
        import Box2D.Collision.Shapes.*;
        import Box2D.Common.Math.*;
        import flash.display.Sprite;
        import flash.events.MouseEvent;
        public class Main extends Sprite {
                private var worldScale:int=30;
                private var canvas:Sprite;
                private var laserSegment:b2Segment;
                private var drawing:Boolean=false;
                public function Main() {
                        canvas = new Sprite();
                        addChild(canvas);
                        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);
                }
        }
}


我们来看一下用到的变量
worldScale:用来将Box2D中的“米”转换为像素,如果你对Box2D中的米还很陌生的话,请点击这里,在我们这个例子中1米表示30像素
canvas:用来绘制激光的画布
laserSegment:b2Segment类变量用来存储激光的起点和终点。
drawing:表示玩家是否正在绘画的一个布尔值变量
然后建立三个鼠标事件侦听器:一个是鼠标按下、一个是鼠标移动、另一个是鼠标弹起。
鼠标按下时:将drawing设置为true,因为实际我们已经开始绘制了,创建一个新的b2Segment实例,同时将它的p1属性(起点)设置为一个新的b2Vec2变量(可以理解为Flash的point对象),该属性的作用是将当前的鼠标坐标转换为Box2D单位。
当鼠标移动时:首先检查是否正在绘画过程中,然后从起点(laserSegment的p1属性)到鼠标当前位置绘制一个激光(一条细长的红线)。
当鼠标弹起时:首先将drawing设置为false,因为我们已经停止绘制了,将p2属性(终点)设置为一个新的b2Vec2变量,存储转换为Box2D单位的鼠标坐标,这一点跟“鼠标按下”里的处理是一样的。
现在,你可以用鼠标绘制激光了,测试一下:


点击并拖动鼠标绘制激光。
现在我们需要一些用来切割的东西。

添加对象
下面我要创建三个静止的元素:floor、box、circle。这一步中,我们只是添加对象,所有你可能会觉得有点无聊,但是我希望你能注意依稀我创建circle的方法:因为Box2D本身并不只是弧线,所以我用12个端点绘制了一个近似circle的多边形。当然你可以用24或36个端点来实现更好的效果,我用的端点比较少,是出于方便理解的考虑的。在日常的项目工作中,我都是用36个顶点来绘制多边形的。
目前的代码如下:
[Actionscript3] 纯文本查看 复制代码
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 canvas:Sprite;
                private var laserSegment:b2Segment;
                private var drawing:Boolean=false;
                public function Main() {
                        debugDraw();
                        addStuff();
                        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,16);
                        var floorShape:b2PolygonShape = new b2PolygonShape();
                        floorShape.SetAsBox(15,0.5);
                        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=24;
                        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();
                        world.DrawDebugData();
                }
        }
}


代码相当多啊,但是大部分都是用来实现”调试绘制”(第42~51行)和添加多边形(第52~85行)的。你只需要看一下我是如何用一组b2Vec2对象,经过几何运算创建多边形,来实现”circle”效果的就可以了。
到这一步,实现的效果如下:


你仍然可以绘制激光,不过现在我们有了一些用来切割的东西了。

检测切入点
既然激光可以切割对象,那么每个被切割的对象肯定有一个切入点和一个切出点。一定要记得:激光可以一次切割多个对象。
谢天谢地,有了b2World的RayCast方法,这个实现起来就简单多了,我们来看一下RayCast的三个参数:
    laserFired:切割触发的函数
    laserSegment.p1:b2Vec2对象,表示切割开始的点
    laserSegment.p2:b2Vec2对象,表示切割结束的点
laserFired回调函数包含了一个对我们很有用的参数:
    fixture:被激光切割的对象
    point:与激光接触的b2Vect2点(即我们要找的切入点)
    normal:交叉点的单位向量
    fraction:交互部分的激光长度,如果你需要知道交互激光长度占总产度比例的话,这个值会很有用。
基础每次激光切割对象时都会调用laserFired方法。如果你不希望激光继续切割,返回0,那么激光会停止继续检测。返回1,激光会继续切割其他的对象。
代码如下:
[Actionscript3] 纯文本查看 复制代码
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 canvas:Sprite;
                private var laserSegment:b2Segment;
                private var drawing:Boolean=false;
                public function Main() {
                        debugDraw();
                        addStuff();
                        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,16);
                        var floorShape:b2PolygonShape = new b2PolygonShape();
                        floorShape.SetAsBox(15,0.5);
                        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) {
                                world.RayCast(laserFired,laserSegment.p1,laserSegment.p2);
                        }
                        world.DrawDebugData();
                }
                private function laserFired(fixture:b2Fixture,point:b2Vec2,normal:b2Vec2,fraction:Number):Number {
                        drawCircle(point,0xff0000);
                        return 1;
                }
                private function drawCircle(origin:b2Vec2,color:Number):void {
                        canvas.graphics.lineStyle(2,color);
                        canvas.graphics.drawCircle(origin.x*worldScale,origin.y*worldScale,5);
                }
        }
}


现在要为laserFired方法添加功能,绘制一个红色的圆圈,这样我们可以清楚的看到切入点。
结果如下:


绘制一个激光,让它穿过一个或多个对象,产看切入点位置的红色圆圈。现在我们要找到切出点。

检测切出点
因为RayCast方法只是实现激光的发生,而无法模拟激光穿过对象,所以Box2D无法找到切出点的位置。
好的,谢谢您的阅读。
….
等、等、等一下…因为激光是从A到B的一条先,我们可以想象另外一条从B到A的闲。B到A的切入点刚好是A到B的起初点。这不正是我们想要的吗(这个方法只有在激光长度大于物体宽度时才适用)!
新增RayCast方法详见第91行。
代码如下:
[Actionscript3] 纯文本查看 复制代码
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 canvas:Sprite;
                private var laserSegment:b2Segment;
                private var drawing:Boolean=false;
                public function Main() {
                        debugDraw();
                        addStuff();
                        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,16);
                        var floorShape:b2PolygonShape = new b2PolygonShape();
                        floorShape.SetAsBox(15,0.5);
                        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) {
                                world.RayCast(laserFired,laserSegment.p1,laserSegment.p2);
                                world.RayCast(laserFired,laserSegment.p2,laserSegment.p1);
                                laserSegment=null;
                        }
                        world.DrawDebugData();
                }
                private function laserFired(fixture:b2Fixture,point:b2Vec2,normal:b2Vec2,fraction:Number):Number {
                        drawCircle(point,0xff0000);
                        return 1;
                }
                private function drawCircle(origin:b2Vec2,color:Number):void {
                        canvas.graphics.lineStyle(2,color);
                        canvas.graphics.drawCircle(origin.x*worldScale,origin.y*worldScale,5);
                }
        }
}


运行结果如下:



绘制一条激光,让它穿过一个或这个更多的对象,看一下切入点和切出点效果。
以上是本文的全部内容。下次,我会继续讨论更多关于切割对象的内容。


下载:
Laser.zip (3.36 MB, 下载次数: 0)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条

感谢所有支持论坛的朋友:下面展示最新的5位赞助和充值的朋友……更多赞助和充值朋友的信息,请查看:永远的感谢名单

SGlW(66139)、 anghuo(841)、 whdsyes(255)、 longxia(60904)、 囫囵吞澡(58054)

下面展示总排行榜的前3名(T1-T3)和今年排行榜的前3名的朋友(C1-C3)……更多信息,请查看:总排行榜今年排行榜

T1. fhqu1462(969)、 T2. lwlpluto(14232)、 T3. 1367926921(962)  |  C1. anghuo(147)、 C2. fdisker(27945)、 C3. 囫囵吞澡(58054)



快速回复 返回顶部 返回列表