如何在box2d和cocos2dx中创建像body一样的蛇?

时间:2014-01-03 12:55:53

标签: box2d cocos2d-x

我的工作类似于蛇。我想制作蛇体。

游戏逻辑是:

Snake上下移动。移动应该像真正的蛇运动。

我受到了打击。

如何制作蛇的身体?

任何想法或参考都应该对我有所帮助。

提前致谢。

2 个答案:

答案 0 :(得分:12)

好的,这将是 LONG 答案。

我使用其他项目中的一些代码以及" snake"组合了一个快速示例。部分。您可以找到整个(cocos2d-x)代码库here on github

最容易(也是第一件)要做的就是建造蛇体。从Box2D的角度来看,你可以用一系列段来构建它们,每个段都由一个旋转关节连接起来。

  • 你想从单头开始
  • 然后使用相对于它的偏移进行迭代,从而创建分段 他们排成一列。
  • 在创建每个细分时,使用a将其链接到上一个细分 旋转关节。
  • 当你靠近尾巴时,开始逐渐减小身高。

以下是我们的目标:

enter image description here

这是"粗糙"我用来创建它的代码:

   // Constructor
    MovingEntity(b2World& world,const Vec2& position) :
   Entity(Entity::ET_MISSILE,10),
   _state(ST_IDLE)
   {
      // Create the body.
      b2BodyDef bodyDef;
      bodyDef.position = position;
      bodyDef.type = b2_dynamicBody;
      Body* body = world.CreateBody(&bodyDef);
      assert(body != NULL);
      // Store it in the base.
      Init(body);

      // Now attach fixtures to the body.
      FixtureDef fixtureDef;
      PolygonShape polyShape;
      vector<Vec2> vertices;

      const float32 VERT_SCALE = .5;
      fixtureDef.shape = &polyShape;
      fixtureDef.density = 1.0;
      fixtureDef.friction = 1.0;
      fixtureDef.isSensor = false;

      // Nose
      vertices.clear();
      vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE));
      vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE));
      vertices.push_back(Vec2(8*VERT_SCALE,-0.5*VERT_SCALE));
      vertices.push_back(Vec2(8*VERT_SCALE,0.5*VERT_SCALE));
      polyShape.Set(&vertices[0],vertices.size());
      body->CreateFixture(&fixtureDef);
      body->SetLinearDamping(0.25);
      body->SetAngularDamping(0.25);

      // Main body
      vertices.clear();
      vertices.push_back(Vec2(-4*VERT_SCALE,2*VERT_SCALE));
      vertices.push_back(Vec2(-4*VERT_SCALE,-2*VERT_SCALE));
      vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE));
      vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE));
      polyShape.Set(&vertices[0],vertices.size());
      body->CreateFixture(&fixtureDef);

      // NOW, create several duplicates of the "Main Body" fixture
      // but offset them from the previous one by a fixed amount and
      // overlap them a bit.
      const uint32 SNAKE_SEGMENTS = 4;
      Vec2 offset(-4*VERT_SCALE,0*VERT_SCALE);
      b2Body* pBodyA = body;
      b2Body* pBodyB = NULL;
      b2RevoluteJointDef revJointDef;
      revJointDef.collideConnected = false;

      // Add some "regular segments".
      for(int idx = 0; idx < SNAKE_SEGMENTS; idx++)
      {
         // Create a body for the next segment.
         bodyDef.position = pBodyA->GetPosition() + offset;
         pBodyB = world.CreateBody(&bodyDef);
         _segments.push_back(pBodyB);
         // Add some damping so body parts don't 'flop' around.
         pBodyB->SetLinearDamping(0.25);
         pBodyB->SetAngularDamping(0.25);
         // Offset the vertices for the fixture.
         for(int vidx = 0; vidx < vertices.size(); vidx++)
         {
            vertices[vidx] += offset;
         }
         // and create the fixture.
         polyShape.Set(&vertices[0],vertices.size());
         pBodyB->CreateFixture(&fixtureDef);

         // Create a Revolute Joint at a position half way
         // between the two bodies.
         Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition());
         revJointDef.Initialize(pBodyA, pBodyB, midpoint);
         world.CreateJoint(&revJointDef);
         // Update so the next time through the loop, we are
         // connecting the next body to the one we just
         // created.
         pBodyA = pBodyB;
      }
      // Make the next bunch of segments get "smaller" each time
      // to make a tail.
      for(int idx = 0; idx < SNAKE_SEGMENTS; idx++)
      {
         // Create a body for the next segment.
         bodyDef.position = pBodyA->GetPosition() + offset;
         pBodyB = world.CreateBody(&bodyDef);
         _segments.push_back(pBodyB);
         // Add some damping so body parts don't 'flop' around.
         pBodyB->SetLinearDamping(0.25);
         pBodyB->SetAngularDamping(0.25);
         // Offset the vertices for the fixture.
         for(int vidx = 0; vidx < vertices.size(); vidx++)
         {
            vertices[vidx] += offset;
            vertices[vidx].y *= 0.75;
         }
         // and create the fixture.
         polyShape.Set(&vertices[0],vertices.size());
         pBodyB->CreateFixture(&fixtureDef);

         // Create a Revolute Joint at a position half way
         // between the two bodies.
         Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition());
         revJointDef.Initialize(pBodyA, pBodyB, midpoint);
         world.CreateJoint(&revJointDef);
         // Update so the next time through the loop, we are
         // connecting the next body to the one we just
         // created.
         pBodyA = pBodyB;
      }
      // Give the tail some real "drag" so that it pulls the
      // body straight when it can.
      pBodyB->SetLinearDamping(1.5);
      pBodyB->SetAngularDamping(1.5);

      // Setup Parameters
      SetMaxAngularAcceleration(4*M_PI);
      // As long as this is high, they forces will be strong
      // enough to get the body close to the target position
      // very quickly so the entity does not "circle" the
      // point.
      SetMaxLinearAcceleration(100);
      SetMaxSpeed(10);
      SetMinSeekDistance(1.0);
   }

这将是你的第一部分,一个基本的身体。以下是有关代码的一些注意事项:

  1. 我给身体增加了阻尼,甚至更多的尾巴,所以它可以 拖着走来&#34;拉下来&#34;在其余的链接上。这使得 拖动它时看起来更平滑。
  2. 相邻的身体部分不会碰撞但不相邻的部分会碰撞,所以蛇 可以&#34;击中&#34;本身。您可以选择其他部件是否应该碰撞或 不
  3. 现在,让身体按照你想要的方式移动会有点棘手。我先给你看一张图片(和视频),这样你就可以看到我的位置了。所有这些都在我引用的代码库中,因此如果您愿意,可以调整它。

    首先,这是我将它拖了一下之后蛇的样子的截图。 enter image description here

    我拍了一些视频,你可以看到它碰撞,放慢速度等等。(see it here)。

    当你看到它的动态时,它仍然不是完美,但我觉得它看起来相当不错,只需几个小时的工作。

    为了让蛇移动,我采取了&#34;拖动&#34;它的头部周围。当我拖动它时,头部朝我的手指旋转(或者你可以使它遵循代码中的路径,追逐某些东西等)并将其头部朝向目标。身体的其他部分&#34;拖拉&#34;沿着,给它一个&#34; ok&#34;看动作。

    控制器使用两种不同的机制来移动身体:

    使用搜寻行为的身体方向

    身体使用&#34;寻找&#34;行为,就像这样:

    下面是MovingEntity类的ApplyThrust(...)方法的代码。

       void ApplyThrust()
       {
          // Get the distance to the target.
          Vec2 toTarget = GetTargetPos() - GetBody()->GetWorldCenter();
          toTarget.Normalize();
          Vec2 desiredVel = GetMaxSpeed()*toTarget;
          Vec2 currentVel = GetBody()->GetLinearVelocity();
          Vec2 thrust = desiredVel - currentVel;
          GetBody()->ApplyForceToCenter(GetMaxLinearAcceleration()*thrust);
       }
    

    此方法应用推力使b2Body朝目标方向移动。它具有最大速度(GetMaxSpeed())和最大线性加速度(GetMaxLinearAcceleration())作为类的属性。

    如果您按照代码并绘制矢量,您将看到它的作用是施加推力以驱动您的速度,使其指向目标的位置。

    另一种观察方式:它在(在矢量中)的反馈循环中起到保持速度与所需速度匹配的作用。如果你只是考虑标量,就会更容易看到。

    如果您以5米/秒的速度向右移动(currentVel)并且您的最大速度(desiredVel)为6米/秒,则推力将向右推动,向右推动更快(所需尺寸 - 当前尺寸= +1)。也就是说,你会加速到右边。

    如果您以7米/秒的速度向右移动(currentVel)并且您的最大速度(desiredVel)为6米/秒,则推力将为负,指向左侧,使您减速(desiredVel - currentVel = -1)。

    从物理的更新到更新,这会让你的身体以你想要的速度向目标方向移动。

    在你的情况下,你只想左右移动,让重力拉下你的身体。所有这一切都应该在物理学的背景下很好地发挥作用。您可以通过控制线性加速度来控制代理加速/减速的速度。

    使用PID控制器的身体旋转

    这有点复杂(好像前一部分不是)。

    这个想法是根据你想要去的角度和你的身体所面对的角度之间的差异对身体施加扭矩。 PID控制器使用当前角度差(乘以 比例 常数),与最近历史记录的差异( 积分 ),以及角度差异的变化率( 衍生 )。前两个让身体转动,最后一个让它在达到目标角度时减速。

    You can see more details on the theory and implementation here.

    所有这些都封装在代码中的一个类中,称为PIDController。

    注意:代码库中的PIDController是泛型。它对box2d,物理或它的用途一无所知。它就像一个排序算法......它只关心它被输入的数据以及你设置的参数如何工作。它可以轻松地在其他环境中使用......并且有大约100年的时间。

    希望这能让你朝着正确的方向前进。这可能有点矫枉过正,但我​​有点挖掘这些东西,所以很有趣。

答案 1 :(得分:0)

您可以开始将蛇建模为n对齐bodies(我猜circle shapes会获得最佳效果),然后使用distance joint按顺序附加它们。

假设相机与经典蛇游戏相同,您还应将世界gravity设置为0, 0