Cocos2d Box2d - assign generic userData to bodyDef - cocos2d-iphone

Most examples I see of assigning userData go something like this:
CCSprite *sprite = [CCSprite spriteWithFile:#"whatever.png" rect:CGRectMake(0, 0, screenSize.width, screenSize.height)];
sprite.tag = kWallTag;
[self addChild:sprite];
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
groundBodyDef.userData = (__bridge void*)sprite;
That's fine if you're using a sprite. But in my case, I don't want to create a sprite because I just want to test collisions with the screen edges. I could create a sprite the size of the screen with just a border but I don't want to use that much texture memory just for detecting walls. So my question is how to assign the kWallTag to the groundBodyDef, without assigning it a sprite. And how would I retrieve the tag value?

I've answered the first part:
GenericUserData *usrData = (GenericUserData*)malloc(sizeof(GenericUserData));
usrData->tag = kWallTag;
groundBodyDef.userData = usrData;
But I don't know how to test for the generic data:
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (__bridge CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (__bridge CCSprite *) bodyB->GetUserData();
How do I test for generic user data instead of just assuming that it's a CCSprite?

Related

How to get b2Body of current CCSprite

In my code i've always used spriteA = (__bridge CCSprite *) bodyA->GetUserData(); //where spriteA is a CCSprite and bodyA is a b2Body. I use it to get whatever sprite is linked to bodyA. My problem is, how do I do this the other way around? I have a sprite and I want to find out what b2Body is linked to it. How do I do this?
Edit
I don't know wether I set it up right or not, but I'm trying to remove all b2bodies (and sprites) in an array called row4 once there are no more blue objects (objects in row4BlueArray)
Here is part of the code in my tick method:
//Find the sprite for the b2Bodies
else if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
spriteA = (__bridge CCSprite *) bodyA->GetUserData();
spriteB = (__bridge CCSprite *) bodyB->GetUserData();
contactPositionX = spriteA.position.x;
contactPositionY = spriteB.position.y;
//If sprite is a member of row 4 (tag 400)
if (spriteA.tag == 400 && spriteB.tag == 8)
{
[self createExplosionBlue];
[self addTileScore];
[self removeChild:spriteA cleanup:YES];
[self removeChild:spriteB cleanup:YES];
NSLog(#"row 4 count: %d",row4BlueArray.count);
//Remove object from another array
[row4BlueArray removeLastObject];
toDestroy.insert(bodyA);
toDestroy.insert(bodyB);
[self unschedule:#selector(tick:)];
ballCount = 0;
//if that array is empty, then remove all objects from this array (row4)
if (row4BlueArray.count == 0) {
for (b2Body * b = _world->GetBodyList(); b != NULL; b = b->GetNext()) {
Box2DSprite * sprite = (__bridge Box2DSprite*) b->GetUserData();
b2Body * spriteBody = sprite.body;
//not sure how to remove all bodies in an array (row4)`
}
}
}
One simple way to do this is to cache the body as a property of a class.
You could extend the CCSprite class with a class called Box2DSprite. In Box2DSprite, you include a property for the b2Body * you used to create the physics for your sprite. You can also store a reference to the world if you think you may need it.
#interface Box2DSprite : CCSprite {
b2Body * body;
b2World * world;
} // end ivars
#property (readwrite) b2Body * body;
#property (readwrite) b2World * world;
Also remember to rename your .m file to a .mm file
Edit
In your game loop, to obtain the Box2DSprite you simply need to cast it back to a Box2DSprite.
for (b2Body * b = world->GetBodyList(); b != NULL; b = b->GetNext()) {
Box2DSprite * sprite = (__bridge Box2DSprite*) b->GetUserData();
b2Body * spriteBody = sprite.body;
// Do stuff with body here
}
You just need to ensure that whenever you create a sprite with a physics body, you are creating a Box2DSprite.
Hope this helps

CCRenderTexture not making a perfect copy (cocos2d)

I am having a problem with CCRenderTexture:
The item on the left is the "copy" .. on the right is the original. I use the code below to try to create and EXACT copy of the sprite/png on the right.
But when I create a sprite from the CCRenderTexture below ... I am losing the drop shadows and the the image looks a bit smudged ... should be an exact copy.
Any ideas?
Am I using CCRenderTexture incorrectly?
Why the difference?
//Sprite on the right
CCSprite* hack = [CCSprite spriteWithFile:#"ColumnMatchCheck.png"];
hack.anchorPoint = ccp(0,0);
CCRenderTexture* render = [CCRenderTexture renderTextureWithWidth:hack.contentSize.width
height:hack.contentSize.height
pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[render beginWithClear:1 g:1 b:1 a:0];
[render.sprite setBlendFunc:(ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}];
[hack visit];
[render end];
//Sprite on the left
CCSprite* test2 = [CCSprite spriteWithTexture:render.sprite.texture];

How To Have The Same Sprite In Multiple Locations Cocos2d

How To Have The Same Sprite In Multiple Locations Cocos2d Please Help
I have searched all over and cannot find answer
Just create multiple Sprites (CCSprite instances). They can all use the same texture (bitmap-file).
CCSprite * mySprite1;
CCSprite * mySprite2;
CCSprite * mySprite3;
// create several sprites from the same bitmap file
mySprite1 = [CCSprite spriteWithFile:#"spriteBitmap.png"];
mySprite2 = [CCSprite spriteWithFile:#"spriteBitmap.png"];
mySprite3 = [CCSprite spriteWithFile:#"spriteBitmap.png"];
mySprite1.position = ccp(100, 100);
mySprite2.position = ccp(200, 200);
mySprite3.position = ccp(300, 300);
You can not add the same CCSprite as a child to multiple CCNodes but you can make Cocos2D render the same CCSprite multiple times.
To achieve this you need to create a subclass of CCNode that will store the reference to your CCSprite and draw it in its -draw method applying required transformations.
For example
-(void)draw
{
[super draw];
CGPoint initialPosition = [_node position];
float initialScale = [_node scale];
[_node setScale:self.scale];
[_node setPosition:self.position];
[_node visit];
[_node setPosition:initialPosition];
[_node setScale:initialScale];
}
You may have to to use glScissor if you need picture-in-picture appearance.
Then you just need to addChild an instance of this class for every time you want an additional copy of your original CCSprite rendered.
Put a method on a for loop.
Inside the method create the CCSprite and modify it.
This is best suited for static sprites, since I don't know how you would access these outside of the method.

Cocos2d - Orbiting/spinning one sprite around another

I've got to sprites, essentially a nucleus (parent) and an electron (child). I'm trying to find a way to have the electron orbit the nucleus. I've found a few posts here and there on moving anchor points, but that is apparently related to the texture of the sprite, not the sprite itself.
This is my current init method for my parent sprite:
self = [super init];
if (self) {
CCSprite *orbitAnchor = [CCSprite new];
[self addChild:orbitAnchor];
orbitAnchor.position = ccp(32,32);
CCSprite *orbiter = [CCSprite spriteWithFile:#"line.png" rect:CGRectMake(0, 0, 8, 8)];
[orbitAnchor addChild:orbiter];
orbiter.position = ccp(40,40);
CCAction *orbitAction = [CCRepeatForever actionWithAction:[CCRotateTo actionWithDuration:1 angle:720]];
[orbitAnchor runAction:orbitAction];
[self initAnimations];
}
return self;
Those numbers are all totally arbitrary though - I just stuck them in there and got what looked best. I'm sure there's a more programatic way to do what I want.
Basically, I'm looking for a way to set the child's axis point at the center of the parent, and then have it rotate around that point. It seems like a relatively simple thing to do using CCRotate and such, but I think I'm missing what to search for in order to move the anchor point of the child. Any suggestions/other posts?
Thanks
You have [CCSprite new] which is an unusual use, probably not supported. Unless the orbit anchor node should display a texture, you can just use a CCNode as anchor.
Technically you're doing everything correct from what I can see. You might want to try without the rotate actions and instead change the direction manually in a scheduled update method.
CCNode *orbitAnchor = [CCNode node];
[self addChild:orbitAnchor z:0 tag:1];
orbitAnchor.position = ccp(32,32);
CCSprite *orbiter = [CCSprite spriteWithFile:#"line.png" rect:CGRectMake(0, 0, 8, 8)];
[orbitAnchor addChild:orbiter z:0 tag:2];
orbiter.position = ccp(40,40);
[self scheduleUpdate];
Update method:
-(void) update:(ccTime)delta
{
CCNode* orbitAnchor = [self getChildByTag:1];
orbitAnchor.direction += 5;
}
From the image filename it looks like you're trying to draw a line from the orbitAnchor to the orbiter. You can do that with ccDrawLine:
-(void) draw
{
CCNode* orbitAnchor = [self getChildByTag:1];
CCNode* orbiter = [self getChildByTag:2];
ccDrawLine(orbitAnchor.position, orbiter.position);
}
Anchor points. Automatically, objects are placed based on the center of an object right? with anchor points you can move that around, so if you rotate the object it will rotate around the anchorpoint. http://www.qcmat.com/understanding-anchorpoint-in-cocos2d/

cocos2d: Why can't I change a scaled node's position?

I think I'm just understanding scaling/positioning nodes/layers incorrectly. I'm setting up my node like this (node class is derived from CCNode):
-(id) init
{
if ((self = [super init]))
{
// Create parallax background node.
background = [BackgroundNode node];
[self addChild:background z:0];
// Create foreground node.
foreground = [ForegroundLayer node];
[self addChild:foreground z:0];
self.position.y = 500.0f;
self.scaleX = 1.5f;
self.scaleY = 1.5f;
}
return self;
}
It doesn't seem to matter what I set the self.position.y to - the scaled node is always displayed as though it was positioned in the bottom-left of the screen. I've tried playing around with anchorPoint as well, but it doesn't really seem to change anything.
The reason I'm asking is because I'd like to be able to pan vertically when I'm zoomed in - this doesn't seem to really be the right way to accomplish it, though. Any ideas?
self.position.y = 500.0f;
doesn't work. It should be
self.position = ccp(self.position.x, 500.0f);
Please refer to "Cocoa Objective-c Property C structure assign fails".