1

I want to make a game that takes place in space, using Box2D for the physics. One of the things I want the main character you control be able to do, is walk upside down on the ceiling, or sideways on the walls. I want the walls and/or ceilings to be able to be at any angle too, so no matter the shape of the room he'll always be able to stick to it.

While I want the character to be able to walk all over of the terrain, I still want all of the enemies, items, objects, etc to have normal gravity applied to them, pushing them down towards the ground.

Would this be possible in Box2D? If so, what would be the best way to go about getting this to work?

CharlesB
  • 86,532
  • 28
  • 194
  • 218
krej
  • 468
  • 1
  • 6
  • 18

2 Answers2

1

This is entirely possible in Box2d, but requires some work to get it going.

A game I am working on, called Space Spiders Must Die!, required "space spiders" to be able to walk on Astroids, jumping between them.

The first thing you will need are waypoints. These are the points, either on the body you want to move around, or in your case, right next to your walls, that you want your character to move along. Having the points be "static" (not on a moving body) cuts down the work tremendously.

Suppose you want to move between two points, p0 and p1, over time T. Your time step is dT (fixed). It is tempting to use SetTransform(...) on the body, doing something like:

// 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.
}

HOWEVER, if you do this, I believe you will find that the physics engine no longer interacts with your body for collisions. I believe this is because SetTransform(...) causes the engine to "miss a beat" with regard to collision detection.

So, the solution that I found was to create a b2PrismaticJoint at p0, attached to the body, and pointing in the direction of p1. The maximum length of the joint was set to the distance between the points.

This is the code from the project I did this in:

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

After each update of the physics engine, you need to check how close the moving body is to the next point. When it gets close enough, destroy the existing prismatic joint and create a new one to the next point.

This is part of a larger code base, but the idea should be well represented here. In my case, I stored all the data for each point-point segment in something called PATH_DATA_T structures. There was a list of them which I iterated over. When the spider gets close to the next point, the iterator moves to the next element and works off that. This worked VERY well (see video here).

Was this helpful?

FuzzyBunnySlippers
  • 3,387
  • 2
  • 18
  • 28
0

Gravity is just a constant force applied to the player body in a certain direction, usually down. All you need to do is manage this yourself by canceling the default gravity on the player, and adding your own manually as necessary.

Canceling gravity for a single body is very easy, see here for details: http://www.iforce2d.net/b2dtut/custom-gravity

The bigger question is how you will decide which way the new gravity should go... you could find the closest wall, or maybe your player can control the gravity direction himself, I dunno. Either way once you have decided which direction it will be, just apply the same force used to cancel the default gravity, but in the current gravity direction. You'll probably want to turn the player body around too so he doesn't just get pushed sideways into walls or get stuck with his head against the ceiling :)

iforce2d
  • 8,194
  • 3
  • 29
  • 40