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);
			}

		}

	}

}

 源文件下载

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

5 Replies to “FilterData让Box2D碰撞分类进行”

  1. .createBox怎么会有9个参数?你定义的只有8个参数,public static function createBox(world:b2World,posX:Number,posY:Number,boxWidth:Number,boxHeight:Number,isStatic:Boolean=false,userData:*=null):b2Body

  2. groupIndex单独使用

    categoryBits和maskBits配合使用,

    感觉如何三个参数一起混合使用是不就混乱了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注