这几天我一直在纠结这个问题,我迫切需要你的帮助。由于我一直在关注 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
任何帮助将不胜感激。