允许角色在Box2D的墙壁和天花板上行走?

时间:2011-11-02 22:16:06

标签: box2d

我想制作一个在太空中发生的游戏,使用Box2D作为物理学。我希望你控制的主角能够做的事情之一就是倒在天花板上,或者在墙壁上侧身。我希望墙壁和/或天花板能够处于任何角度,所以无论房间的形状如何,他总能坚持下去。

虽然我希望角色能够遍布地形,但我仍然希望所有敌人,物品,物体等都能施加正常的重力,将它们向下推向地面。

Box2D可以实现吗?如果是这样,那么最好的方法是什么呢?

2 个答案:

答案 0 :(得分:1)

这在Box2d中是完全可能的,但需要一些工作才能实现。

我正在研究的一款名为Space Spiders Must Die!的游戏,要求“太空蜘蛛”能够在Astroids上行走,在它们之间跳跃。

您需要的第一件事是航路点。这些是你想要移动的身体上的点,或者在你的情况下,就在你的墙壁旁边,你希望你的角色移动。让点“静止”(不是在移动的身体上)会大大减少工作量。

假设您想要在两个点p0和p1之间移动一段时间T.您的时间步长为dT(固定)。在身体上使用SetTransform(...)很有诱惑力,例如:

// Initialize for new points
int steps = T/dT;
Vec2 toTarget = p1-p0;
toTarget.Normalize();
int stepsTaken = 0;
float32 bodyAngle = atan2f(toTarget.y,toTarget.x)

// Each time step
stepsTaken++;
Vec2 pos = p0 + stepsTaken*dT*toTarget;
body.SetTransform(pos,bodyAngle);
if(stepsTaken >= steps)
{  
   // Set up next set of points.
}

但是,如果你这样做,我相信你会发现物理引擎不再与你的身体发生碰撞。我相信这是因为SetTransform(...)导致引擎在碰撞检测方面“错过了一个节拍”。

所以,我找到的解决方案是在p0处创建一个b2PrismaticJoint,连接到主体,并指向p1的方向。关节的最大长度设置为点之间的距离。

这是我在以下项目中执行此操作的代码:

 void CreateNextMovingJoint(float32 motorStrength = 1.0f)
   {
      b2PrismaticJointDef jointDef;
      // We need to figure out some geometry here.
      _pathIter = _pathDataList.begin();
      PATH_DATA_T& pd = *_pathIter;
      _pathIter++;
      // Anchor
      Vec2 pAnchor = pd.point;
      // End point of the anchor, used for the axis.
      Vec2 pEnd = pd.point + pd.distToNext[_direction]*pd.normalAlongPath[_direction];
      jointDef.bodyA = _bodyMovingOn;
      jointDef.bodyB = _bodyMoving;
      jointDef.localAnchorA = pAnchor;
      jointDef.localAnchorB = Vec2(0.0,0.0);
      jointDef.localAxisA = pEnd-pAnchor;
      jointDef.referenceAngle = 0;

      jointDef.enableMotor = true;
      jointDef.enableLimit = true;
      jointDef.motorSpeed = _maxSpeed;
      jointDef.maxMotorForce = motorStrength*(_maxSpeed/4)*_bodyMoving->GetMass();
      jointDef.lowerTranslation = -0.1*pd.distToNext[_direction];
      jointDef.upperTranslation = 1.1*pd.distToNext[_direction];
      jointDef.collideConnected = true;
      _bodyMoving->GetWorld()->CreateJoint(&jointDef);
   }

每次更新物理引擎后,您需要检查移动体与下一个点的接近程度。当它足够接近时,破坏现有的棱柱关节并在下一点创建一个新关节。

这是更大代码库的一部分,但这个想法应该在这里得到很好的体现。就我而言,我将每个点对点段的所有数据存储在一个名为PATH_DATA_T结构的东西中。有一个我重复过来的列表。当蜘蛛接近下一个点时,迭代器移动到下一个元素并对其进行处理。这非常有效(see video here)。

这有用吗?

答案 1 :(得分:0)

重力只是在某个方向上施加到玩家身体的恒定力,通常是向下。您需要做的就是通过取消播放器的默认重力并根据需要手动添加自己的重力来自行管理。

取消单个身体的重力非常容易,详情请见此处:http://www.iforce2d.net/b2dtut/custom-gravity

更大的问题是你将如何决定新引力应该走哪条路?你可以找到最近的墙,或者你的玩家可以自己控制引力方向,我不知道。无论哪种方式,一旦确定了它的方向,只需应用相同的力来消除默认的重力,但是在当前的重力方向上。你可能也想把玩家的身体转过来,这样他就不会被侧面推到墙上或者被他的头撞在天花板上:)