【Box2D】21.Box2D C++ 教程-射线投射
https://blog.csdn.net/Const_Gong/article/details/51464597
声明:本教程翻译自:Box2D C++ tutorials - Ray casting,仅供学习参考。
射线投射经常用来找出部分物理世界当中已经存在的物件。所谓射线就是一条直线,我们可以通过使用Box2D当中提供的方法来检测是否与某个定制器(fixture)有交点。我们也可以找出与定制器交点的法线。
下面就是上面我提到的方法,如果射线与某个定制器发生碰撞则返回true。这里需要注意的是此方法是b2Fixture类的一个成员,这也就是说首先我们满足一个射线对儿这个基本条件才行。
bool b2Fixture::RayCast(b2RayCastOutput* output, const b2RayCastInput& input);
现在让我们看一下输入、输出参数。直接从源代码中可以看出b2RayCastInput包含了哪些参数:
// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
struct b2RayCastInput
{
b2Vec2 p1, p2;
float32 maxFraction;
};
p1和p2点确定了射线的方向。maxFraction表示了射线检测到交点的距离。下面的图可能能够让人一目了然。maxFraction等于1意味着射线段是从p1点到p2点,在此例子中显然不能与多边形有交点,但是maxFraction等于2就可以。

下面是b2RayCastOutput包括的参数:
// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2
// come from b2RayCastInput.
struct b2RayCastOutput
{
b2Vec2 normal;
float32 fraction;
};
如果射线确实与多边形相交,b2Fixture::RayCast将会返回true,而且我们可以通过这个输出结构体里的fraction参数找出交点段值(详见上图),还有在多边形定制器表面生成的法线:

例子
为了尝试一下这个很方便实用的功能,让我们创建一个场景,场景的条件为失重状态,其中漂浮着一些多边形。现在创建这些你应该很顺手了,接下来我们在四周创建一些墙来代替四方盒子作为边界,为的是看起来更好看。
FooTest() {
// a static body
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody;
myBodyDef.position.Set(0, 0);
b2Body* staticBody = m_world->CreateBody(&myBodyDef);
// shape definition
b2PolygonShape polygonShape;
// fixture definition
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &polygonShape;
// add four walls to the static body
b2Vec2 bl(-20, 0);
b2Vec2 br( 20, 0);
b2Vec2 tl(-20,40);
b2Vec2 tr( 20,40);
polygonShape.SetAsEdge( bl, br ); // ground
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsEdge( tl, tr);// ceiling
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsEdge( bl, tl );// left wall
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsEdge( br, tr );// right wall
staticBody->CreateFixture(&myFixtureDef);
myBodyDef.type = b2_dynamicBody;
myBodyDef.position.Set(0,20);
polygonShape.SetAsBox(2,2);
myFixtureDef.density = 1;
for (int i = 0; i < 5; i++)
m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
// circles
b2CircleShape circleShape;
circleShape.m_radius = 2;
myFixtureDef.shape = &circleShape;
for (int i = 0; i < 5; i++)
m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
// turn gravity off
m_world->SetGravity( b2Vec2(0,0) );
}

现在我们需要有一个射线与这些多边形发生碰撞。让我们在屏幕的中心生成一条四周投射的射线,并且缓慢旋转。我们需要唯一做的就是保持住射线的旋转角度,那么我们就不要为此特别封装一个类了,我们只需要有一个全局变量就OK了。
// at global scope
float currentRayAngle = 0;
// in Step() function
currentRayAngle += 360 / 20.0 / 60.0 * DEGTORAD; // one revolution every 20 seconds
// calculate points of ray
float rayLength = 25; // long enough to hit the walls
b2Vec2 p1( 0, 20 ); // center of scene
b2Vec2 p2 = p1 + rayLength * b2Vec2( sinf(currentRayAngle), cosf(currentRayAngle) );
// draw a line
glColor3f(1,1,1); //white
glBegin(GL_LINES);
glVertex2f( p1.x, p1.y );
glVertex2f( p2.x, p2.y );
glEnd();

现在在场景中你应该能够看到一条旋转的白线。现在我们可以就用RayCast方法来获取离射线最近的多边形,并画出线段的长度。我们将会对每个多边形中的每个定制器进行检测,当然这并不是最有效的办法,但是可以作为一个学习的例子(详见查询world话题)来学习。这样的话,我们就可以了解一下世界里的物件:
// in Step() function, continuing on from section above
// set up input
b2RayCastInput input;
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
// check every fixture of every body to find closest
float closestFraction = 1; // start with end of line as p2
b2Vec2 intersectionNormal(0,0);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if ( ! f->RayCast( &output, input ) )
continue;
if ( output.fraction < closestFraction ) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
b2Vec2 intersectionPoint = p1 + closestFraction * (p2 - p1);
// draw a line
glColor3f(1,1,1); // white
glBegin(GL_LINES);
glVertex2f(p1.x, p1.y);
glVertex2f(intersectionPoint.x, intersectionPoint.y);
glEnd();
// draw a point at the intersection point
glPointSize(5);
glBegin(GL_POINTS);
glVertex2f(intersectionPoint.x, intersectionPoint.y);
glEnd();
你可能注意到现在我们在每个上方画出了两条线…为了清晰起见,删除第一条你就会看到类似下面的画面:

上面所说的就是找出交点。我们也可以在输出结构中使用法线来实现一些有趣的想法,现在我们就可以试试看。首先,让我们把法线长什么样儿简单的画出来:
// draw intersection normal
b2Vec2 normalEnd = intersectionPoint + intersectionNormal;
glColor3f(0,1,0); // green
glBegin(GL_LINES);
glVertex2f(intersectionPoint.x, intersectionPoint.y);
glVertex2f(normalEnd.x, normalEnd.y);
glEnd();

作为最后的压轴戏,我们可以把投射代码放到一个自有方法中,然后每次不断的递归调用直到射线的耗尽。这并不意味着Box2D怎么写都行,我只是觉得这么做代码看起来更整洁而已:)。

代码:
// new function for FooTest class
void drawReflectedRay(b2Vec2 p1, b2Vec2 p2)
{
// set up input
b2RayCastInput input;
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
// check every fixture of every body to find closest
float closestFraction = 1; // start with end of line as p2
b2Vec2 intersectionNormal(0,0);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if (!f->RayCast( &output, input))
continue;
if (output.fraction < closestFraction) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
b2Vec2 intersectionPoint = p1 + closestFraction * (p2 - p1);
// draw this part of the ray
glBegin(GL_LINES);
glVertex2f(p1.x, p1.y);
glVertex2f( intersectionPoint.x, intersectionPoint.y );
glEnd();
if (closestFraction == 1)
return; // ray hit nothing so we can finish here
if (closestFraction == 0)
return;
// still some ray left to reflect
b2Vec2 remainingRay = (p2 - intersectionPoint);
b2Vec2 projectedOntoNormal = b2Dot(remainingRay, intersectionNormal) * intersectionNormal;
b2Vec2 nextp2 = p2 - 2 * projectedOntoNormal;
// recurse
drawReflectedRay(intersectionPoint, nextp2);
}
…然后在Step()方法中添加下面几行代码:
// calculate points of ray
float rayLength = 25;
b2Vec2 p1(0, 20); // center of scene
b2Vec2 p2 = p1 + rayLength * b2Vec2(sinf(currentRayAngle), cosf(currentRayAngle));
glColor3f(1,1,1); // white
drawReflectedRay(p1, p2);
- 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)