Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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

boxboy, example of the technique I used

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?

share|improve this question
1  
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
add comment

1 Answer

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)];
    [batch addChild:circleSprite];

    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)];
    [batch addChild:boxSprite];

    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;
    revoluteJointDef.motorSpeed = CC_DEGREES_TO_RADIANS(0);

    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 

            rj->SetMotorSpeed(CC_DEGREES_TO_RADIANS(angularSpeed));
        }
    }

    //...
}

replace the file blocks.png for this file

enter image description here

this is the screen capture

http://youtu.be/10WO9tp5p0M

share|improve this answer
    
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
    
@Qqwy added some comments, and made the code more readable. –  rraallvv Jan 20 '13 at 0:23
    
Hmm... after trying out your code in my environment I've come to the conclusion that the reason the objects are not rolling down the slope is because their density and gravity-scale is much lower than my player.(The other solution: fixed angle is not supported in my environment) Basically this means that I have to choose between fast jumping/falling and stopping on slopes... hmm... Also, it's still impossible to run up slopes this way. But that's all something for another question. I consider this question 'solved' and you will earn your Bounty. Thanks a lot for your help. –  Qqwy Jan 20 '13 at 11:43
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.