Box2D C++教程18-碰撞过滤器

Box2D C++教程18-碰撞过滤器

本文出自https://shuwoom.com博客,欢迎访问!

备注:由于本人最近在学习box2d引擎,而中文资料中好的文章比较少,我就在google上找了一些英文资料。于是,发现了网上的Box2Dtutorial系列文章,觉得写得挺好的,于是做了一些翻译和大家分享分享。由于这是我第一次翻译技术型文章,翻译不当的地方还请各位多理解。

下面我给出原文网址:http://www.iforce2d.net/b2dtut/user-data,本系列翻译文章仅用于学习交流之用,请勿用于商业用途。欢迎各位转载!

 

碰撞过滤器

到目前为止我们所创作的场景,所有的fixtures都可以和其他的fixtures发生碰撞。这是默认的行为,但是我们还可以设置“碰撞过滤器”来提供更好的碰撞控制。碰撞过滤器是在创建fixture时设置一些标志。这些标志是:

         .categoryBits

         .maskBits

         .groupIndex

每一个’bits’值都是一个16位整数,所以你可以有16个不同类别的碰撞。There is a little more to it than that though,因为这些值的组合可以决定两个Fixtures是否会碰撞。Group index用来覆盖category/mask来设置一组给定的fixtures。

 

Category and maskbits

这个categoryBits标志可以认为fixture在说“我是……….”,maskBits就想是在说“我将会和…发生碰撞”。重要的一点是:这些情况下两个fixtures都得满足,这是为了碰撞被许可。

 

例如,你有两个categories、猫和老鼠。猫可能会说“我是猫,我将会和其他的猫和老鼠碰撞”,但老鼠就不会很愿意和猫碰撞,可能会说到“我是老鼠,我将会和其他的老鼠碰撞”。根据这些规则,一对猫会碰撞,并且一对老鼠也将会碰撞,但是老鼠和猫的组合就不会碰撞(即使猫对此不在意)。特别说明的是,程序是通过一个位和这两个标志来检查的,这点能帮助我们理解下面的代码:

bool collide=

         (filterA.maskBits& filterB.categoryBits)!=0&&

         (filterA.categoryBits& filterB.maskBits)!=0;

categoryBits默认的值是0x0001,maskBits默认的值是0xFFFF,或者用另外一种说法就是,每一个fixture都会说“我是一个物体,我会和所有其他的物体碰撞”,同时,由于所有的fixtures都有相同的规则,我们发现所有的fixture真的是会和其他的物体都发生碰撞。

 

让我们做一个实验,通过改变这些标志,看看他们如何被使用。我们需要一个有许多物体的情景,同时我们想看到所有的物体撞在一起但没有飞出屏幕,所以我想用drawing your ownobjects这一话题的场景,这个场景中有很多的圆形物体在围栏内:

 

到现在,你应该对设置项这样的场景应当很熟悉,我在这里就不在多叙述了。如果你想用自己的场景,关键的一点是这个场景要有很多大小不同并且有颜色的物体。

 

在这个例子中,我们想为每一个实体设置不同的尺寸和颜色,并把颜色作为持续测试的时间。我们在构造函数中已经有一个半径的参数,因此添加必要的代码来设置颜色参数,并且在渲染时去使用它。我们也会在每一个实体中添加categoryBits和maskBits参数:

//Ball class member variable
  b2Color m_color;
  
  //edit Ball constructor
  Ball(b2World* world, float radius, b2Color color, uint16 categoryBits, uint16 maskBits) {
    m_color = color;  
    ...
    myFixtureDef.filter.categoryBits = categoryBits;
    myFixtureDef.filter.maskBits = maskBits;
  
  //in Ball::render
  glColor3f(m_color.r, m_color.g, m_color.b);

 

好吧,那我们用尺寸和颜色来做什么呢?一个自上而下有许多不同categories的车辆的战斗场景能很好地解释这一点,这个场景中,只有一部分物体会发生碰撞,例如:地面上的车辆不能和飞行器发生碰撞。让我们创建一个场景,有船、有飞行器,并且每一个实体都有一个友好或敌对状态。我们将使用大小来直观的判断一个实体时船还是飞机,并用颜色来显示它的友好/敌对状态。让我们想场景中添加大量的实体,先让标志为空。

//in FooTest constructor
  b2Color red(1,0,0);
  b2Color green(0,1,0);
  
  //large and green are friendly ships
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 3, green, 0, 0 ) );
  //large and red are enemy ships
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 3, red, 0, 0 ) );
  //small and green are friendly aircraft
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 1, green, 0, 0 ) );
  //small and red are enemy aircraft
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 1, red, 0, 0 ) );

我将会关闭重力来更好地进行观察。你运行的结果应该像下面这样:

 

很明显所有的物体都没有发生碰撞。这是因为你在fixture的maskBits设置为0造成的,它将不会再碰撞。然而,这不是我们想要的效果。让我们来看看如何得到的不是仅仅简单的“全或无”的控制。我们会在可以发生碰撞的物体间设置如下的规则:

1.      所有的车辆可以和边界碰撞

2.      船和飞行器不碰撞

3.      所有的船可以和其他船碰撞

4.      飞行器会和敌对的飞行器碰撞,而不会和同伴的飞行器碰撞

 

这看起来相当的复杂,如果我们从每一个category下手,那么就不会这么的复杂了。首先让我们给每一个category定义一些位标志:

  enum _entityCategory {
    BOUNDARY =          0x0001,
    FRIENDLY_SHIP =     0x0002,
    ENEMY_SHIP =        0x0004,
    FRIENDLY_AIRCRAFT = 0x0008,
    ENEMY_AIRCRAFT =    0x0010,
  };

由于fixture默认的category是1,这样的安排意味着我们不需要做特别的边界fixture。至于其他的,可以考虑每一种车辆的类型,如“我是….、我和…碰撞”:

实体

我是….

(categoryBits)

我和…碰撞

(maskBits)

友好的船

FRIENDLY_SHIP

BOUNDARY |FRIENDLY_SHIP | ENEMY_SHIP

敌对的船

ENEMY_SHIP

BOUNDARY |FRIENDLY_SHIP | ENEMY_SHIP

友好的飞行器

FRIENDLY_AIRCRAFT

BOUNDARY |ENEMY_AIRCRAFT

敌对的飞行器

ENEMY_AIRCRAFT

BOUNDARY |FRIENDLY_AIRCRAFT

 

所以,我们可以再创建实体时使用上面的数据来设置合适的参数。

//large and green are friendly ships
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 3, green, FRIENDLY_SHIP, BOUNDARY | FRIENDLY_SHIP | ENEMY_SHIP ) );
  //large and red are enemy ships
  for (int i = 0; i < 3; i++)
    balls.push_back( new Ball(m_world, 3, red, ENEMY_SHIP, BOUNDARY | FRIENDLY_SHIP | ENEMY_SHIP ) );
  //small and green are friendly aircraft
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 1, green, FRIENDLY_AIRCRAFT, BOUNDARY | ENEMY_AIRCRAFT ) );
  //small and red are enemy aircraft
  for (int i = 0; i < 3; i++) 
    balls.push_back( new Ball(m_world, 1, red, ENEMY_AIRCRAFT, BOUNDARY | FRIENDLY_AIRCRAFT ) );

现在再次运行程序,你会发现上面的规则都被很好地履行了。

 

使用group indexes

 

Fixture的groupIndex标志能够用来覆盖上面的category和mask设置。顾名思义,它能够将fixture放在一起,要么它们总是可以碰撞,要么永远不能碰撞。groupIndex作为一个有符号的整数,而不是bitflag。这里是它的工作原理----慢慢地阅读,一开始会有些难理解。当检查两个fixture来看看它们是否应该碰撞:

         .如果它们中的一个groupIndex是0,使用category/mask规则同上

         .如果两个的groupIndex都是非零但不相同,使用category/mask规则同上

         .如果两个的groupIndex相同且都为正,碰撞

         .如果两个的groupIndex相同且为负,不碰撞

groupIndex的默认值为0,所以目前为止它什么也没有参与。让我们做一个简单的例子,覆盖上面的category/mask设置。我们会将边界墙和一辆车放在同一组内,并且将group值设为负值。如果你仔细留意了上面的解释,你会发现这将让一辆车偷偷摸摸地逃离边界围栏。

 

你可以再Ball类的构造函数中添加一个groupIndex参数,或者用一个快捷的方法:

//in FooTest constructor, before creating walls
  myFixtureDef.filter.groupIndex = -1;//negative, will cause no collision
  
  //(hacky!) in global scope
  bool addedGroupIndex = false;
  
  //(hacky!) in Ball constructor, before creating fixture
  if ( !addedGroupIndex )
    myFixtureDef.filter.groupIndex = -1;//negative, same as boundary wall groupIndex
  addedGroupIndex = true;//only add one vehicle to the special group

如果我们将groupIndex设置为正值,这个汽车总是会和墙碰撞。在这种情况下,所有的汽车都和墙碰撞了,所以这没有太大的区别,这就是我们为什么将它设置为负,因此我们就可以看到要发生的事。在其他的情况下,这可能会相反,例如船和飞行器永远都不会发生碰撞,你可能会说一部分的飞行器会和船碰撞。如低空飞行的水上飞机,或许…

 

需要更多的控制

如果你需要更加具体地控制什么和什么可以碰撞,你可以在世界里设置一个碰撞过滤回调以便Box2D在需要检查两个fixtures是否应该碰撞,而不是使用上面的规则给出两个fixtures并让你去决定。回调的使用方法和debug draw以及碰撞回调的使用一样,通过继承b2ContactFilter,实现线面的函数,让引擎通过调用世界的SetContactFilter函数回去信息。

bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB);

 

在运行时改变碰撞过滤器

有时候你可能想根据游戏中具体的事件来改变碰撞过滤器。你可以在fixture中创建新的b2Filter来改变他们的categoryBits、maskBits和groupIndex。通常你只想改变他们中的一个,所以这很方便获取现有的过滤器,改变你想要修改的数值,并把它放回去。如果你有一个指向fixture的引用,这回变得更容易:

//get the existing filter
  b2Filter filter = fixture->GetFilterData();
  
  //change whatever you need to, eg.
  filter.categoryBits = ...;
  filter.maskBits = ...;
  filter.groupIndex = ...;
  
  //and set it back
  fixture->SetFilterData(filter);

如果你只有只想物体的引用,你需要遍历物体的Fixture来找到你想要的fixture。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值