FilterData让Box2D碰撞分类进行
上一节课,我们学会了用b2Body的isSensor属性制作一个Box2D感应区域。当刚体的isSensor属性为true是,不会参与物理碰撞模拟,实际上,我们可以用Box2D的另一个特性实现相同的效果,它就是今天我们要学的FilterData类。
Filter是”过滤”的意思,所以FilterData可以简单的理解成用来过滤碰撞刚体(比如接下来哦我么要实现的矩形只与矩形刚体碰撞,圆形只与圆形刚体碰撞)。FilterData有3个属性:groupIndex、categoryBits和maskBits,他们的用法如下:
- groupIndex:表示刚体的分组信息。相同groupIndex属性的刚体属性一个组,groupIndex为正数时,刚体只和同组的刚体发生碰撞。groupIndex为负数时,刚体只和同组之外的刚体进行碰撞。
- categoryBits:表示刚体的分组信息,但不决定要碰撞的分组对象。另外,值得注意的,这个值必须是2的N次方。当然设置成其他值,程序不会报错,但是实际的碰撞分类效果,可能会出现意想不到的差错。
- maskBits:表示刚体要碰撞的那个刚体分组对象。这个值通常是另外一个FilterData对象的categoryBits属性,表示只与该类刚体发生碰撞。如果要对多组刚体进行碰撞,可以设置maskBits为多个categoryBits的加合。如要和categoryBits分别为2和4的刚体组都进行碰撞,可以设置maskBits属性为6。
举个例子,比如,圆形刚体的categoryBits和maskBits分别为2和2,矩形刚体的categoryBits和maskBits分别为4和4。那么圆形与矩形刚体之间不会发生碰撞,只有相同形状刚体之间才会发生碰撞。
FilterData对象可以通过赋值给b2FixtureDef对象filter属性来设置,非常简单吧!
var fixtureRequest:b2FixtureDef = new b2FixtureDef(); fixtureRequest.filter = filter;
在下面的示例中,我没有在文档类中设置b2Fixture.filter属性,而是直接在LDEasyBox.createBod()中添加了一个filter的可选参数,变更后的createBox()方法如下,重点部分已用黄色标示。
public static function createBox( world:b2World, posX:Number, posY:Number, boxWidth:Number, boxHeight:Number, isStatic:Boolean = false, userData:*= null, isSensor:Boolean = false, filter:b2FilterData = null ):b2Body { //1.创建刚体需求b2BodyDef var bodyRequest:b2BodyDef = new b2BodyDef(); bodyRequest.type = isStatic? b2Body.b2_staticBody:b2Body.b2_dynamicBody; bodyRequest.position.Set(posX / pixelPerMeter, posY / pixelPerMeter);//记得米和像素的转换关系 //Ladeng6666是Flash元件库中的一个图片 if ( userData != null) { bodyRequest.userData = userData; //设定上衣的尺寸 bodyRequest.userData.width = boxWidth; bodyRequest.userData.height = boxHeight; //需手动将上衣添加到舞台上 //addChild(bodyRequest.userData); } //2.创建形状 var shapeBox:b2PolygonShape = new b2PolygonShape(); shapeBox.SetAsBox(boxWidth / pixelPerMeter / 2, boxHeight / pixelPerMeter / 2); //2.Box2D世界工厂更具需求创建createBody()生产刚体 var box:b2Body = world.CreateBody(bodyRequest); //3.创建敢提形状需求b2ShapeDef的子类 var fixtureRequest:b2FixtureDef = new b2FixtureDef(); fixtureRequest.density = 3; fixtureRequest.friction = 0.3; fixtureRequest.restitution = 0.2; fixtureRequest.shape = shapeBox; fixtureRequest.isSensor = isSensor; if (filter != null) { fixtureRequest.filter = filter; } //4.b2Body刚体工厂根据需求createShape生产形状 box.CreateFixture(fixtureRequest); return box; }
同样createCircle()方法也做出了相同的修改。
说了这么多,让我们看看示例吧!
[swfobject]625[/swfobject]
完整的代码和注释如下:
package { import Box2D.Dynamics.b2Body; import Box2D.Dynamics.b2FilterData; import Box2D.Dynamics.b2World; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; /** * ... * @author ladeng6666 */ public class FilterDataTest extends Sprite { //创建Box2D世界 private var world:b2World; private var pressedBody:b2Body; //创建更重Filter数据 private var boxFilter:b2FilterData; private var circleFilter:b2FilterData; private var wallFilter:b2FilterData; public function FilterDataTest() { //实例化Box2D世界 world = LDEasyBox2D.createWorld(); addChild(LDEasyBox2D.createDebug(world)); LDEasyBox2D.stage = stage; //实例化刚体Filter对象 createFilter(); //因为要指定包裹墙体的FilterData,所有我修改了createWrapWall方法,把它放到这里了。 createWrapWall(); //创建经过FilterData分类的刚体 createBodies(); //添加事件侦听器 addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler); stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler); addEventListener(Event.ENTER_FRAME, loop); } private function createFilter():void { //创建Box刚体FilterData分类数据,记得只能是2的N次方 boxFilter = new b2FilterData(); //设置刚体分类为2 boxFilter.categoryBits = 2 //设置刚体只与分类为10(即类别2矩形刚体+类别8包裹墙体)的刚体进行碰撞 boxFilter.maskBits = 10; //创建Box刚体FilterData分类数据,记得只能是2的N次方 circleFilter = new b2FilterData(); //设置刚体分类为4 circleFilter.categoryBits = 4; //设置刚体只与分类为12(即类别4圆形刚体+类别8包裹墙体)的刚体进行碰撞 circleFilter.maskBits = 12; //设置包裹墙体的分类为8 wallFilter = new b2FilterData(); wallFilter.categoryBits = 8; } //更新Box2D引擎模拟 private function loop(e:Event):void { LDEasyBox2D.updateWorld(world); } private function createBodies():void { for (var i:int = 0; i < 5; i++) { //随机创建5个矩形刚体,并设置它的碰撞分类为boxFilter,即只与矩形刚体和墙体碰撞 LDEasyBox2D.createBox(world, Math.random() * 400 + 50, 30, 30, 30,false,null,false,boxFilter); //随机创建5个矩形刚体,并设置它的碰撞分类为circleFilter,即只与圆形刚体和墙体碰撞 LDEasyBox2D.createCircle(world, Math.random() * 400 + 50,15,30,false,null,false,circleFilter); } //在舞台中间添加一个静态的长条刚体,设置它的碰撞分类是boxFilter, //这样矩形刚体可以落在这个长条上,而圆形刚体不行 LDEasyBox2D.createBox(world, 275, 200, 350, 20, true, null, false, boxFilter); } private function createWrapWall():void { var w:Number = 550; var h:Number = 400; var wallThick:Number = 20;//in pixels //在创建墙体时,设置它的碰撞分类,让它可以同时与矩形和圆形刚体进行碰撞 LDEasyBox2D.createBox(world, w/2, 0, w, wallThick, true, null, false, wallFilter); LDEasyBox2D.createBox(world, w/2, h, w, wallThick, true, null, false, wallFilter); LDEasyBox2D.createBox(world, 0, h/2, wallThick, h, true, null, false, wallFilter); LDEasyBox2D.createBox(world, w, h/2, wallThick, h, true, null, false, wallFilter); } //鼠标拖动刚体 private function mouseEventHandler(e:MouseEvent):void { if (e.type == MouseEvent.MOUSE_DOWN) { pressedBody = LDEasyBox2D.getBodyAtMouse(world); if (pressedBody != null) { LDEasyBox2D.startDragBody(world, pressedBody); } }else if (e.type == MouseEvent.MOUSE_UP) { LDEasyBox2D.stopDragBody(world); } } } }
联系作者
又在期待着下一个教程了…
.createBox怎么会有9个参数?你定义的只有8个参数,public static function createBox(world:b2World,posX:Number,posY:Number,boxWidth:Number,boxHeight:Number,isStatic:Boolean=false,userData:*=null):b2Body
上面有了,没看到,不好意思。
谢谢你的关注!希望你能继续支持我的教程!
groupIndex单独使用
categoryBits和maskBits配合使用,
感觉如何三个参数一起混合使用是不就混乱了。