0

这几天我一直在纠结这个问题,我迫切需要你的帮助。由于我一直在关注 Ray Wenderlich 的教程,当我尝试从 ContactListener 中的 b2Body 获取用户数据时,我仍然收到 EXC_BAD_ACCESS 错误,因此WHContactListener.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"


class WHContactListener : public b2ContactListener
{
private:
    void BeginContact(b2Contact* contact);
    void EndContact(b2Contact* contact);
};

WHContactListener.mm

#import "WHContactListener.h"
#import "cocos2d.h"
#import "ExSprite.h"

void WHContactListener::BeginContact(b2Contact *contact)
{


    b2Body *bodyA = contact->GetFixtureA()->GetBody();
    b2Body *bodyB = contact->GetFixtureB()->GetBody();

    /*The problem occurs here, while it returns a non-null value it just crashes when I implement any method here, such as NSLog */

    ExSprite *spriteB = (ExSprite*)bodyB->GetUserData();
    NSLog(@"output %@", spriteB);

}

void WHContactListener::EndContact(b2Contact *contact)
{

}

精灵类ExSprite.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"

@interface ExSprite : CCSprite {

    BOOL _exploded;
    b2Body *_explBody;
}

@property (nonatomic, readwrite) BOOL exploded;
@property (nonatomic, assign) b2Body *explBody;

-(void)setPhysicsBody:(b2Body*)body;

@end

ExSprite.mm

#import "ExSprite.h"
#import "HelloWorldLayer.h"

#pragma mark - ExSprite
@implementation ExSprite

@synthesize exploded = _exploded;
@synthesize explBody = _explBody;

-(void)setPhysicsBody:(b2Body *)body
{
    _explBody = body;
    _explBody->SetUserData(self);
}

// this method will only get called if the sprite is batched.
// return YES if the physics values (angles, position ) changed
// If you return NO, then nodeToParentTransform won't be called.
-(BOOL) dirty
{
    return YES;
}

// returns the transform matrix according the Chipmunk Body values
-(CGAffineTransform) nodeToParentTransform
{   
    b2Vec2 pos  = _explBody->GetPosition();

    float x = pos.x * PTM_RATIO;
    float y = pos.y * PTM_RATIO;

    if ( ignoreAnchorPointForPosition_ ) {
        x += anchorPointInPoints_.x;
        y += anchorPointInPoints_.y;
    }

    // Make matrix
    float radians = _explBody->GetAngle();
    float c = cosf(radians);
    float s = sinf(radians);

    if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
        x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
        y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
    }

    // Rot, Translate Matrix
    transform_ = CGAffineTransformMake( c,  s,
                                       -s,  c,
                                       x,   y );    

    return transform_;
}

-(void) dealloc
{
    // 
    [super dealloc];
}

@end

最后是我使用的层HelloWorld.h

#import <GameKit/GameKit.h>

// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#import "WHContactListener.h"

//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32

// HelloWorldLayer
@interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
    CCTexture2D *spriteTexture_;    // weak ref
    b2World* world;                 // strong ref
    GLESDebugDraw *m_debugDraw;     // strong ref
    WHContactListener* contactListener;
    CCRenderTexture *target;
    CCSprite *brush;

    b2Body *groundBody;
    NSMutableArray *shapeVert;
}

@property (nonatomic, readwrite)b2Body *groundBody;
@property (nonatomic, assign)NSMutableArray *shapeVert;

// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;

@end

HelloWorld.mm

#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#import "PhysicsSprite.h"
#import "ExSprite.h"

enum {
    kTagParentNode = 1,
};


#pragma mark - HelloWorldLayer

@interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;

@end

@implementation HelloWorldLayer

@synthesize groundBody = groundBody_;
@synthesize shapeVert = shapeVert_;

+(CCScene *) scene
{
    // 'scene' is an autorelease object.
    CCScene *scene = [CCScene node];

    // 'layer' is an autorelease object.
    HelloWorldLayer *layer = [HelloWorldLayer node];

    // add layer as a child to scene
    [scene addChild: layer];

    // return the scene
    return scene;
}

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

        // enable events

        self.isTouchEnabled = YES;
        self.isAccelerometerEnabled = YES;
        CGSize s = [CCDirector sharedDirector].winSize;

        // init physics
        [self initPhysics];

        target = [CCRenderTexture renderTextureWithWidth:s.width height:s.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
        [target retain];
        [target setPosition:ccp(s.width/2, s.height/2)];
        [self addChild:target z:0];

        brush = [CCSprite spriteWithFile:@"bird.png"];
        [brush retain];


        [self scheduleUpdate];
    }
    return self;
}

-(void) dealloc
{
    delete world;
    world = NULL;
    delete contactListener;
    contactListener = NULL;
    delete m_debugDraw;
    m_debugDraw = NULL;

    [super dealloc];
}   


-(void) initPhysics
{

    CGSize s = [[CCDirector sharedDirector] winSize];

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


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

    world->SetContinuousPhysics(true);

    contactListener = new WHContactListener();
    world->SetContactListener(contactListener);

    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(s.width/2/PTM_RATIO, s.height/2/PTM_RATIO); // bottom-left corner

    //SET USERDATA

    //groundBodyDef.userData = groundBody;

    // 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;      

    float32 x1 = 2*cosf(0.0f * b2_pi/180);
    float32 y1 = 2*sinf(0.0f * b2_pi/180);    

    for(int32 i=1; i<=18; i++){
        float32 x2 = 2*cosf((i*20) * b2_pi / 180);
        float32 y2 = 2*sinf((i*20) * b2_pi / 180);


        groundBox.Set(b2Vec2(x1,y1), b2Vec2(x2,y2));
        groundBody->CreateFixture(&groundBox, 0);

        x1 = x2;
        y1 = y2;
    }

}

-(void) draw
{
    //
    // IMPORTANT:
    // This is only for debug purposes
    // It is recommend to disable it
    //
    [super draw];

    ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );

    kmGLPushMatrix();

    world->DrawDebugData(); 

    kmGLPopMatrix();
}

-(void) addNewSpriteAtPosition:(CGPoint)p
{
    //CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
    CCNode *parent = [self getChildByTag:kTagParentNode];

    //We have a 64x64 sprite sheet with 4 different 32x32 images.  The following code is
    //just randomly picking one of the images
    int idx = (CCRANDOM_0_1() > .5 ? 0:1);
    int idy = (CCRANDOM_0_1() > .5 ? 0:1);

    //PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)];
    ExSprite *sprite = [ExSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32*idx, 32*idy, 32, 32)];

    [parent addChild:sprite];

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

    // Define the dynamic body.
    //Set up a 1m squared box in the physics world
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
    //USERDATA set
    //bodyDef.userData = sprite;
    b2Body *body = world->CreateBody(&bodyDef);

    // 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 fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.restitution = 0.0f;
    //fixtureDef.density = rand() * 20.0f;
    fixtureDef.friction = 0.3f;
    body->CreateFixture(&fixtureDef);

    [sprite setPhysicsBody:body];

    //NSLog(@"here %@", body->GetUserData());
}

-(void) update: (ccTime) dt
{
    //It is recommended that a fixed time step is used with Box2D for stability
    //of the simulation, however, we are using a variable time step here.
    //You need to make an informed choice, the following URL is useful
    //http://gafferongames.com/game-physics/fix-your-timestep/

    int32 velocityIterations = 8;
    int32 positionIterations = 1;

    // Instruct the world to perform a single step of simulation. It is
    // generally best to keep the time step and iterations fixed.
    world->Step(dt, velocityIterations, positionIterations);    
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    //Add a new body/atlas sprite at the touched location
    for( UITouch *touch in touches ) {
        CGPoint location = [touch locationInView: [touch view]];

        location = [[CCDirector sharedDirector] convertToGL: location];

        [self addNewSpriteAtPosition: location];
    }
}

-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    /*UITouch *touch = (UITouch*)touches.anyObject;
    CGPoint start = [touch locationInView:[touch view]];
    start = [[CCDirector sharedDirector] convertToGL:(start)];
    CGPoint end = [touch previousLocationInView:[touch view]];
    end = [[CCDirector sharedDirector] convertToGL:end];
    [target begin];

    float distance = ccpDistance(start, end);

    for(int i =0; i < distance; i++){
        float difx = end.x - start.x;
        float dify = end.y - start.y;
        float delta = (float)i / distance;
        [brush setPosition:ccp(start.x + (difx*delta), start.y + (dify * delta))];

        [brush visit];
    }

    [target end];
    */
}

#pragma mark GameKit delegate

-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
    AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
    [[app navController] dismissModalViewControllerAnimated:YES];
}

-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
    AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
    [[app navController] dismissModalViewControllerAnimated:YES];
}

@end

任何帮助将不胜感激。

4

1 回答 1

0

我发现了问题。似乎我没有声明 CCNode,当我将带有 UserData 的主体附加到不可用的 CCNode 时,当我在 contactlistener 类中检索它时它会崩溃

于 2012-11-26T08:51:07.010 回答