跳转至

一、世界(b2World)

物理世界只是一个抽象的概念,可以将其理解成是一个盒子,盒子里面放的是各种个样的数学模型和物理模型(或者说就是N多的数学公式和物理公式),所有的物理模拟都在这个盒子内完成。

物理世界和cocos2d的渲染世界不同,渲染世界由场景、层和精灵等组成,在游戏运行时,渲染世界是可以看见(渲染显示)、可以摸到(绑定触摸事件)真实存在的。而物理世界的一切就跟万物的灵魂一样,看不见也摸不着,都是默默在后台运行的一些数据片段。

万物都是因为混沌初开,世界形成才存在的。同样,要使用物理引擎里面的东西,一切也都要从创建世界开始。代码如下:

    -- 创建世界    local world = b2World(b2Vec2(0, -9.8))        
    -- 允许静止的物体休眠    world:SetAllowSleeping(true)    
    -- 开启连续物理检测,使模拟更加的真实    world:SetContinuousPhysics(true)

创建世界可以通过调用b2World(gravity)函数进行创建,该函数的参数gravity是物理世界的重力加速度(g)。在物理学中,加速度是具有大小和方向的矢量,所以该参数可以使用二维向量来表示,其数据类型是b2Vec2,创建矢量可以直接调用b2Vec2(x, y)函数。

由于物理运算经常伴随着平方、立方、开平方和开立方,甚至是更高次的幂运算,所以其计算量是非常大的,对于性能的消耗也是非常可观。而游戏恰恰又非常强调运行的流畅性,所以很多时候当物体处于禁止状态的时候并不需要实时进行物理运算,这时候就可以将其从物理模拟中暂时的剔除出去,以提高整体的计算效率。调用物理世界对象的SetAllowSleeping(isSleep)方法就可以设置世界内的物体是否在禁止的时候休眠,处于休眠状态的物体将不参与物理运算。

同时,为了物理模拟的更加真实,通常还需要开启物理世界的连续检测。调用物理世界对象的SetContinuousPhysics(bool)方法便可以设置是否开启连续检测。(连续检测会消耗一定的性能

世界非常的大,可以说是无边无际,然而游戏设备的屏幕是固定大小的,游戏的渲染画面也就那么大,所以为了保证物理模拟的物体处于可见的画面中,通常还需要给定一个边缘,用于表示物理模拟的世界大小,所有的物体都添加到这个边界里面。

物理世界里面的东西都可以看成是由刚体组成的,所以世界的边界我们也可以创建一个四边形刚体来表示,关于刚体的创建详见下文。

二、Box2D对刚体描述的定义

b2BodyDef()
{
    // 用户数据
    userData = NULL;
    // 刚体位置
    position.Set(0.0f, 0.0f);
    // 刚体角度
    angle = 0.0f;
    // 刚体线性速度
    linearVelocity.Set(0.0f, 0.0f);
    // 刚体角速度
    angularVelocity = 0.0f;
    // 刚体线性阻尼
    linearDamping = 0.0f;
    // 刚体角度阻尼
    angularDamping = 0.0f;
    // 刚体是否可以进行休眠
    allowSleep = true;
    // 刚体初始状态是否处于唤醒状态
    awake = true;
    // 刚体是否固定旋转角度
    fixedRotation = false;
    // 刚体是否是一个快速移动的物体,为了防止发生击穿想象,开启它会增加处理时间
    bullet = false;
    // 刚体类型
    type = b2_staticBody;
    // 刚体是否处于活跃状态
    active = true;
    // 刚体所受重力加速度影响的倍数
    gravityScale = 1.0f;
}
b2BodyDef是刚体描述的结构体

三、形状(b2Shap)

Box2D内置了以下几种简单形状: - 链条(b2ChainShape) - 圆形(b2CircleShape) - 边线(b2EdgeShape) - 多边形(b2PolygonShape) 除了以上几种之外,还可以借助PhysicsEditor等物理形状编辑器进行描点来创建更加复杂的形状。

四、夹具(b2Fixture)

创建好形状之后,需要将形状和对应的刚体进行绑定,这样刚体才能拥有形状。b2Fixture类就是用于见形状绑定到刚体上的,b2Fixture我们可以将其称为“夹具”或者“材质”。

在创建b2Fixture之前,也需要先创建对应的材质描述对象(b2FixtureDef),设定一些材质信息。材质描述的定义如下:

b2FixtureDef()
{    // 形状    
    shape = NULL;    
    // 用户数据    
    userData = NULL;    
    // 摩擦系数    
    friction = 0.2f;    
    // 恢复系数    
    restitution = 0.0f;    
    // 密度    
    density = 0.0f;   
    // 是否为传感器    
    isSensor = false;
}

材质信息中的形状和用户数据请参考上文,这里就不在赘述了。重点看下以下几个属性:

  • 摩擦系数:用于影响刚体的运动,取值通常在区间[0, 1],当然也可以更大。
  • 恢复系数:或者称之为“弹性系数”,用于刚体碰撞后能量的损失计算。取值通常在区间[0, 1],当然也可以更大。0表示发生非躺下碰撞,1表示发生完全弹性碰撞。
  • 密度:密度通常用于计算刚体的质量,间接的影响刚体的惯性。
  • 是否为传感器:当设置isSensor为true时,刚体发生碰撞的时候并不会发生碰撞响应(反弹),但是会接收到碰撞的信号,所以该属性可以理解为传感器。

五、物理调试

上文说过,物理世界的一切都是看不见的,但是有时候为了方便排错,可以用其它的方法让物理模拟变得可见。

比如:我们创建好了一个刚体,我们想要知道刚体对应到cocos2d渲染世界里面的位置,那么我们可以在cocos2d渲染世界里面创建一个lable标签或者一个sprite精灵,并放到刚体的位置上面,这样我们就等同于是让刚体可见了。而对于边线、圆之类的刚体形状,我们可以使用一些游戏引擎的绘图API在渲染世界内对应的进行绘制,这样形状也可以看到了。很多时候将这些数据进行可视化会帮助游戏开发者更好的进行物理排错。

在bbframework中,可以使用以下代码进行物理的可视化操作:

local debugDraw = GB2DebugDrawLayer:create(world, 32)
self:add(debugDraw, 9999)

GB2DebugDrawLayer这个类专门用于负责物理对象的可视化模拟,调用该类的create(b2World, PTM_RATIO)方法进行构造时,需要传入物理世界对象和cocos2d与Box2D的度量单位比例(像素/米)。然后将GB2DebugDrawLayer的实例对象添加到当前场景的Layer上。

这样我们便可以在渲染世界里面看到物理模拟的效果了。