# Stopping on a slope in Box2d

I am creating a simple platformer using Box2d. I've implemented a variant of the technique described here.

To make the player character move more 'platformer-like' I want him to stop on (shallow) slopes.

However, the engine I am using does not support angle-joints, so I have trouble restricting the movement of the wheel. I've tried some different alternatives that not worked: Setting the horizontal velocity to 0 every game loop doesn't seem to work, because between calls the wheel catches up speed once again.

Is there another way to fix this?

• You might consider enabling limits for the revolute joint, and setting one or the two of them to the current rotation angle every game loop. – rraallvv Jan 13 '13 at 21:25

One possible solution is as follow:

• Fix the rotation angle for the box

Box2D doesn't have angle joints, but setting the body definition with the property fixedRotation = true allows it to maintain a constant angle of rotation like the angle joint.

• High maximum torque for the motor's joint

The maximum torque for the motor in the revolute joint needs to be relatively high, so that the joint can accelerate or be able to stand the two bodies on the slope. To stop on the slope set the motor speed to zero.

• High friction coefficient for the circle

Also a high friction coefficient for the circle allows it to maintain a good grip on the ground, so that it can start to move without drifting.

To test the suggested technique create a new project, then choose the iOS > cocos2d > cocos2d_box2d template.

After creating the project go to HelloWorldLayer.mm and change the init method like this

-(id) init
{
//...

// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);

//...
}


changes to

-(id) init
{
//...

// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(.5f*screenSize.width/PTM_RATIO,.25f*screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0); groundBox.SetAsEdge(b2Vec2(.5f*screenSize.width/PTM_RATIO,.25f*screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);

//...
}


look for addNewSpriteWithCoords method and replace all its content

-(void) addNewSpriteWithCoords:(CGPoint)p
{
//...
}


changes to

-(void) addNewSpriteWithCoords:(CGPoint)p
{
CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagBatchNode];

//We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
//just randomly picking one of the images
CCSprite *circleSprite = [CCSprite spriteWithBatchNode:batch rect:CGRectMake((rand()%4)*32,32,32,32)];

circleSprite.position = ccp( p.x, p.y);

// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
//bodyDef.fixedRotation = true;

circleBodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
circleBodyDef.userData = circleSprite;
b2Body *circleBody = world->CreateBody(&circleBodyDef);

// Define another box shape for our dynamic body.
b2CircleShape dynamicCircle ;
dynamicCircle.m_radius = .5f;//These are mid points for our 1m box

// Define the dynamic body fixture.
b2FixtureDef circleFixtureDef;
circleFixtureDef.shape = &dynamicCircle;
circleFixtureDef.density = 1.0f;
circleFixtureDef.friction = 4.0f;
circleBody->CreateFixture(&circleFixtureDef);

//We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
//just randomly picking one of the images
CCSprite *boxSprite = [CCSprite spriteWithBatchNode:batch rect:CGRectMake((rand()%4)*32,0,32,32)];

boxSprite.position = ccp( p.x, p.y);

// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.fixedRotation = true;

boxBodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
boxBodyDef.userData = boxSprite;
b2Body *boxBody = world->CreateBody(&boxBodyDef);

// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box

// Define the dynamic body fixture.
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &dynamicBox;
boxFixtureDef.density = 1.0f;
boxFixtureDef.friction = 0.3f;
boxBody->CreateFixture(&boxFixtureDef);

b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.bodyA = boxBody;
revoluteJointDef.localAnchorA = b2Vec2(0, -.5);
revoluteJointDef.bodyB = circleBody;
revoluteJointDef.collideConnected = false;
revoluteJointDef.enableMotor = true;
revoluteJointDef.maxMotorTorque = 200;

world->CreateJoint(&revoluteJointDef);
}


in the tick method add this code just after world->step(…)

-(void) tick: (ccTime) dt
{
//...

world->Step(dt, velocityIterations, positionIterations);

for (b2Joint* j = world->GetJointList(); j; j = j->GetNext())
{
int randomNumber = rand()%40; // choose a random number, ranged between 0 and 39
bool changeMovement = randomNumber == 0;  // change movement only when random number is 0, it could be any other arbitrary number

if (changeMovement)
{
b2RevoluteJoint* rj = (b2RevoluteJoint*) j;

rj->GetBodyB()->SetLinearVelocity(b2Vec2(0,0));

int randomNumber = rand()%3; // take the number 0, 1, or 2 at random

randomNumber = randomNumber-1; // convert the random number to either -1, 0 or +1

float angularSpeedInDegrees = randomNumber*2.f*360.f // multiply the random number by a given angular speed, in this case two revolutions per second

}
}

//...
}


replace the file blocks.png for this file

this is the screen capture

http://youtu.be/10WO9tp5p0M

• Wow... you've certainly put a lot of work in this answer. Altough I am not using Cocos2d I can seem to understand most of what your code does. A few questions tough: What does the "if(!rand()%40))" accomplish? And the " rj->SetMotorSpeed(CC_DEGREES_TO_RADIANS(((rand()%3)-1)*2.f*360.f));" ? – Qqwy Jan 19 '13 at 18:23