免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
自己翻译的一篇外文,有很多错误和语句不通顺之处希望大家见谅
日期:2011-9-15
http://www.iforce2d.net/b2dtut/sensors
在上一个主题里面,我们可以看到一些设置可以用来去阻止两个fixtures相撞(互相穿越),它们只是通过彼此好像根本没有看见对方一样尽管我们可以在屏幕上看到它们互相重叠了,这种物理现象也许是我们想要的,然而这里有一点小缺点,因为这两个fixtures根本没有相撞,所以他们就不可能给我们任何接触开始或者接触结束的信息。
如果我们不想让两个刚体相撞会怎么样,就像在上面的主题里面提到的飞船和飞行器,但是我们仍然想在它们重叠的时候得到一些返回信息例如,当敌人的飞船飞过(穿越)己方的飞船时,你能够检测到它并像它投掷炸弹,或者其它类似的游戏主题。仅仅简单的关上在飞船和飞行器之间的碰撞设置,用这个collision filter的一些设置并不能并不能让我们知道这个事件(两个个飞船重叠), 但是这里有其他的方法。
当你创建一个fixtures时,你可以通过更改isSensor member的默认值false为true 来为fixture添加它,或者在fixture创建以后通过调用SetSensor(bool),在模拟的时候改变sensor的值来达到你想要的效果(只有开关两种状态)。sensors的作用就像碰撞设置中的maskbits的值设置为零一样,如果sensor是rrue那么它就不会和任何物体碰撞,但是它们能产生接触开始和接触结束的信息,从而让我们知道,何时它开始或者结束和另外一个fifxture的接触。
sensor fixtures(设定了sensor特征的fixtures)的其他特征 仍然和一个normal fixture一样。sensor fixture可以被添加到任何类型的body,他们仍然有质量并且能影响所有body的质量(附加在sensor fixtures上的)。记住在一个body上你可以有不只一个的fixture在,如此你便可以有一个由solid形状和sensors混合而成的东东,这中方法允许我们做出任何奇妙的东东。
这里有一些提示
1可以发现刚体的进入或者离开一个固定的区域
2做一个事件的扳机(触发,导火索)
3在游戏中对各种角色经过地面进行检测,从而触发一些事件
4为一个刚体添加一个区域,这个区域在这个刚体的检测范围内 如果有另外的物体进入会有反应
就代码的进行程度而言,你使用这个sensor真正要做的是添加isSensor = true这个属性为你的fixtures,但是如果在这里停住会让这个话题太短了,让我们在tesbed代码工程里面为上面提到的最后一例子做一个演示教程。再一次用一个计划里组织严密的战场(在这里我们用一个sensor去表现一个刚体能够检测到的区域来代替这个战场)继续我们的sensors,不用关心最近的几个主题里面的 -user data, collision filtering and callbacks和它之间的关系
在这个演示里面,我们的场景里将会有一个友方的飞船和一些敌方的飞行器,这个飞船会被装备一个圆形雷达当飞行器经过他们的范围的时候去检测它们。我们同样会在指定的位置建造一个友方的雷达塔(和那个飞船的雷达形成对比),和一个相似但是不停旋转的雷达去检测敌人的飞船。
像你策划的那样去设置碰撞的一些属性,所有的刚体都能彼此碰撞,但是这飞行器仅仅碰撞和边界围栏。
当一个己方的刚体检测到敌人的飞行器的时候,我们会改变他的颜色以便于我们能够看到,来确定我们的代码已经正确的运行了,我们将要再进一步并且保持追踪我们发现的那个敌方的飞行器,所以我们便拥有了一个有效率的的更新敌人列表的方法(用每个己方飞船的范围) 这个显然是一个有用的东西去得到一些东西例如人工只能和其它类似的游戏主题。
我的狗狗有跳骚。哈。。。。。。 继续我们的检查
如果你在上面没有代码的文字下仍然还是清醒的,理论已经够了 我们开始代码部分吧
Example usage(简单用法)
由于这个策划和最后一个非常的像,我们用那个做开始,还记得我们有大圆作为飞船,和一些小的圆作为飞行器,这次我们仅仅创建一个飞船作为友方,和三个敌方的飞行器,由于我们我们有一对新的刚体类型,我们需要为我们的collision filtering去添加这些categories flags
1enum _entityCategory {
2BOUNDARY = 0x0001,
3FRIENDLY_SHIP = 0x0002,
4ENEMY_SHIP = 0x0004,
5FRIENDLY_AIRCRAFT = 0x0008,
6ENEMY_AIRCRAFT = 0x0010,
7FRIENDLY_TOWER = 0x0020,
8RADAR_SENSOR = 0x0040,
9};
Set up the entities in FooTest constructor like this... (设置刚体的:)
//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 );
这第一部分已经明白了,这最后一部分几乎几乎就像我们期望的那样,我们我们不想要塔变成一个静态的body。 不如添加一个参数到这个ball class constructor 他只是简单的去警告比如一次性的情况,为什么我们不把这个塔做成静态的body呢?当然能,但是这样的话,他就不能旋转—记住我们想让这个塔变成一个旋转的雷达 你应该看到过一些像这样的 东西
现在让我们制作一些sensors 在上面的代码后面,我们可以想下面这样添加一个sensor到飞船上面
//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);
这个塔会得到一个半独立式的sensor,并且角速度不为零,给他设定了一个扇形的雷达区并且角速度不为零
//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);
现在所有剩下要做的就是碰撞返回的实现。记住我们想要存储友方(己方刚体)当前都能看到的敌人飞行器的的一个信息表格,因为那是明显的我们需要有一个list去存储,并且那个是非常棒的,那是灰常棒的当box2d告诉我们一些改变时、我们去用函数做一些改变在list中。
//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);
I'm really starting to think the name 'Ball' for this class is not appropriate anymore... maybe we should have called it Entity to begin with :)
Now set up a collision callback to tell the friendly entities when their radar sensor begins or ends contact with an enemy aircraft. Since much of the code here tends to get repeated, I have put the repetitive part in a sub function so that the main logic in the callback itself is easier to see.
我真的开始对认为ball这个名字对这个类一点都不适用,从这里开始我们叫他刚体
现在 当他们的sensor对一个敌方的飞行器 开始接触或者结束接触的时候,我们设定一个碰撞返回信息用来告诉友方的刚体。由于这里有太多的代码需要重复书写
我已经把反复的部分在这子函数里面 ,以便于我们主要的话题返回信息 ,更容易去明白
//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 );
}
};
几乎在这里 我们去实现这个系统,我们需要设定userdata 在这物理的body上,去标记我们的刚体 ,并且让这无力引擎知道我们正在接触什么
listener:
//in Ball constructor
m_body->SetUserData( this );
//at global scope
MyContactListener myContactListenerInstance;
//in FooTest constructor
m_world->SetContactListener(&myContactListenerInstance);
现在,如果一切运行正常,你应该看到这飞船和飞行器还有塔,当任何敌方的飞行器正在接触雷达sensor的时候会编程黄色
作为一个小的联系 记住我们想要为每个己方的刚体做一个可以看见所有敌人的list(可以实时更新的)现在我们拥有一个了,所以让我,么用一个线连接他们 我们可以在每个雷达和在他们扫描范围内的刚体之间做一条线
//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);