b2Vec2 gravity; gravity.Set(0.0f, -10.0f); bool doSleep = true; m_world = new b2World(gravity); m_world->SetAllowSleeping(doSleep); m_world->SetContinuousPhysics(true); // Define the ground body. b2BodyDef groundBodyDef; groundBodyDef.position.Set(0, 0); // bottom-left corner // Call the body factory which allocates memory for the ground body // from a pool and creates the ground box shape (also from a pool). // The body is also added to the world. m_groundBody = m_world->CreateBody(&groundBodyDef);
void HelloWorld::tick(ccTime dt) { int velocityIterations = 8; int positionIterations = 1; m_world->Step(dt, velocityIterations, positionIterations); //Iterate over the bodies in the physics world for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { //Synchronize the AtlasSprites position and rotation with the corresponding body CCSprite myActor = (CCSprite)b->GetUserData(); myActor->setPosition( CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y PTM_RATIO) ); myActor->setRotation( -1 CC_RADIANS_TO_DEGREES(b->GetAngle()) ); } } }
推动投射器臂吧! 好的,现在是时候移动这个投射器臂啦!为了完成这个任务我们将会使用鼠标关节(mouse joint)。如果你读了雷的弹球游戏教程你就一定已经知道鼠标关节是什么了。
但你没读过,这里是Ray的定义:
“In Box2D, a mouse joint is used to make a body move toward a specified point.”
那就是我们正需要的,所以,然我们先声明一个鼠标关节变量在类定义中:
又是引用Ray的话:
"When you set up a mouse joint, you have to give it two bodies. The first isn’t used, but
the convention is to use the ground body. The second is the body you want to move”.
当你创建一个鼠标关节,你不得不给他2个物体,第一个实际上并不会被使用,但为了方便就把ground body赋给它吧,第二个是你想移动的物体。
目标就是我们想要用关节来移动物体。我们首先要将触摸点左边转换成cocos2d-x坐标在转换为Box2D坐标。我们只在触摸在投射器臂物体左边时才会创建关节。50像素的偏移让我们可以触摸投射器臂稍微偏右的位置。
最大力参数将决定应用在投射器臂上跟随目标点移动的最大力。就我们而言,我们不得不确保它足够强壮来抵消被应用与旋转关节的马达的扭矩。
In our case we have to make it strong enough to counteract the torque applied by the motor of the revolute joint.
如果你把这个值设置的太小,那么你将不能拉回投射器臂,因为它的扭矩过大。你可以减小我们的转动关节的最大马达扭矩(maxMotorTorque)或增大最大力(maxForce)参数。
为了确定什么值合适我建议你用下鼠标关节的最大力参数和旋转关节的最大马达扭矩参数。减小maxForce到500并试验,你将看到你不能推动投射器臂。那么减小maxMotorTroque到1000你将看到你又可以推动它了。但让我们先完成这些的实现。。
我们现在不得不实现CCTouchesMoved方法,这样鼠标关节会跟随你的触摸:
std::vector<b2Body > m_bullets; int m_currentBullet;
增加一个方法用来生产×××:(头文件中加声明)
void Catapult::createBullets(int count) { m_currentBullet = 0; float pos = 62.0f; if (count > 0) { // delta is the spacing between corns // 62 is the position o the screen where we want the corns to start appearing // 165 is the position on the screen where we want the corns to stop appearing // 30 is the size of the corn float delta = (count > 1)?((165.0f - 62.0f - 30.0f) / (count - 1)):0.0f; for (int i=0; i<count; i++, pos += delta) { // Create the bullet CCSprite *sprite = CCSprite::spriteWithFile("acorn.png"); this->addChild(sprite, 1); b2BodyDef bulletBodyDef; bulletBodyDef.type = b2_dynamicBody; bulletBodyDef.bullet = true; bulletBodyDef.position.Set(pos/PTM_RATIO,(FLOOR_HEIGHT+15.0f)/PTM_RATIO); bulletBodyDef.userData = sprite; b2Body bullet = m_world->CreateBody(&bulletBodyDef); bullet->SetActive(false); b2CircleShape circle; circle.m_radius = 15.0/PTM_RATIO; b2FixtureDef ballShapeDef; ballShapeDef.shape = &circle; ballShapeDef.density = 0.8f; ballShapeDef.restitution = 0.2f; ballShapeDef.friction = 0.99f; bullet->CreateFixture(&ballShapeDef); m_bullets.push_back(bullet); } } }
这个方法的大部分你现在应该很熟悉啦。我们的方法将创建几个×××和在第一个松鼠和投射器身体之间的平均的空隙。
bulletBodyDef的bullet参数可能是你之前没有见过的细节之一。这告诉box2d这个物体是一个高速物体,在模拟期间box2d会小心的模拟它。
box2d的官方文档解释了为什么我们要把这些物体设置为××××××物体:
“Game simulation usually generates a sequence of p_w_picpaths that are played at some frame
rate. This is called discrete simulation. In discrete simulation, rigid bodies can move
by a large amount in one time step. If a physics engine doesn't account for the large
motion, you may see some objects incorrectly pass through each other. This effect is
called tunneling."
“游戏模拟通常是以一定的帧速播放一系列图。这被称为离散模拟。在离散模拟中,刚体可以在一个时间步中移动一大段距离。如果物理引擎没有对高速移动的解决方案,你可能看到某些物体穿越了,被称作隧道效应”。
默认情况下,box2d应用持续碰撞检测(continuous collision detection (CCD))来防止动态物体贯穿静态物体。通过从旧的坐标到新坐标来扫描形状从而完成前述功能。引擎通过扫描和计算冲撞的TOI(time of impact)寻找新的碰撞。物体在第一个TOI移动并在接下来的时间步中停止。
正常情况下,持续碰撞检测并不用于动态物体间。这是为了让性能更加优化。但在某些游戏中你需要动态物体应用持续碰撞检测。例如,你想做个发射高速×××打一堆砖块的游戏。没有持续碰撞检测,×××可能会因为隧道效应贯穿砖块。
接下来我们会创建一个用来将×××粘在投射器上的方法。我们还需要2个额外的类属性,在头文件中增加代码:
// Arm is being released if (m_releasingArm && m_bulletJoint != NULL) { // Check if the arm reached the end so we can return the limits if (m_armJoint->GetJointAngle() <= CC_DEGREES_TO_RADIANS(10)) { m_releasingArm = false; // Destroy joint so the bullet will be free m_world->DestroyJoint(m_bulletJoint); m_bulletJoint = NULL; } }