Thanks to PhilM & blackmouth in another thread, I'm able to distinguish groups of b2Fixtures using UserData. This brought up another related question. I want to swap out an AtlasSprite when the user double-taps by associating it with "selected" UserData. I've almost got it working, but when after I get the new AtlasSprite added, the old one remains - but it's not attached to a body anymore. Here's my code:
- (void) addSprites {
myArray = [myGame.posArray mutableCopy];
secondArray = [myGame.posArrayTwo mutableCopy];
for (NSValue *value in myArray) {
CGPoint point = [value CGPointValue];
AtlasSpriteManager *mgr = (AtlasSpriteManager *) [self getChildByTag:kTagSpriteManager];
mySprite = [AtlasSprite spriteWithRect:CGRectMake(1, 75, 74, 37) spriteManager:mgr];
[mgr addChild:mySprite];
mySprite.position = ccp(point.y, point.x);
b2BodyDef bodyDef;
bodyDef.position.Set(point.x/PTM_RATIO, point.y/PTM_RATIO);
bodyDef.userData = mySprite;
bodyDef.fixedRotation = FALSE;
b2Body *body = world->CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(0.7f, 0.3f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 5.0f;
fixtureDef.friction = 10.0f;
b2Fixture *fixture2;
fixture2 = body->CreateFixture(&fixtureDef);
fixture2->SetUserData(@"mySprite"); // THANKS badawe & blackmouth for this solution!
body->SetMassFromShapes();
}
for (NSValue *value in secondArray) {
// set other fixtures in the same way, using different UserData
}
}
- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
CGPoint convertedTouch = [self convertTouchToNodeSpace: touch];
b2Vec2 convertedPoint(convertedTouch.x/PTM_RATIO,convertedTouch.y/PTM_RATIO);
if (tapCount == 2) {
// user double tapped
if (m_mouseJoint != NULL) { return YES; }
// Make a small box.
b2AABB aabb;
b2Vec2 d;
d.Set(0.001f, 0.001f);
aabb.lowerBound = convertedPoint - d;
aabb.upperBound = convertedPoint + d;
// Query the world for overlapping shapes.
QueryCallback callback(convertedPoint);
world->QueryAABB(&callback, aabb);
if (callback.m_fixture) {
// get user data for tapped fixture
AtlasSprite *ud = (AtlasSprite *)callback.m_fixture->GetUserData();
if ([ud isEqual:@"mySprite"]) {
// if the sprite is of the correct userdata, proceeed
callback.m_fixture->SetUserData(@"mySpriteSel"); // change userdata on fixture
b2Vec2 p = callback.m_fixture->GetBody()->GetPosition();
AtlasSpriteManager *mgr = (AtlasSpriteManager *) [self getChildByTag:kTagSpriteManager];
mySpriteSel = [AtlasSprite spriteWithRect:CGRectMake(1, 1, 74, 73) spriteManager:mgr];
[mgr addChild:mySpriteSel];
mySpriteSel.position = ccp(p.x * PTM_RATIO, p.y * PTM_RATIO);
callback.m_fixture->GetBody()->SetUserData(mySpriteSel); // change body's userdata to allow tick: to move it.
}
}
}
return kEventHandled;
}
- (void) tick: (ccTime) dt {
int32 velocityIterations = 10;
int32 positionIterations = 10;
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
b->SetLinearVelocity(b2Vec2(0,0));
b->SetAngularVelocity(0);
if ([GameHUD sharedHUD].toggleOn == YES) {
b->ApplyImpulse([self randomVect], b2Vec2(0,0));
}
//Synchronize the AtlasSprites position and rotation with the corresponding body
AtlasSprite* myActor = (AtlasSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
I've found that by putting this line
callback.m_fixture->GetBody()->SetUserData(mySpriteSel);
in prior to adding the new AtlasSprite, I get the desired result, but the new AtlasSprite is not attached to the body. However, if I run it as I have above, I get exactly what I want, but the original mySprite is left on screen, but not attached to its body anymore.
Is there something wrong with my approach or is this just not what the UserData was meant for?