2

I have multiple sprites with physics bodies. User is moving one sprites at a time. A mouse joint is created to support the movement. Using Cocos2d V2.0

The mouse joint is initialized in the ccTouchesBegan after a QueryAABB(&callback, aabb) reports the fixture. Below the logic between the three methods re Mouse Joint

Within the interface

b2World* world;             
GLESDebugDraw *m_debugDraw; 

b2MouseJoint *mouseJoint;
b2Body* groundBody;              

float _boxHeight;
float _boxWidth;
int sectionW;

b2MouseJoint *m_mouseJoint;

Implementation

I get most of the time a EXC_BAD_ACCESS on the line above within TOUCHES ENDED, xcode displays the following on the thread Thread 1

0 0x0000000
1 b2World::DestroyJoint(b2Joint*)
2 -[matchSprites ccTouchesEnded::withEvent
3 -[NSObject .... more lines reported

At the 0 step above it displays on the right window error: address doesn't contain a section that points to a section in a object file

I have followed the recommendations provided so far with no success, at this point cannot see what is generating the condition, upon testing I am leaning to think that the way I am destroying the objects is causing an issue (i.e. if I disable the code that is destroying objects) there is no such error. Any help will be appreciated.

Full code below

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

    self.isTouchEnabled = YES;

        // Load physics file
        [[GB2ShapeCache sharedShapeCache] addShapesWithFile:@"imagesphysics.plist"];

        // Add number images to cache
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"images.plist"];

        // init physics
        [self initPhysics];

        // Tags for the options are based on 500
        int numberOfOptions = 2;
        CGSize s = [CCDirector sharedDirector].winSize;

        sectionW = s.width / numberOfOptions;

        for (int i = 0; i < 2; i++) { 

            // Add target matching Sprites
            CCSprite *targetSprite = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:@"%d.png",i+1]];
            targetSprite.position = CGPointMake(i * sectionW + (sectionW/2.0)  ,s.height * 0.75); 
            [targetSprite runAction:[CCTintBy actionWithDuration:0.2f red:50 green:50 blue:40]];
            targetSprite.scale = 0.6f;
            [self addChild:targetSprite z:30 tag:i+1];

            // Add source matching physics sprites
            [self addNewSpriteAtPosition:CGPointMake(i * sectionW + (sectionW/2.0)  ,s.height * 0.35) number:i bodyType:b2_dynamicBody];
        }

        [self scheduleUpdate];
    }
    return self;
}

// Add Sprites

-(void) addNewSpriteAtPosition:(CGPoint)p number:(int)number bodyType:(b2BodyType)bodyType
{
    CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);

    CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:@"%d.png",number+1]];
    sprite.scale = 0.6f;

    sprite.position = ccp(p.x,p.y);
    [self addChild:sprite z:35 tag:(number+1)+100];     

    // Define the dynamic body.
    b2BodyDef bodyDef;
    bodyDef.type = bodyType; 
    bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);

    bodyDef.userData = sprite;

    b2Body *body = world->CreateBody(&bodyDef);

    // Load the fixture using the vertices file generated by Physics Editor
    [[GB2ShapeCache sharedShapeCache] addFixturesToBody:body forShapeName:[NSString stringWithFormat:@"%d",number+1] forSprite:sprite];
    [sprite setAnchorPoint:
     [[GB2ShapeCache sharedShapeCache] anchorPointForShape:[NSString stringWithFormat:@"%d",number+1]]];

}

//Update the physics

    -(void) update: (ccTime) dt
    {

        int32 velocityIterations = 8;
        int32 positionIterations = 1;

        CGSize s = [CCDirector sharedDirector].winSize;

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

        // Store objects to be destroyed
        std::vector<b2Body *>toDestroy;

        CCSprite *currentSprite;
        CCSprite *targetSprite;
        int currentTag;

        for (b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {

            if (b->GetUserData() != NULL) {

                CCSprite *obj = (CCSprite*)b->GetUserData();
                obj.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
                obj.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());

                // Calculate the bounding box for this sprite
                _boxHeight = obj.boundingBox.size.height;
                _boxWidth = obj.boundingBox.size.width;

                currentSprite = (CCSprite *)b->GetUserData();
                currentTag = currentSprite.tag;
                targetSprite = (CCSprite *)[self getChildByTag:currentTag - 100];

                // SPECIFIC - matching sprite is tag + 100 of current userdata sprite for b object
                float distX = b->GetPosition().x * PTM_RATIO - targetSprite.position.x;
                float distY = b->GetPosition().y * PTM_RATIO - targetSprite.position.y; 

                if (distX * distX + distY * distY < (_boxWidth * _boxHeight) && b->GetType() == b2_dynamicBody) {
                    // Destroy object later
                    toDestroy.push_back(b);
                }

            } // if b-getuserdata

        }

        // Destroy objects

        std::vector<b2Body *>::iterator pos2;
        for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
            b2Body *body = *pos2;
            if (body->GetUserData() != NULL) {

                // Remove target matching
                CCSprite *sprite = (CCSprite *) body->GetUserData();
                currentTag = currentSprite.tag;
                targetSprite = (CCSprite *)[self getChildByTag:currentTag - 100];
                [self removeChild:sprite cleanup:YES];

                // Remove physics body associated with the Sprite
                world->DestroyBody(body);

    // This line has been commented  then test and the error persist!!!!
                    [self addNewSpriteAtPosition:CGPointMake(targetSprite.position.x  ,s.height * 0.75) number:targetSprite.tag-1 bodyType:b2_staticBody];
                }



        }

    }

//Init physics

-(void) initPhysics
{

    CGSize s = [[CCDirector sharedDirector] winSize];

    b2Vec2 gravity;
    gravity.Set(0.0f, -4.81f);
    world = new b2World(gravity);


    // Do we want to let bodies sleep?
    world->SetAllowSleeping(true);

    world->SetContinuousPhysics(true);

    m_debugDraw = new GLESDebugDraw( PTM_RATIO );
    //world->SetDebugDraw(m_debugDraw);

    uint32 flags = 0;
    flags += b2Draw::e_shapeBit;
            flags += b2Draw::e_jointBit;
            flags += b2Draw::e_aabbBit;
            //flags += b2Draw::e_pairBit;
            //flags += b2Draw::e_centerOfMassBit;
    m_debugDraw->SetFlags(flags);


    // Define the ground body.
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0, 0); // bottom-left corner

    // Call the body factory which allocates memory for the ground body
    // from a pool and creates the ground box shape (also from a pool).
    // The body is also added to the world.

    groundBody = world->CreateBody(&groundBodyDef);

    // Define the ground box shape.
    b2EdgeShape groundBox;

    // bottom

    groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));
    groundBody->CreateFixture(&groundBox,0);

    // top
    groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));
    groundBody->CreateFixture(&groundBox,0);

    // left
    groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0));
    groundBody->CreateFixture(&groundBox,0);

    // right
    groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,0));
    groundBody->CreateFixture(&groundBox,0);
}

//Touches handling

-(void)registerWithTouchDispatcher {

    [[[CCDirector sharedDirector] touchDispatcher] addStandardDelegate:self priority:0];
}

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (mouseJoint != NULL) return;

    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 p = b2Vec2(location.x / PTM_RATIO, location.y / PTM_RATIO);

    // Make a small box.
    b2AABB aabb;
    b2Vec2 d;
    d.Set(0.001f, 0.001f);
    aabb.lowerBound = p - d;
    aabb.upperBound = p + d;

    // Query the world for overlapping shapes.
    QueryCallback callback(p);
    world->QueryAABB(&callback, aabb);

    if (callback.m_fixture)
    {
        b2Body* body = callback.m_fixture->GetBody();
        b2MouseJointDef md;
        md.bodyA = groundBody;
        md.bodyB = body;
        md.target = p;
        md.maxForce = 1500.0f * body->GetMass();

        mouseJoint = nil;
        mouseJoint = (b2MouseJoint*)world->CreateJoint(&md);
        pointer = &mouseJoint;
        NSLog(@"Pointer: %p", *pointer);
        //mouseJoint = (b2MouseJoint*)world->CreateJoint(&md);
        body->SetAwake(true);
    }
    [self ccTouchesMoved:touches withEvent:event];
}

-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (mouseJoint == NULL) return;

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView: [touch view]];
    location = [[CCDirector sharedDirector] convertToGL: location];
    b2Vec2 locationWorld = b2Vec2(location.x / PTM_RATIO, location.y / PTM_RATIO);

    mouseJoint->SetTarget(locationWorld);

}

-(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (mouseJoint) {
        world->DestroyJoint(mouseJoint);  // TODO INVESTIGATE WHY THIS CAUSES A BAD ACCESS ERROR
        mouseJoint = nil;

    }
}

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


    if (mouseJoint) {

        //CCLOG(@"WORLD is nil %d",world != nil);
        //CCLOG(@"MOUSEJOINT is nil %d",mouseJoint != nil);

        NSLog(@"Pointer: %p", *pointer);

        mouseJoint->SetTarget(b2Vec2_zero);
        world->DestroyJoint(mouseJoint);  // TODO INVESTIGATE WHY THIS CAUSES A BAD ACCESS ERROR
        mouseJoint = NULL;
        CCLOG(@"MOUSE JOINT WAS DESTROYED SUCCESSFULLY!!!!!");
    }
}

//Query Callback


    class QueryCallback : public b2QueryCallback
    {
    public:
        QueryCallback(const b2Vec2& point)
        {
            m_point = point;
            m_fixture = NULL;
        }

        bool ReportFixture(b2Fixture* fixture)
        {
            b2Body* body = fixture->GetBody();
            if (body->GetType() == b2_dynamicBody)
            {
                bool inside = fixture->TestPoint(m_point);
                if (inside)
                {
                    m_fixture = fixture;

                    // We are done, terminate the query.
                    return false;
                }
            }

            // Continue the query.
            return true;
        }

        b2Vec2 m_point;
        b2Fixture* m_fixture;
    };

5 Answers5

0

Are you sure it's not just a typo? I see mouseJoint in some places and MouseJoint in others. You test for MouseJoint, but you destroy mouseJoint.

I'll admit, I'm trying to solve the exact same bug with a mouse joint, so I was hoping to find an answer here. All signs point toward a joint that was removed by some other operation before I called DestroyJoint so that a null pointer gets processed, so if MouseJoint is not nil but mouseJoint is then it might explain the error in your case.

Vramin
  • 58
  • 6
0

I was trying to solve the same problem. I had other places in the code that would destroy the mouse joint when the attached sprite/body was going to be destroyed, etc. Turns out that you have to be sure to only manage the mouse joint in the touch events and leave the rest to Box2D.

Is there any other place in your code that might be modifying the mouse joint variable, say in the tick: method?

These are the only places I touch the mouse joint. It seems to map to your use case as well. I'm using LevelHelper event code, so the method signatures look a little different. Doing this right greatly simplified things.

-(void)touchBegin:(LHTouchInfo*)info {
    CGPoint location = [[CCDirector sharedDirector] convertToGL: [info.touch locationInView: [info.touch view]]];
    _lastBrickTouched = info.sprite;
    _mouseJoint = [lh mouseJointForBody: _lastBrickTouched.body touchPoint: location];
}

-(void)touchMoved:(LHTouchInfo*)info{
    CGPoint touchZone = [info.touch locationInView:[info.touch view]];
    touchZone = [[CCDirector sharedDirector] convertToGL:touchZone];
    b2Vec2 p;
    p.Set(touchZone.x/PTM_RATIO, touchZone.y/PTM_RATIO);

    if (_mouseJoint != nil)
    {
        _mouseJoint->SetTarget(p);
    }
}

-(void)touchEnded:(LHTouchInfo*)info {
    if (_mouseJoint != nil) {
        world->DestroyJoint(_mouseJoint);
        _mouseJoint = nil;
    }
}
Vramin
  • 58
  • 6
0

I had the same issue with a current project I'm working on. I would get a program crash (EXC_BAD_ACCESS in touchesEnded:) each time I would attempt to destroy a body that had mouseJoint attached to it.

My touchesEnded: code is similar to above:

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event 
{
    if (mouseJoint != NULL)
    {
        world->DestroyJoint(mouseJoint);
        mouseJoint = NULL;
    }
}

Box2d does destroy joints attached to bodies as they get destroyed. However, I guess Box2d doesn't nullify a joint pointer after destroying it via DestroyJoint?

Anyway, the way I prevented program crashes was that in my body destroy function I first destroyed the mouseJoint (and set it to NULL) before destroying the body. My code for destroying bodies (and first deleting mouseJoint if attached to the body to be deleted) is as follows:

-(void)destroyActor:(ObjectView *)objectView
{
    b2Body* b = (b2Body*)objectView.tag;

    // Destroy mouseJoint if attached to body b

    b2JointEdge* jl = b->GetJointList();

    while (jl)
    {
        b2Joint* j = jl->joint;

        if (j == mouseJoint)
        {
            world->DestroyJoint(j);
            mouseJoint = NULL;
            break;
        }

        jl = jl->next;
    }

    world->DestroyBody(b);
    [objectView removeFromSuperview];
    [objectView release];
}

In my code ObjectView* is simply a subclass of UIImageView I use to handle the actor.

DoctorE
  • 31
  • 5
0

I had the same crash error:

Thread 1, Queue : com.apple.main-thread
error: address doesn't contain a section that points to a section in a object file

And I fixed it by adding -Obj**C flag to the **Other Linker Flags.

Ramis
  • 13,985
  • 7
  • 81
  • 100
0

I'll add some suggestions to check. I'm using box2d in libGDX. Both body and joint must be destroyed by world.destroyBody() and world.destroyJoint() accordingly. The order to destroy is the following:

  1. All joints
  2. All related bodies, not before 1!
  3. When all 1 & 2 destroyed, you may dispose world itself

And another thing to think of is the fact, that in box2d (libGDX's) mouseJoint is like the other joints has 2 bodies attached: bodyA & bodyB. But one of them is fictional - it may be any static body. I use fictional offscreen body. When deleting joints you may accidently remove that body. And all other joints may work well! But you'll indeed get exception when creating new joint, because of using badptr or null body, which is inexceptable. So, you must allways carefully destroy that fictional body. For example, you may add some MouseJointHelper class like:

class MouseJointHelper
{
  Body fictionalBody;

  MouseJoint createJoint(...) //here we create joint, using fictional body
  void destroyJointAndBodies(MouseJoint joint) //destroy joint and bodyA, assuming that bodyB is the same for all (fictional body)
  void dispose() //in destructor method destroy fictional body
};
Deepscorn
  • 822
  • 10
  • 22