【Box2D】20.Box2D C++ 教程-传感器
https://blog.csdn.net/Const_Gong/article/details/51464593
声明:本教程翻译自:Box2D C++ tutorials-Sensors ,仅供学习参考。
- 传感器(Sensors)
在上一个话题中,我们看到,可以通过一些设置防止两个定制器发生碰撞。它们肆无忌惮的互相穿透,好像压根儿就没有看到对方,即便它们在穿透的过程中停止运动,也会保持着重叠的状态。也许这可能就是你想要的特性,但这么做有一个缺陷:既然互相之间没有碰撞,那么我们也就永远都不会获得BeginContact/EndContact信息。
如果我们让两个实体不发生碰撞,就像上一个话题例子中的船和飞机一样,但是我们依然想知道它们之间什么时候发生重叠。比如说,当一架敌军飞机飞到我方友军船只头顶的时候投下一枚炸弹,或者是其它类似的游戏逻辑。如果简单的关闭船和飞机之间的碰撞特性,那么我们就得不到重叠的响应事件,不过我们还有另外一个办法。
当你创建定制器(fixture)的时候,可以设置成员变量isSensor为true,设置此定制器为’传感器(sensor)’的一员,或者如果定制器已经创建了,在运行过程中,也可以根据需要通过调用SetSensor(bool)方法,将其设置成传感器(sensor)。传感器(sensor)的特点有点类似于把遮罩标志位(maskBits)设置成零-它们之间永远都不会发生碰撞。但是当两个定制器开始或者结束碰撞时,依然会触发相关事件,对BeginContact/EndContact进行方法回调。
所有传感定制器(sensor fixtures)的其它特性依然和普通定制器一样。它们依然可以添加到任何类型的物体上。传感器自身的质量依然会添加到所附加的物体上。记住!你可以为同一个物体同时附加传感器和定制器,依此可以延伸出许多其它方面的效果。这里有几个小想法:
- 可以检测某个实体进入或离开某个活动区域
- 触发开关事件
- 检测玩家脚下的特性
- 某个实体的可见区域
至于代码方面,只需要设置定制器的isSensor属性为true就可以了。呃,如果话题就停留在这儿,似乎这次话题的篇幅有点太短了,:)。让我们接着上一个话题中所做的例子,在testbed框架下做一个简单的范例。还是以从上到下(top-down)竖屏战斗场景为基础,我们会使用传感器的特性来表现我们所看到的实体,我们还会把最近几次话题中所讲到的知识点逐步融入近来-用户数据(User data)、碰撞过滤(Collision filtering)以及回调(Collision callback)。
在此实例中,场景中会有一个友军的船只以及一些敌军的飞机,我们通过改变它们的颜色来检查代码是否在正确的工作。我们也会进一步跟踪每一个友军可以看到的敌军的动向。这样我们就可以在安全范围内高效的查看敌军,这就类似于AI一样,是一个非常实用的功能,类似于其它类型的游戏逻辑。
我的狗身上有只跳骚。哈哈…只是想测试你一下脑子是否还处于清醒,毕竟到现在为止一直在罗列文字,没有代码,没有截图。貌似讲了很多枯燥的东西,让我们来点新鲜的吧。
- 用法示例(Example usage)
既然目前这个场景和上一个话题中所举例子的场景非常相像,那我们就以上一个例子为起点。记住大圈代表船只,小圈代表飞机。这次我们将只创建一艘船作为友军,创建三架飞机作为敌军。既然我们创建了一对新实体,那么我们需要为碰撞过滤创建一些新的标志位来进行分类:
enum _entityCategory {
BOUNDARY = 0x0001, // 边界
FRIENDLY_SHIP = 0x0002, // 友军船只
ENEMY_SHIP = 0x0004, // 敌军船只
FRIENDLY_AIRCRAFT = 0x0008, // 友军飞机
ENEMY_AIRCRAFT = 0x0010, // 敌军飞机
FRIENDLY_TOWER = 0x0020, // 友军灯塔
RADAR_SENSOR = 0x0040, // 雷达传感器
};
像这样在FooTest类型的构造函数中对实体进行设置…
// one friendly ship
Ball* ship = new Ball(m_world, 3, green, FRIENDLY_SHIP, BOUNDARY | FRIENDLY_TOWER );
balls.push_back( ship );
// three enemy aircraft
for (int i = 0; i < 3; i++)
balls.push_back( new Ball(m_world, 1, red, ENEMY_AIRCRAFT, BOUNDARY | RADAR_SENSOR ) );
// a tower entity
Ball* tower = new Ball(m_world, 1, green, FRIENDLY_TOWER, FRIENDLY_SHIP );
tower->m_body->SetType(b2_kinematicBody);
balls.push_back( tower );
创建船只和飞机这几行代码,我想你应该可以看明白了,创建灯塔(tower)这部分代码,除了不想把它设置成动态物体以外,几乎和创建船和飞机是一样的。我们并没有在Ball类型的构造函数中添加参数,而是简单的只对参数做了改变。那么为什么不把灯塔(tower)设置成静态物体的呢?我们完全可以那么做,但是如果真的设置成静态物体,那么灯塔就不能做旋转。记住,我们可是希望它成为一个可旋转的雷达。看起来应该是这个样子

现在让我们创建一些传感器(sensors),紧接着上面的代码,像下面这样,我们为船创建传感器:
// add radar sensor to ship
b2CircleShape circleShape;
circleShape.m_radius = 8;
myFixtureDef.shape = &circleShape;
myFixtureDef.isSensor = true;
myFixtureDef.filter.categoryBits = RADAR_SENSOR;
myFixtureDef.filter.maskBits = ENEMY_AIRCRAFT;// radar only collides with aircraft
ship->m_body->CreateFixture(&myFixtureDef);

灯塔会拥有半圈的传感器,并且其角速度不为零:
// add semicircle radar sensor to tower
float radius = 8;
b2Vec2 vertices[8];
vertices[0].Set(0,0);
for (int i = 0; i < 7; i++) {
float angle = i / 6.0 * 90 * DEGTORAD;
vertices[i+1].Set( radius * cosf(angle), radius * sinf(angle) );
}
polygonShape.Set(vertices, 8);
myFixtureDef.shape = &polygonShape;
tower->m_body->CreateFixture(&myFixtureDef);
// make the tower rotate at 45 degrees per second
tower->m_body->SetAngularVelocity(45 * DEGTORAD);

现在只剩下碰撞回调的实现。记住,我们想把当前友军能够看到的敌军飞机都存储到一个链表当中。那就很显然,我们需要有一个链表,当Box2D告诉我们有碰撞发生的时候,我们可以方便的获取相应的方法来进行操作。
// Ball class member
std::vector<Ball*> visibleEnemies;
// in Ball class
void radarAcquiredEnemy(Ball* enemy) {
visibleEnemies.push_back( enemy );
}
void radarLostEnemy(Ball* enemy) {
visibleEnemies.erase( std::find(visibleEnemies.begin(), visibleEnemies.end(), enemy ) );
}
// in Ball::render
if ( visibleEnemies.size() > 0 )
glColor3f(1,1,0); //yellow
else
glColor3f(m_color.r, m_color.g, m_color.b);
现在感觉’Ball’类型的名字不太恰当了…或许一开始我们把它称为实体可能更好一点 :)
// helper function to figure out if the collision was between
// a radar and an aircraft, and sort out which is which
bool getRadarAndAircraft(b2Contact* contact, Ball*& radarEntity, Ball*& aircraftEntity)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// make sure only one of the fixtures was a sensor
bool sensorA = fixtureA->IsSensor();
bool sensorB = fixtureB->IsSensor();
if ( ! (sensorA ^ sensorB) )
return false;
Ball* entityA = static_cast<Ball*>( fixtureA->GetBody()->GetUserData() );
Ball* entityB = static_cast<Ball*>( fixtureB->GetBody()->GetUserData() );
if ( sensorA ) { // fixtureB must be an enemy aircraft
radarEntity = entityA;
aircraftEntity = entityB;
}
else { // fixtureA must be an enemy aircraft
radarEntity = entityB;
aircraftEntity = entityA;
}
return true;
}
// main collision call back function
class MyContactListener : public b2ContactListener
{
void BeginContact(b2Contact* contact) {
Ball* radarEntity;
Ball* aircraftEntity;
if ( getRadarAndAircraft(contact, radarEntity, aircraftEntity) )
radarEntity->radarAcquiredEnemy( aircraftEntity );
}
void EndContact(b2Contact* contact) {
Ball* radarEntity;
Ball* aircraftEntity;
if ( getRadarAndAircraft(contact, radarEntity, aircraftEntity) )
radarEntity->radarLostEnemy( aircraftEntity );
}
};
马上完工…还剩下一步,我们需要把物理实体的用户数据(user data)设置成物理实体自身。让物理引擎知道我们使用了哪个接触监听器(contact listener):
// in Ball constructor
m_body->SetUserData( this );
// at global scope
MyContactListener myContactListenerInstance;
// in FooTest constructor
m_world->SetContactListener(&myContactListenerInstance);
现在基本算是完工啦,现在只要飞机和雷达传感器发生接触的时候,你会看到船和灯塔会变成黄色。牛b!

作为最后一个小练习,别忘了我们希望能够为每一个友军实体更新其周围可见的敌军链表,以便让我们有效的进行调用。现在我们有了一种实现方式,然后实现自旋。然后在雷达和敌军之间的可见范围内连接一条虚线。
// in Ball::renderAtBodyPosition
b2Vec2 pos = m_body->GetPosition(); //(existing code)
glColor3f(1,1,1);//white
glLineStipple( 1, 0xF0F0 ); //evenly dashed line
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
for (int i = 0; i < visibleEnemies.size(); i++) {
b2Vec2 enemyPosition = visibleEnemies[i]->m_body->GetPosition();
glVertex2f(pos.x, pos.y);
glVertex2f(enemyPosition.x, enemyPosition.y);
}
glEnd();
glDisable(GL_LINE_STIPPLE);

- Box2D C++ 教程-简介
- Box2D C++ 教程-环境设置
- Box2D C++ 教程-Testbed结构
- Box2D C++ 教程-创建测试
- Box2D C++ 教程-物体
- Box2D C++ 教程-定制器
- Box2D C++ 教程-设置世界
- Box2D C++ 教程-力和冲量
- Box2D C++ 教程-自定义重力
- Box2D C++ 教程-匀速运动
- Box2D C++ 教程-旋转到指定角度
- Box2D C++ 教程-跳跃
- Box2D C++ 教程-使用debug Draw
- Box2D C++ 教程-画自己的图像
- Box2D C++ 教程-用户数据
- Box2D C++ 教程-碰撞剖析
- Box2D C++ 教程-源代码
- Box2D C++ 教程-碰撞回调
- Box2D C++ 教程-碰撞过滤
- Box2D C++ 教程-传感器
- Box2D C++ 教程-射线投射
- Box2D C++ 教程-查询 World
- Box2D C++ 教程-安全地移除物体
- Box2D C++ 教程-跳跃问题
- Box2D C++ 教程-幽灵顶点
- Box2D C++ 教程-连接器-概述
- Box2D C++ 教程-连接器-旋转
- Box2D C++ 教程-连接器-平移
- Box2D C++ 教程-开发环境设置(iPhone)