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

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

[2D 物理引擎] 【9RIA—ladeng6666】—【Box2D系列教程 24】自定义Box2D刚体碰撞处理函数

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

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

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

x

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


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



在谁动了我的刚体——Box2D刚体碰撞中,我们学会了用b2Contact和b2ContactListener来处理发生碰撞的刚体,但是正如我之前所说的,在b2Contact或b2ContactListener中,我们获取的bodyA和bodyB无法知道哪个是游戏主角,哪个是敌人,分不出个青红皂白,所以只能一棒子打死啦。所以今天我们学习在b2Body类中自定义一个碰撞处理函数。

用b2Contact和b2ContactListener来处理发生的碰撞,就免不了下面这样的代码:

[Actionscript3] 纯文本查看 复制代码
                if (contactList != null) {
                //如果发生了碰撞,记录碰撞的双方bodyA和bodyB
                var bodyA:b2Body = contactList.GetFixtureA().GetBody();
                var bodyB:b2Body = contactList.GetFixtureB().GetBody();

                /**
                 * 碰撞的双方有3种可能:
                 * 1.动态刚体与静态地面碰撞
                 * 2.动态刚体与动态刚体碰撞
                 *
                 * 我们需要处理的动态刚体与静态地面碰撞,并移动动态刚体,但是我们不知道哪个是静态地面
                 * 所以只能分别对bodyA和bodyB进行判断
                 */
                //判断bodyA是不是静态地面
                if (bodyA.GetUserData().name == "ground") {
                        //如果是静态地面,然后根据bodyB的类型,进行不同的处理
                        if (bodyB.GetUserData().name == "rect") {
                                bodyB.ApplyImpulse(new b2Vec2(1*bodyB.GetMass()), bodyB.GetWorldCenter());
                        }else {
                                bodyB.ApplyImpulse(new b2Vec2(-1*bodyB.GetMass()), bodyB.GetWorldCenter());
                        }
                //判断bodyA是不是静态地面
                //试着删除elseif里面的内容,看看不同的结果(矩形刚体将不会向右移动)
                }else if (bodyB.GetUserData().name == "ground") {
                        //判断bodyA是不是静态地面
                        if (bodyA.GetUserData().name == "rect") {
                                bodyA.ApplyImpulse(new b2Vec2(1*bodyA.GetMass()), bodyA.GetWorldCenter());
                        }else {
                                bodyA.ApplyImpulse(new b2Vec2(-1*bodyA.GetMass()), bodyA.GetWorldCenter());
                        }
                }


具体我在谁动了我的刚体——Box2D刚体碰撞中已经讲过了,最大的缺点就是要反复进行多次判断。

原因是因为我们分不出bodyA和bodyB具体是哪个对象,不过刚体知道他们自己是谁,我们可以在b2Body里定义一个碰撞处理函数,如contactWith(),而这个函数中可以用this应用刚体自己,另个可以用参数传入,然后让对碰撞刚体分别调用bodyA.contactWith(bodyB),bodyB.contactWith(bodyA)就可以轻松分出青红皂白啦。

好吧,我再讲细一点。首先在b2Body中声明一个beginContactHanlder公共变量,我们可以在外部自定义这个函数。

[Actionscript3] 纯文本查看 复制代码
public var beginContactHanlder:Function;


然后再定义一个beginContactWith公共函数,在碰撞发生时,我们可以在b2Contact或b2ContactListener处理函数中直接用bodyA.contactWith(bodyB),bodyB.contactWith(bodyA)来实现碰撞处理。
[Actionscript3] 纯文本查看 复制代码
        public function beginContactWith(bodyB:b2Body):void {
                //通过this来引用刚体自己
                beginContactHanlder(this, bodyB);
        }


最后在Main类中自定义了beginContactHanlder函数。注意,这个函数一定要包含两个参数,分别表示刚体自己,和与它发生碰撞的刚体。
[Actionscript3] 纯文本查看 复制代码
                groundBody.beginContactHanlder = contactHanlder;

                private function contactHanlder(selfBody:b2Body, contactedBody:b2Body):void {
                        //如果被碰撞的刚体是矩形刚体rect
                        if (contactedBody.GetUserData().name == "rect") {
                                //则添加一个向右的速度
                                contactedBody.ApplyImpulse(new b2Vec2(1*contactedBody.GetMass()), contactedBody.GetWorldCenter());
                        }else {
                                //则添加一个向左的速度
                                contactedBody.ApplyImpulse(new b2Vec2(-1*contactedBody.GetMass()), contactedBody.GetWorldCenter());
                        }
                }


这样我们可以只对需要侦听碰撞事件的刚体设置碰撞函数,并进行碰撞处理。如谁动了我的刚体——Box2D刚体碰撞中的例子,我们只需要在刚体与地面刚体发生碰撞时让他们分别往右或左移动,所以只要对地面刚体设置beginContactHanlder函数就可以啦。
同样的效果:



完整的代码和注释如下:
[Actionscript3] 纯文本查看 复制代码
package
{
        import Box2D.Dynamics.Contacts.b2Contact;
        import flash.display.MovieClip;
        import flash.events.MouseEvent;

        import Box2D.Common.Math.b2Vec2;
        import Box2D.Dynamics.b2Body;
        import Box2D.Dynamics.b2World;
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.KeyboardEvent;
        import flash.ui.Keyboard;

        /**
         * [url]http://www.ladeng6666.com[/url]
         * @author ladeng6666
         */
        public class CustomContactFunction extends Sprite
        {
                //创建世界的基本元素
                private var world:b2World;
                private var debugSprite:Sprite;

                private var contactList:b2Contact;

                private var age:Number = 30;
                private var interval:Number = 0;

                public function CustomContactFunction()
                {
                        LDEasyBox2D.stage = this;
                        world=LDEasyBox2D.createWorld();
                        debugSprite=LDEasyBox2D.createDebug(world);
                        addChild(debugSprite);

                        //创建刚体数据,通过个对象的.name属性,可以判断对象的类型,是矩形?是圆形?还是静态地面
                        var bd:BodyData = new BodyData();
                        bd.name = "ground";
                        //创建矩形地面刚体
                        var groundBody:b2Body=LDEasyBox2D.createBox(world, stage.stageWidth / 2, 300, 400,20, true,bd);
                        //设置地面刚体的碰撞处理函数,
                        groundBody.beginContactHanlder = contactHanlder;
                        //侦听事件
                        addEventListener(Event.ENTER_FRAME, loop);
                }
                //自定义的地面刚体处理函数,记住这个函数必须有两个参数,第一个是碰撞刚体本身slefBody,另外一个是被碰撞的刚体contactedBody
                private function contactHanlder(selfBody:b2Body, contactedBody:b2Body):void {
                        //如果被碰撞的刚体是矩形刚体rect
                        if (contactedBody.GetUserData().name == "rect") {
                                //则添加一个向右的速度
                                contactedBody.ApplyImpulse(new b2Vec2(1*contactedBody.GetMass()), contactedBody.GetWorldCenter());
                        }else {
                                //则添加一个向左的速度
                                contactedBody.ApplyImpulse(new b2Vec2(-1*contactedBody.GetMass()), contactedBody.GetWorldCenter());
                        }
                }
                private function createBodies():void {
                        //没30帧随机创建一个刚体,可能是圆形刚体,也可能是矩形刚体
                        if (++interval > age) {
                                var bd:BodyData = new BodyData();
                                if (Math.random() > 0.5) {
                                        //通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型
                                        bd.name = "rect";
                                        LDEasyBox2D.createBox(world, Math.random() * 300 + 100, 0, 40, 30, false, bd);
                                }else {
                                        //通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型
                                        bd.name = "circle";
                                        LDEasyBox2D.createCircle(world, Math.random() * 300 + 100, 0, 30, false, bd);
                                }
                                interval = 0;
                        }
                }
                private function loop(e:Event):void
                {
                        //更新世界
                        LDEasyBox2D.updateWorld(world);
                        //创建刚体
                        createBodies();
                        //获取world的b2Contact对象
                        contactList = world.GetContactList();
                        if (contactList != null) {
                                //如果发生了碰撞,记录碰撞的双方bodyA和bodyB
                                var bodyA:b2Body = contactList.GetFixtureA().GetBody();
                                var bodyB:b2Body = contactList.GetFixtureB().GetBody();
                                //因为我们还是不知道,到底a是地面,还是b是地面,所以还是要对两个刚体都进行判断
                                //但是跟以前不同的是,因为我们只为地面刚体设置了碰撞处理函数beginContactWith,所以只有它会运行碰撞处理
                                //也就是说,下面的两个语句只会运行一个,或则一个都不运行,因为要么碰撞刚体没有地面,要么就只有一个
                                if (bodyA.beginContactHanlder != null) bodyA.beginContactWith(bodyB);
                                if (bodyB.beginContactHanlder != null) bodyB.beginContactWith(bodyA);

                        }
                        //清除超出屏幕的刚体
                        var body:b2Body = world.GetBodyList();
                        for (; body; body = body.GetNext()) {
                                if (body.GetPosition().y > 400/30) {
                                        world.DestroyBody(body);
                                }
                        }

                }

        }

}

import flash.display.Sprite;

class BodyData extends flash.display.Sprite{

}



下载:
OK 2012-06-30 谁动了我的刚体 改善版.rar (345.88 KB, 下载次数: 1)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐 上一条 /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)



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