Search code examples
objective-cioscocoa-touchbox2dgame-physics

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


Solution

  • 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