精通Cocos2d-x游戏开发(进阶卷)
上QQ阅读APP看书,第一时间看更新

9.4 Body和Shape

Body表示在物理世界中的一个对象,往往也对应游戏世界中的一个对象,物体具有位置、角度、质量、速度等属性,每个刚体都包含一个形状列表,这意味着一个刚体可以由很多个形状组成,如由两个圆形加一个长方形组成一个哑铃,刚体总共可以分为静态、动态以及动力学3种。

❑ 静态刚体永远不会移动,不管其他刚体以多大的力量推动它,它总是岿然不动,也不会与其他静态刚体碰撞。

❑ 动态刚体表示场景中可以运动的对象,可以和任何刚体发生碰撞。撞到动力学刚体和撞到静态刚体都会被弹开,撞到动态刚体,会互相作用,也就是可能互相弹开,或者其中一个撞飞另外一个。

❑ 动力学刚体可以移动,但不受任何力的影响,动态刚体对其施加的力,或者ApplyForce对其施加的力都无效,和静态刚体不会发生碰撞,动力学刚体之间,也不会发生碰撞,但是动力学刚体的运动,可以对动态刚体产生力的作用

9.4.1 刚体的碰撞

在笔者的测试代码中,一个向上移动的动力学刚体,碰到动态刚体,会将动态刚体往上顶起,碰到其他的动力学刚体,会穿过,而碰到静态刚体,也是直接穿过!动力学刚体并不会和静态刚体发生碰撞!当地面上有个动态刚体时,这时候一个动力学刚体从正上方往下运动,会将动态刚体缓缓压入地面。

由于动力学刚体不受力或冲量的作用,所以只能通过Body->SetLinearVelocity设置线性速度或者关节的马达驱动,来使其移动。如图9-2~图9-4演示了动力学刚体与各种刚体碰撞时的表现。

图9-2 动力学刚体和动力学刚体互相穿透

图9-3 动力学刚体和动态刚体互相碰撞

图9-4 动力学刚体和静态刚体互相穿透

如表9-1总结了各种刚体的碰撞情况,如果读者觉得动力学刚体难以理解,可以想一想在超级玛丽等游戏中可以跳上去的那些来回移动的平台,它可以托着游戏者移动,但却可以不受游戏者的影响。现实生活中的电梯也勉强可以理解为动力学刚体。

表9-1 各种刚体的碰撞情况

在游戏中经常会碰到一些单面墙壁,例如,是男人就上100层这样的游戏中,游戏者从下往上跳时可以穿过平台,而从上往下掉落时却会被平台挡住,这就需要使用到Box2d的碰撞监听了。当监听到碰撞事件时,判断角色运动的方向是向上还是向下,根据判断的结果来决定是否禁用此次碰撞。

9.4.2 创建刚体

要创建一个Body需要传入一个物体的定义,b2BodyDef是一个内容丰富的结构,其默认构造函数如下,一般在填充完刚体描述对象的type和position之后,就会调用World的CreateBody接口来创建一个新的Body。

        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;
        }

刚体的大致结构如图9-5所示,每个刚体都会有其对应的一些形状,在Box2d中通过Fixture来描述这些形状,以及它们的密度、摩擦、弹性等。

图9-5 刚体的组成

使用Body的CreateFixture可以为刚体设置一个新的形状,也可以为刚体设置很多形状,通过Body的SetGravityScale还可以设置刚体的重力缩放,某些物体可以让其不受重力的影响。

物体的摩擦系数是一个0~1之间的数,0表示非常光滑,1表示非常粗糙。物体的弹性系数(恢复系数)也是一个0~1之间的数,0表示没有弹性,1表示没有能量损失的反弹。密度也是一个必须要设置的数据,假设将密度设置为0,物体仍然会往下掉落,但是没有任何重量的物体堆叠起来就会变成如图9-6所示。

图9-6 密度为0的堆箱子

在Box2d里可以创建的形状包含Circle圆形、Edge边界线、Polygon多边形以及Chain链条。多边形是一个闭合的多边形,而Chain是一系列点,可以理解为一个不闭合的多边形。形状主要是用于物理模拟中的碰撞检测,在创建完Body之后,对Body进行设置。下面看一下如何创建多边形。

        b2CircleShape circle;                   //圆形
        circle.m_radius = 1.0f;                 //半径为1

        b2EdgeShape edge;
        edge.Set(b2Vec2(-100.0f, 0.0f),         //创建一条平行于x轴的边
            b2Vec2(100.0f, 0.0f))

        b2PolygonShape polygon;                 //创建一个封闭的多边形
        polygon.SetAsBox(5.0f, 5.0f);           //创建一个长和宽都为10的正方形

        b2ChainShape shape;                     //链形
        b2Vec2* arr = new b2Vec2[5];
        arr[0] = b2Vec2(0.0f, 0.0f);
        arr[1] = b2Vec2(10.0f, 10.0f);
        arr[2] = b2Vec2(20.0f, 0.0f);
        arr[3] = b2Vec2(30.0f, 10.0f);
        arr[4] = b2Vec2(40.0f, 0.0f);           //数组描述了一个’W’形状
        shape.CreateChain(arr, 5);              //传入一系列点来确定形状
        delete[] arr;

在创建闭合多边形的时候,也可以使用Set()函数,像Chain一样,传入一个顶点数组来创建形状,在为Body创建Fixture时,将shape对象的指针赋值给b2FixtureDef对象的shape变量即可。

上面的Edge常用于创建世界的边界,其确实很适合做这个工作,而且做得要比Polygon要好。至于Chain,感觉像是一系列连续的Edge。

SetAsBox是个很好用的接口,可以节省代码,需要注意的一点是,传入的参数是宽和高的一半,函数会以当前位置为中心,创建一个四边形

填充好Shape数据之后,需要把它赋给对应的Fixture,同时为Fixture填充弹性、摩擦力、质量之类的属性。最后调用Body的CreateFixture,绑定到刚体之上。