用刚体isSensor属性创建感应区域
Box2D是一个强大的2D物理引擎,可以帮我们完美实现2D物理碰撞。前面学习了各种刚体和关节对象,以及它们的检测碰撞,充分证明了Box2D是一个强大而完美的2D物理引擎。
默认情况下,碰撞发生后,Box2D会按照动量守恒定律,自动模拟物理碰撞的反弹或变向运动。但是有时候,根据碰撞后处理方式的不同,碰撞后的动量守恒运动可能并不是我们想要的。比如,当碰撞后改变刚体重力方向,这时候被碰撞的刚体更像是一个感应区域。
如下面的示例,点击舞台空白处,创建矩形刚体,当矩形刚体与大圆发生碰撞后,会变更重力方向,变更后的重力始终指向小圆。
是不是很酷?有点像太空版的愤怒的小鸟的效果吧!哇咔咔!
效果实现的关键部分是,设置刚体的isSensor属性,决定刚体是否参与物理模拟。isSensor是一个boolean值,它的作用是:
- 当isSensor为false时(这也是默认值),在发生碰撞后,由Box2D模拟物理碰撞后的反弹或变向运动。
- 当isSensor是true时,刚体只进行碰撞检测,而不模拟碰撞后的物理运动。此时,我们就可以自定义刚体处理方式了,如示例中的绕小圆运动。
完整的代码和注释如下:
文档类:SensorTest.as
package { import Box2D.Collision.Shapes.b2CircleShape; import Box2D.Common.Math.b2Vec2; import Box2D.Dynamics.b2Body; import Box2D.Dynamics.b2BodyDef; import Box2D.Dynamics.b2FilterData; import Box2D.Dynamics.b2FixtureDef; import Box2D.Dynamics.b2World; import Box2D.Dynamics.Contacts.b2Contact; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; /** * ... * @author ladeng6666 */ public class SensorTest extends Sprite { private var world:b2World; private var sensorBody:b2Body; public function SensorTest() { //创建Box2D世界 world = LDEasyBox2D.createWorld(); addChild(LDEasyBox2D.createDebug(world)); LDEasyBox2D.stage = stage; //创建包裹的舞台四周的墙体 LDEasyBox2D.createWrapWall(world, this); //自定义Box2D碰撞事件侦听器 world.SetContactListener(new MyContactListener()); //创内部的小圆,它不是sensor,可以和刚体模拟物理碰撞 LDEasyBox2D.createCircle(world, 275, 200, 50, true); //创建sensor大圆形刚体,因为LDEasyBox2D中没有设置isSensor刚体,所以我们从零开始创建刚体。还记得怎么创建吗? createSensor(); //添加事件侦听器 stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler); addEventListener(Event.ENTER_FRAME, loop); } //鼠标按下后添加矩形刚体 private function mouseEventHandler(e:MouseEvent):void { //用一个Object对象作为刚体的useData,目的就是给刚体添加一个changeGravity属性 //当矩形刚体与sensor碰撞后,改变它的重力 var body:b2Body = LDEasyBox2D.createBox(world, mouseX, mouseY, 20,20,false,new Object()); body.GetUserData().changeGravity = false; } private function createSensor():void { //创建刚体需求 var bodyRequest:b2BodyDef = new b2BodyDef(); bodyRequest.type = b2Body.b2_staticBody; bodyRequest.position.Set(275/30, 200/30); //创建刚体形状 var circleShape:b2CircleShape = new b2CircleShape(); circleShape.SetRadius(170 / 30); //创建刚体fixuture,并添加形状 var fixtureRequest:b2FixtureDef = new b2FixtureDef(); //****************************************** //这里是本章的重点 fixtureRequest.isSensor = true; //****************************************** fixtureRequest.density = 3; fixtureRequest.friction = 0.3; fixtureRequest.restitution = 0.3; fixtureRequest.shape = circleShape; sensorBody = world.CreateBody(bodyRequest); sensorBody.CreateFixture(fixtureRequest); //这里的beginContactHandler是我在b2Body中自定义的一个函数 //具体请参考我的教程:http://www.ladeng6666.com/blog/index.php/2012/09/16/custom-box2d-contact-handler/ sensorBody.beginContactHanlder = contactEventHandler; } private function contactEventHandler(self:b2Body, contactedBody:b2Body):void { //当碰撞发生后,表示矩形刚体的changeGravity为true,这样在loop函数中可以实时更新它的重力,始终指向小圆 contactedBody.GetUserData().changeGravity = true; } private function loop(e:Event):void { LDEasyBox2D.updateWorld(world); var body:b2Body; for (body = world.GetBodyList(); body; body = body.GetNext()) { if (body.GetUserData() != null) { ///如果跟刚体的GetUserData().changeGravity属性为true,则实时更新它的重力方向 ///始终指向小的圆形刚体 if (body.GetUserData().changeGravity) { //下面是一个简单的向量计算 //1.获得矩形刚体与sensor刚体坐标向量的差 var force:b2Vec2 = sensorBody.GetPosition().Copy(); force.Subtract(body.GetPosition()); //2.单位化该向量,只取得它的方向即可 force.Normalize(); //3.改变该向量的长度,生成新的重力 force.Multiply(10); //重新设置刚体的重力 body.m_customGravity = force; //为了让刚体慢慢坠落到小的圆形刚体上,用Multiply(0.99)逐渐缩小它的速度 var velocity:b2Vec2 = body.GetLinearVelocity(); velocity.Multiply(0.99); } } } } } }
自定义的碰撞事件侦听类MyContactListener.as
package { import Box2D.Dynamics.b2Body; import Box2D.Dynamics.b2ContactListener; import Box2D.Dynamics.Contacts.b2Contact; /** * ... * @author ladeng6666 */ public class MyContactListener extends b2ContactListener { //具体详见我的教程http://www.ladeng6666.com/blog/index.php/2012/09/16/custom-box2d-contact-handler/ public function MyContactListener() { } override public function BeginContact(contact:b2Contact):void { var bodyA:b2Body = contact.GetFixtureA().GetBody(); var bodyB:b2Body = contact.GetFixtureB().GetBody(); if (bodyA.beginContactHanlder != null) bodyA.beginContactWith(bodyB); if (bodyB.beginContactHanlder != null) bodyB.beginContactWith(bodyA); } override public function EndContact(contact:b2Contact):void { var bodyA:b2Body = contact.GetFixtureA().GetBody(); var bodyB:b2Body = contact.GetFixtureB().GetBody(); if (bodyA.endContactHanlder != null) bodyA.endContactWith(bodyB); if (bodyB.endContactHanlder != null) bodyB.endContactWith(bodyA); } } }
在SensorTest类中
第60~63行:将大圆的isSensor属性设置为false,则它不会进行物理碰撞模拟,只侦听碰撞事件。
第73行:自定义SensorBody的碰撞事件处理函数,我在自定义Box2D刚体碰撞处理函数中讲过这个用法。
第76~80行:当碰撞发生后,标示矩形刚体的changeGravity为true,这样在loop函数中可以实时更新它的重力,始终指向小的圆形刚体
第85~103行:实时更新刚体的自定义重力,这里用到了我在Box2D自定义重力中讲过的知识点。其中还用到了很多向量的只是,我在向量基础里曾经讲过,其实并不难,看看基础就能明白。
在MyContactListener类中
我创建了一个自定义的MyContactListener(b2ContactListener的子类),来侦听并处理碰撞事件。具体用法详见我的教程自定义Box2D刚体碰撞处理函数
联系作者
晕,怎么我完全按照你的步骤做却不行的呢??到底哪里出问题了
有问题给我发邮件吧,我的邮箱是ladeng6666@163.com
问题找到了,谢谢哈…
感谢拉登叔,又在这学到东西了。
感谢你一直以来的支持!
This can be a excellent web site post, im pleased I discovered it. Ill be back down the monitor to look at various other blogposts in which
您乃神人也…..对我的毕业设计很有帮助。。
强烈围观!!!!!!!!
源代码的下载链接无效了,能否重新发一个,谢谢!