user635064
user635064

Reputation: 6247

Box2D dynamic body getting stuck (iPhone)

I have a ground object and a spear shaped object(dynamic). When a button is pressed, a linear velocity is applied to the spear. It works fine but sometimes, it gets stuck on the ground. This happens most of the time (not always) when the side opposite of spear hide collides straight down with the ground. I am attaching some images so you guys get the picture: enter image description here

Here my code:

#import "HelloWorldLayer.h"

#define PTM_RATIO 32


@implementation HelloWorldLayer

+(CCScene *) scene{
    CCScene *scene = [CCScene node];

    HelloWorldLayer *layer = [HelloWorldLayer node];
    [scene addChild: layer];

    return scene;
}

-(id) init {

    if( (self=[super init])) {

        isSimulating = NO;

        CGSize winSize = [CCDirector sharedDirector].winSize;

        self.isAccelerometerEnabled = YES;
        self.isTouchEnabled = YES;

        // Create sprite and add it to the layer
        _spear = [CCSprite spriteWithFile:@"image_SPEAR.png"];
        _spear.position = ccp(40, 30);

        [self addChild:_spear];


        label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:20];
        CGSize size = [[CCDirector sharedDirector] winSize];
        label.position =  ccp( size.width /2 , size.height/2 );
        [self addChild: label];

        CCMenuItem *starMenuItem = [CCMenuItemImage 
                                    itemFromNormalImage:@"ball.png" selectedImage:@"ball.png" 
                                    target:self selector:@selector(starButtonTapped:)];
        starMenuItem.position = ccp(60, 250);
        CCMenu *starMenu = [CCMenu menuWithItems:starMenuItem, nil];
        starMenu.position = CGPointZero;
        [self addChild:starMenu];


        // Create a world
        b2Vec2 gravity = b2Vec2(0.0f, -20.0f);
        bool doSleep = true;
        _world = new b2World(gravity, doSleep);

        // Create edges around the entire screen
        b2BodyDef groundBodyDef;
        groundBodyDef.position.Set(0,0);
        b2Body *groundBody = _world->CreateBody(&groundBodyDef);
        b2PolygonShape groundBox;
        b2FixtureDef boxShapeDef;
        boxShapeDef.shape = &groundBox;
        groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
        groundBody->CreateFixture(&boxShapeDef);
        groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
        groundBody->CreateFixture(&boxShapeDef);
        groundBox.SetAsEdge(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
        groundBody->CreateFixture(&boxShapeDef);
        groundBox.SetAsEdge(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
        groundBody->CreateFixture(&boxShapeDef);
        boxShapeDef.friction = 0.3f;


        [self setup];

    }
    return self;
}

- (void)setup {

    NSLog(@"Setting up...");
    //set the sprite's initial position
    _spear.position = ccp(40, 30);
    _spear.rotation = 0.0f;


    //row 1, col 1
    int num = 7;
    b2Vec2 verts[] = {
        b2Vec2(-36.0f / PTM_RATIO, -2.7f / PTM_RATIO),
        b2Vec2(20.1f / PTM_RATIO, -2.1f / PTM_RATIO),
        b2Vec2(24.4f / PTM_RATIO, -4.6f / PTM_RATIO),
        b2Vec2(36.7f / PTM_RATIO, -1.4f / PTM_RATIO),
        b2Vec2(23.9f / PTM_RATIO, 2.7f / PTM_RATIO),
        b2Vec2(20.7f / PTM_RATIO, 0.0f / PTM_RATIO),
        b2Vec2(-36.0f / PTM_RATIO, -0.5f / PTM_RATIO)
    };






    // Create spear body and shape
    b2BodyDef spearBodyDef;
    spearBodyDef.type = b2_dynamicBody;
    spearBodyDef.position.Set(40.0/PTM_RATIO, 30.0/PTM_RATIO);
    //spearBodyDef.angle =  45.0 * (180.0f/b2_pi);
    spearBodyDef.userData = _spear;
    _spearBody = _world->CreateBody(&spearBodyDef);

    b2PolygonShape spearShape;
    spearShape.Set(verts, num);

    b2FixtureDef spearShapeDef;
    spearShapeDef.shape = &spearShape;
    spearShapeDef.density = 0.75f;
    spearShapeDef.friction = 0.2f;
    spearShapeDef.restitution = 0.2f;
    _spearBody->CreateFixture(&spearShapeDef);

}


- (void)starButtonTapped:(id)sender {

    if (isSimulating) {
        NSLog(@"Not simulating now...");

        [self unschedule:@selector(tick:)];
        _world->DestroyBody(_spearBody);
        _spearBody = NULL;
        [self setup];
    } else {
        NSLog(@"Simulating now...");
        [self schedule:@selector(tick:)];

        float angle = _spearBody->GetAngle();

        b2Vec2 force;
        force.Set(cos(angle) * 15.0f , sin(angle) * 15.0f);
        _spearBody->SetLinearVelocity(force);
    }
    isSimulating = !isSimulating;

}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    if (!isSimulating) {

        UITouch* touch = [touches anyObject];
        CGPoint location = [touch locationInView: [touch view]];
        location = [[CCDirector sharedDirector] convertToGL: location];

        float angleRadians = atanf((float)location.y / (float)location.x);
        float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);

        _spear.rotation = -1 * angleDegrees;
        _spearBody->SetTransform(_spearBody->GetPosition(), angleRadians);

        [label setString:[NSString stringWithFormat:@"Angle: %f    X: %f    Y:%f", angleDegrees, location.x, location.y]];
        NSLog(@"%@", @"touched");

    }

}



- (void)tick:(ccTime) dt {
    _world->Step(dt, 10, 10);
    for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {    
        if (b->GetUserData() != NULL) {
            CCSprite *ballData = (CCSprite *)b->GetUserData();
            ballData.position = ccp(b->GetPosition().x * PTM_RATIO,
                                    b->GetPosition().y * PTM_RATIO);
            ballData.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
        }        
    }
}

- (void) dealloc {
    delete _world;
    _world = NULL;

    [super dealloc];
}
@end

I am guessing it has to do with friction and restitution but I have tried a lot of values and nothing makes this behavior go away. Thanks.

**UPDATE:**I figured out what's causing this. Everytime it gets stuck, it's when the spear goes outside of groundBody. Everytime. But, why would it go outside of groundBody in the first place? Here's an image showing this. At the bottom, the spear is outside of body: enter image description here

Upvotes: 1

Views: 2123

Answers (4)

tom
tom

Reputation: 23

note that in the latest version of Cocos2D, you need to use the following to prevent that sticking behaviour:

_world->SetAllowSleeping(false);

Upvotes: 2

Andrew
Andrew

Reputation: 24866

Actually box2D has an iterative solver. So the quality of the simulation directly depends on iterations quantity. Try increasing position and/or velocity iterations, when making Step. Also check you are using fixed time step (usually it gives better results). And also you your spear moves rapidly try enabling continuous physics and setting your spear body as bullet (b2BodyDef property).

Upvotes: 1

Skyler Saleh
Skyler Saleh

Reputation: 3991

Try turning off sleeping...

bool doSleep = false;
_world = new b2World(gravity, doSleep);

I had a similar problem in one of my box2d based games and this "feature" is what caused it.

Upvotes: 3

iforce2d
iforce2d

Reputation: 8272

Your spear shape is not convex. I highly recommend you use Box2D's debug draw feature to check things every now and then. http://www.iforce2d.net/b2dtut/debug-draw

Here is what the polygon really comes out like - the lines are what you specified, the shaded area is what you got:

enter image description here

Upvotes: 1

Related Questions