Box2D - b2body GetUserData всегда возвращает ноль - PullRequest
2 голосов
/ 22 февраля 2011

Я пытаюсь отрегулировать положение и вращение спрайта в зависимости от положения тела b2 в box2d.

После того, как я создал тело, я устанавливаю свойство userData для объекта моего тела, которое содержит спрайт, позицию и т. Д. Проблема в том, что в методе тика 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

-(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, так что, если я делаю что-то не так, пожалуйста, дайте мне знать!

EDIT: Я попробовал еще один кусочек кода здесь, и я, кажется, смог получить немного дальше.

if I set bodyDef.UserData = sprite; 

сразу после строки

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

тогда GetUserData возвращает спрайт. однако, если я устанавливаю пользовательские данные на спрайт в конце метода addPlayerShip, то он снова возвращает ноль. Удаляется ли объект спрайта / тела где-то?

Ответы [ 2 ]

4 голосов
/ 22 февраля 2011

Вы назначаете данные пользователя для bodyDef ПОСЛЕ , когда вызываете CreateBody () , поэтому они никогда не добавляются в фактическое тело.

b2BodyDef - это структура, которая содержит настройки для создания тела.После того как вы вызвали CreateBody (), изменения в этой структуре не имеют никакого эффекта.

Вы сами нашли решение, переместив bodyDef.UserData = sprite;где-то перед вызовом CreateBody ().Это правильное место, чтобы поставить его.Если вы думаете, что вам это нужно по какой-то причине в конце функции (другие переменные?), То вам нужно реорганизовать свой код, чтобы это не имело место.

Я бы изменил эту строку (и класс BodyObject):

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

, чтобы вы могли создать его экземпляр без необходимости в элементах box2d (т.е. до того, как они были определены), а затем использоватьсвойства для назначения этих значений после того, как они были созданы.Таким образом, вы можете передать этот объект как пользовательские данные тела, создать тело, а затем назначить b2Body *, используя [bodyObject setBody:body];

Не забудьте убедиться, что ваши свойства Body и Fixture не только для чтения.

Окончательный код может выглядеть следующим образом:

-(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 голос
/ 21 августа 2012

Если у кого-то есть та же проблема, что GetUserData () возвращает null, даже если вы SetUserData.Тогда не делайте мою ошибку и предполагайте, что b2World.QueryShape () возвращает тела в обратном вызове.Нет, это дает вам светильники.Вы должны fixture.GetBody (). GetUserData () (мой случай был: box2dweb в javascript; это вопрос, на который я гуглил, я предполагаю, что другие придут таким же образом)

...