Box2D - b2body GetUserData始终返回null

时间:2011-02-21 21:27:20

标签: objective-c box2d

我试图根据box2d中b2body的精灵来调整精灵的位置和旋转。

在我创建了body后,我将userData属性设置为保存精灵和位置等的body对象的属性。问题是在tick方法中b-> GetUserData永远不会检索我放在那里的对象。你能看到以下任何问题吗?

这是我的添加播放器方法:

-(void) addPlayerShip
{
    CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:@"PlayerShip.png" capacity:1];

    //Get sprite sheet
    CCSprite *sprite = [CCSprite spriteWithSpriteSheet:sheet rect:CGRectMake(0,0,64,64)];
    [sheet addChild:sprite];

    CGSize screenSize = [CCDirector sharedDirector].winSize;

    sprite.position = ccp( screenSize.width/2, screenSize.height/2);

    [self addChild:sheet];

    // Define the dynamic body.
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y /PTM_RATIO);
    bodyDef.angularVelocity=0;

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

    b2PolygonShape dynamicTriangle;

    dynamicTriangle.m_vertexCount=3;
    dynamicTriangle.m_vertices[0].Set(0,-1);
    dynamicTriangle.m_vertices[1].Set(1,1);
    dynamicTriangle.m_vertices[2].Set(-1,1);

    // Define the dynamic body fixture.
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicTriangle;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.0f;
    fixtureDef.restitution=0.0f;

    b2Fixture *fixture = body->CreateFixture(&fixtureDef);

    b2Vec2 *vector = new b2Vec2;

    BodyObject *bodyObject = [[BodyObject alloc] initWithBody:body 
                                                   andFixture:fixture 
                                                  andVelocity:vector 
                                                    andSprite:sprite];


    bodyDef.userData = bodyObject;
}

和在b-> GetUserData

上始终返回null的tick方法
-(void) tick: (ccTime) dt
{
    int32 velocityIterations = 8;
    int32 positionIterations = 1;

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

    //Iterate over the bodies in the physics world
    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
    {
        if (b->GetUserData() != NULL) {
            //Synchronize the Sprites position and rotation with the corresponding body
            GameObject *myObject = (GameObject*)b->GetUserData();
            myObject.Sprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
            myObject.Sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
        }   
    }
}

最后是bodyObject

#import "BodyObject.h"


@implementation BodyObject

@synthesize Body;
@synthesize Fixture;

-(id) initWithBody:(b2Body*) body 
        andFixture:(b2Fixture*) fixture 
       andVelocity:(b2Vec2*) velocity 
         andSprite:(CCSprite*) sprite
{
    self = [super initWithSprite:(CCSprite*)sprite 
                     andVelocity:(b2Vec2*)velocity];

    self.Body=body;
    self.Fixture=fixture;

    return self;
}

@end

我对目标c不熟悉所以如果我做了什么重大错误请告诉我!

编辑: 我在这里尝试了另外一些代码,我似乎能够进一步了解。

if I set bodyDef.UserData = sprite; 

在行

之后
bodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y /PTM_RATIO); 

然后GetUserData返回精灵。但是,如果我在addPlayerShip方法结束时将用户数据设置为精灵,则它再次返回null。精灵/身体对象是否被处置?

2 个答案:

答案 0 :(得分:4)

您正在将用户数据分配给bodyDef AFTER 您调用CreateBody(),因此永远不会将其添加到实际正文中。

b2BodyDef是一个结构,用于保存创建主体的设置。一旦调用CreateBody(),对此结构的更改就不起作用。

您通过移动bodyDef.UserData = sprite自己找到了解决方案;调用CreateBody()之前的某处。这是放置它的正确位置。如果您认为在函数结束时由于某种原因需要它(其他变量?),那么您需要重构代码,以便不是这种情况。

我会改变这一行(和BodyObject类):

BodyObject *bodyObject = [[BodyObject alloc] initWithBody:body 
                                               andFixture:fixture 
                                              andVelocity:vector 
                                                andSprite:sprite];

这样您就可以在不需要box2d元素的情况下实例化它(即在它们被定义之前),然后使用属性在创建它们之后分配这些值。这样您就可以将此对象作为正文的用户数据传递,创建正文,然后使用[bodyObject setBody:body];

指定b2Body *

不要忘记确保您的身体和夹具属性只读。

最终代码可能如下所示:

-(void) addPlayerShip
{
    CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:@"PlayerShip.png" capacity:1];

    //Get sprite sheet
    CCSprite *sprite = [CCSprite spriteWithSpriteSheet:sheet rect:CGRectMake(0,0,64,64)];
    [sheet addChild:sprite];

    CGSize screenSize = [CCDirector sharedDirector].winSize;

    sprite.position = ccp( screenSize.width/2, screenSize.height/2);

    [self addChild:sheet];

    b2Vec2 *vector = new b2Vec2;

    BodyObject *bodyObject = [[BodyObject alloc] initWithSprite:sprite 
                                                   andVelocity:vector];

    //MOVES THE BODY DEFINITION AFTER THE BODYOBJECT DEFINITION
    // Define the dynamic body.
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y /PTM_RATIO);
    bodyDef.angularVelocity=0;
    bodyDef.userData = bodyObject;

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

    b2PolygonShape dynamicTriangle;

    dynamicTriangle.m_vertexCount=3;
    dynamicTriangle.m_vertices[0].Set(0,-1);
    dynamicTriangle.m_vertices[1].Set(1,1);
    dynamicTriangle.m_vertices[2].Set(-1,1);

    // Define the dynamic body fixture.
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicTriangle;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.0f;
    fixtureDef.restitution=0.0f;

    b2Fixture *fixture = body->CreateFixture(&fixtureDef);

    [bodyObject setBody:body];
    [bodyObject setFixture:fixture];
}

然后bodyObject类看起来像这样:

#import "BodyObject.h"


@implementation BodyObject

@synthesize Body;
@synthesize Fixture;

-(id) initWithSprite:(CCSprite*) sprite 
       andVelocity:(b2Vec2*) velocity 
{
    self = [super initWithSprite:(CCSprite*)sprite 
                     andVelocity:(b2Vec2*)velocity];

    return self;
}

@end

答案 1 :(得分:1)

如果有人遇到与GetUserData()返回null相同的问题,即使你是SetUserData。 然后不要犯错,假设比b2World.QueryShape()给出回调中的主体。不,它给你Fixtures。你必须 fixture.GetBody()。GetUserData()(我的情况是:javascript中的box2dweb;这是我搜索答案的问题,我假设其他人会这样来)