Cocos2d-x之Box2D物理引擎

1、概述

我们在游戏中,有时需要模拟真实的物理世界,让游戏世界中的物体都按照物理世界的规律进行运动。而要实现这种功能,就需要用到物理引擎。
在cocos2d-x中集成了2个物理引擎,一个是Chipmunk,一个是Box2D,Box2D的使用人数更多,文档,资料更加完成,所以我们一般选择Box2D。
cocos2d-x 3.0之后的版本对Box2D进行了封装,但是我们学习的时候还是最好了解着更加全面一点。

2、基本概念

Box2D中有几个常见的概念。

世界(world)

世界代表一个遵循物理规律的空间,所有的物体都在世界中运动,世界具有创建销毁刚体,创建销毁关节等功能。

刚体(body)

一块非常坚硬的物质,它上面的任何两点之间的距离都是完全不变的。刚体具体划分为静态刚体,动态刚体和菱柱刚体。

形状(shape)

一块严格依附于物体(body)的2D碰撞几何结构(collision geometry)。形状通过关联附加到刚体上,这样刚体就具备了视觉上的外形。

关节(joint)

它是一种用于把两个或者多个物体固定到一起的约束。Box2D支持的关节类型有:旋转,菱柱,距离等等。关节支持限制(limits)和马达(motors)。

关节限制(joint limit)

一个关节限制限定了一个关节的运动范围。例如人类的胳膊肘只能做某一范围角度的运动。

关节马达(joint motor)

一个关节马达能依照关节的自由度来驱动所连接的物体。例如,你可以使用一个马达来驱动一个肘的旋转。

3、世界

世界代表了一个遵守某些物理定律的空间。

3.1 世界的创建

创建世界需要两个步骤,首先生成重力向量,然后根据重力向量生成世界。

    //重力参数,向量
    b2Vec2 gravity;
    //设置水平方向和垂直方向上的加速度
    gravity.Set(0.0f, -10.0f);
    //创建世界
    world = new b2World(gravity);
    //是否允许物体休眠,如果允许休眠,可以提高物理世界的处理效率,只有在发生碰撞时才唤醒对象,节省CPU
    world->SetAllowSleeping(true);
    //是否开始连续碰撞,计算机智能把一段连续的时间分成许多离散的时间点,在对每个时间点之间的行为进行机选,如果时间点分割得不够细致,速度较快的两个物体碰撞时就可能产生碰撞,连续碰撞将启用特殊的算法避免该现象。
    world->SetContinuousPhysics(true);

最后程序结束的时候,别忘了销毁世界哟。

    //销毁世界
    delete world;

3.2 世界的使用

3.2.1 模拟

世界类用于驱动模拟。你需要指定一个时间步和一个迭代次数。例如:

    float timeStep = 1/60.0f;
    int32 velocityIterations = 10;//速度迭代次数
    int32 positioniterations = 1;//位置迭代次数
    world->Step(timeStep, velocityIterations, positioniterations);

Box2D使用一个称为积分器(integrator)的算法。在离散的时间点上模拟物理方程。
Box2D还使用了大量的代码来调用约束求解器(constraint solver)。约束求解器可以解决约束问题。单个约束可以被完美的解决。但是有的时候,我们求解一个约束的同时会影响到另一个约束。为了解决所有问题,我们需要多次遍历所有约束。
约束求解器中有两个阶段:速度阶段和位置阶段。在速度阶段,求解器为使物体能够正确的移动需要计算必要的冲量。在位置阶段,求解器调整物体的位置来降低物体之间的重叠和连接的分离程度。
Box2D建议的迭代次数是速度阶段8次,位置阶段3次。我们可以根据自身的喜好改变这个数字。用更少的迭代次数会提高效率,但会影响精度。同样,用更多的迭代次数会降低效率但会提升模拟质量。

3.2.2 扫描世界

世界就是物体和关节的容器。我们可以获取世界中所有物体和关节,并遍历他们。

    for (b2Body* a = world->GetBodyList(); a; a->GetNext()) {
        if (a->GetUserData() != nullptr) {
            Sprite* sp = (Sprite* )a->GetUserData();
            sp->setPosition(Vec2(a->GetPosition().x*PTM_PATIO, a->GetPosition().y*PTM_PATIO));
            sp->setRotation(-1*CC_RADIANS_TO_DEGREES(a->GetAngle()));
        }
    }

物体

我们已经知道了刚体的概念,那么该怎么创建一个刚体呢?
创建刚体需要以下几个步骤。
1.创建一个刚体定义bodyDef
2.世界根据刚体定义创建刚体
3.用形状(shape),摩擦(friction),密度(density)等来定义fixture
4.在body上来创建fixture

4.1 创建一个动态刚体

auto sprite = Sprite::create("zh.png");
    sprite->setPosition(Vec2(p.x, p.y));
    addChild(sprite);

    //物体定义
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;//物体类型为动态刚体
    bodyDef.position.Set(p.x/PTM_PATIO, p.y/PTM_PATIO);
    b2Body* body = world->CreateBody(&bodyDef);//创建物体
    body->SetUserData(sprite);//将精灵放置到物体的UserData属性中,这样便于我们从物体中获取相关联的物体
    //定义两米见方的盒子形状
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox(1, 1);//中心点为1,1,盒子左下角为0,0.这样大小可以确定

    //夹具定义
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    //设置密度
    fixtureDef.density = 1.0f;
    //设置摩擦系数
    fixtureDef.friction = 0.4f;
    //设置弹性系数
    fixtureDef.restitution = 0.5f;
    //使用夹具固定形状到物体
    body->CreateFixture(&fixtureDef);

4.2 创建一个静态刚体

上面创建的是一个动态刚体,如果只需要一个静态刚体,可以去掉

    bodyDef.type = b2_dynamicBody;//物体类型为动态刚体

默认情况下,body 是静态的。同时,密度,摩擦系数,弹性系数也没有意义了。

4.3物体工厂

物体的创建和摧毁都是通过世界来完成的。世界创建物体,摧毁物体,完成内存的分配和回收。物体有动态和静态的。静态的物理没有质量,动态的物体有质量。
无论物体是静态还是动态的。摧毁方式都是一样的。

    world->DestroyBody(body);
    body = NULL;

5、形状

5.1 形状概述

形状就是物体上的碰撞几何结构。另外形状也可以用于定义物体的质量。也就是说,我们指定密度,Box2D根据形状可以计算出质量。
形状具有摩擦和恢复的性质。形状用于属于某物体,单个物体可以拥有多个形状。形状是抽象类,所以在Box2D中可以实现许多类型的形状。

5.2 形状定义

形状定义用于创建形状。通用的形状数据会保存在b2Shape中,特殊的形状数据会保存在其派生类中。其派生类有b2PolygonShape,b2EdgeShape,b2CircleShape等,我们可以直接使用意见定义好的形状。

5.2.1 形状常见属性

1)摩擦和恢复
摩擦可以使对象逼真地沿着其他对象滑动。摩擦参数设置在0到1之间。0意味着没有摩擦,1会产生强摩擦。
恢复可以使对象弹起。恢复的值也是0到1之间。0的意思是物体不会弹起,这称为非弹性碰撞。1的意思是物体的速度会得到精确的反射。这称为完全弹性碰撞。恢复是通过这样的公式计算的。
2)筛选
碰撞筛选是为了防止某些形状发生碰撞的系统。Box2D通过种群和组支持了这样的碰撞筛选。
Box2D支持16个种群,对于任何一个形状你都可以指定它属于哪个种群。你还可以指定这个形状可以和其他那些种群发生碰撞。例如,你可以在一个多人游戏中指定玩家之间不会碰撞,怪物之间也不会碰撞,但是玩家和怪物会发生碰撞。这是通过掩码来完成的。
playerShapeDef.filter.categoryBits = 0x0002;
monsterShapeDef.filter.categoryBits = 0x0004;
playerShape.filter.maskBits = 0x0004;
monsterShapeDef.filter.maskBits = 0x0002;
碰撞组可以让你指定一个整数的组索引。你可以让同一个组的所有形状总是相互碰撞(正索引)或者永远不碰撞(负索引)。组索引通常用于一些以某种方式关联的事物。就像汽车的部件。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值