How to destroy a body with CCPhysicsSprite - cocos2d-iphone

I've realized that CCPhysicsSprite is different in a few ways from CCSprite. For example, you must set the body before you set the position of the sprite. I believe it is one of these differences that is causing an EXC_BAD_ACCESS error when I try destroying the body. I call the scheduleSprite method in the update method.
-(void)scheduleSprite {
if ([testSprite physicsSprite].b2Body != NULL) {
b2Vec2 force = b2Vec2(-5, 10.0 * [testSprite physicsSprite].b2Body->GetMass());
[testSprite physicsSprite].b2Body->ApplyForce(force, [testSprite physicsSprite].b2Body->GetWorldCenter() );
if ([testSprite physicsSprite].position.x < 0) {
world->DestroyBody([testSprite physicsSprite].b2Body);
[testSprite physicsSprite].b2Body = NULL;
}
}
}
I get an EXC_BAD_ACCESS pointing to line
b2Vec2 pos = _b2Body->GetPosition();
in the
-(CGAffineTransform) nodeToParentTransform
method, within the class
CCPhysicsSprite.mm
Thanks.

Despite you destroyed the body, the sprite will keep doing its stuff, so you may remove the sprite from its parent also, something like.-
if ([testSprite physicsSprite].position.x < 0) {
world->DestroyBody([testSprite physicsSprite].b2Body);
[[testSprite physicsSprite] removeFromParentAndCleanup:YES];
}

- (void) killBody:(cpBody*)body
{
cpBodyEachShape_b(body, ^(cpShape *shape) {
cpSpaceRemoveShape( _space, shape );
cpShapeFree(shape);
});
cpSpaceRemoveBody( _space, body );
cpBodyFree(body);//edited to include this line
CCPhysicsSprite* sprite = (__bridge CCPhysicsSprite*) body->data;
[sprite removeFromParentAndCleanup:true];
}
I was getting the same thing, the method above seemed to fix it.
First, remove the shapes attached to the body from the space. I'm using _space as a instance variable pointing to a cpSpace object. Next, remove the body from the space. Finally, remove the sprite from the parent.
When I make a CCPhysicsSprite object, I connect its body to the sprite using body->data. You can see that in the method above.
If you are starting with a CCPhysicsSprite object, you would first get the cpBody object from the sprite, then remove the body and shape as shown above.

Related

How to remove a node from its callback function?

There is a Geode whose Geometry is a ball with a MatrixTransform() assigned above it. It's callback function makes it falls. When the ball intersects with the ground, I hope to remove it from the scene.
The following code throws exception:
//inside the ball's callback
virtual void operator()(osg::Node* node ,osg::NodeVisitor* nv)
{
using namespace osg;
MatrixTransform* matrix_node = dynamic_cast<MatrixTransform*>(node);
Matrix matrix = matrix_node->getMatrix();
velocity += Vec3(0, 0, -0.002);
matrix.postMultTranslate(velocity);
matrix_node->setMatrix(matrix);
Vec3 now_position = start_position * matrix;
osgUtil::IntersectVisitor ivXY;
osg::ref_ptr<osg::LineSegment> lineXY = new osg::LineSegment(now_position, now_position+velocity);
ivXY.addLineSegment(lineXY);
GAME.main_camera->m_pHostViewer->getSceneData()->accept(ivXY) ;
if(ivXY.hits())
{
node->getParent(0)->removeChild(node);
}
return;
}
How to do it correctly? Thank you!
This is an excerpt from the OpenSceneGraph Group class (from which MatrixTransform inherits):
void Group::traverse(NodeVisitor& nv)
{
for(NodeList::iterator itr=_children.begin();
itr!=_children.end();
++itr)
{
(*itr)->accept(nv);
}
}
bool Group::removeChild( Node *child )
{
unsigned int pos = getChildIndex(child);
if (pos<_children.size()) return removeChildren(pos,1);
else return false;
}
Your code (which is called from traverse) throws an exception probably because the iterator gets invalidated in the middle of the loop, when removeChild is called. To remove the node you would have to wait at least until your node callback returns.
To resolve this I would just use a node mask to control whether the ball is displayed or not. Initially, set the ball node mask to the "visible" value. When the ball hits the ground, set the node mask to the "hidden" value. The node will not be traversed/rendered, but will still be in memory.
If memory is an issue, you can move the code to the parent node or outside the update traversal (e.g. use a modified Viewer::run method).

bad access with array of sprites

I am populating an NSMutableArray with CCSprites and adding them to the current layer which is added to a scene. When i try to access the elements of the array I get a bad access error
sprites = [[[NSMutableArray alloc] init]autorelease];
int j = 0;
for (int i=0; i<[sprites count]; i++)
{
j=i+1;
sprite = [CCSprite spriteWithFile:[NSString stringWithFormat:#"intro%d.png",j]];
sprite.position = ccp(WIDTH/2, HEIGHT/2+(i*HEIGHT));
[sprites addObject:sprite];
}
for (int i = 0; i<[sprites count]; i++)
{
[self addChild:[sprites objectAtIndex:i]];
}
[self scheduleUpdate];
}
return self;
This is done in the init method and sprites and sprite are declared in the header file. Inside the update method, I have
sprite = [sprites objectAtIndex:1];
sprite.position = ccpAdd(sprite.position, ccp(0,dy));
CCSprite* spr = [sprites objectAtIndex:2];
spr.position = ccpAdd(spr.position, ccp(0,dy));
When control is passed to the update method, I get a bad access. I would be glad if anyone could help me
This is happening because you created an autorelease object, that you then try to access later in your update code. The sprites array has a retain count of zero (since it is autorelease), and gets deallocated, you then try to access it, and get a bad-access error.
To fix, you should not make the sprites array an autorelease object. Since you plan on using the object later on in your code, you should keep it retained (by not adding the autorelease). You should also then make sure to release the array in the (whatever the parent object that is operating on the array)'s dealloc method.
- ( void ) dealloc {
[ sprites release ];
[ super dealloc ];
}
You should retain the sprites array (make certain you release it in your object's dealloc). Since it is autoreleasable, during the cycle from your init to your update method, it gets released.
Also (strange) , if your code is as shown, sprites.count is 0 when you start your first loop. You wont get much in there :).
The others are right (the autorelease call is the culprit - you should just remove it and then release the array in the dealloc method of the scene).
Also note that [[[NSMutableArray alloc] init]autorelease] has the same effect as [NSMutableArray array] (for your scenario, though, this is not what you need).

cocos2d : How to clone a CCNode hierarchy

I try to clone a CCNode hierarchy, the problems is I need to reset and set all the variable by type, can I have a way to do that more automatically ?
basiclly what I want to do is :
- store a CCNode* (with some child, for example an image at Pos 10-10, and a Label at Pos 100-50 with the text "Test");
- then clone it, for get a new CCNode* with the same default value and childs.
I need to copy it, because after they will get modify, is like a template of Node, before get custom value.
If you know a simple way to copy, and set all the hierarchy (with correct type also), without big if/else statement for each kind of type, it will help me a lot ^^
thanks
This code clones CCNode and all child CCNodes recursively. You can add other subclasses and other properties to copy.
+ (CCNode*) cloneCCNode:(CCNode*)source
{
CCNode* clone = [CCNode node];
for (CCNode* srcSubnode in source.children) {
CCNode* subnode;
if ([srcSubnode isKindOfClass:[CCSprite class]]) { //only CCSprites are copied, add other subclasses if you need to
CCSprite* srcSprite = (CCSprite*)srcSubnode;
subnode = [CCSprite spriteWithTexture:srcSprite.texture];
((CCSprite*)subnode).displayFrame = srcSprite.displayFrame;
} else {
subnode = [self cloneCCNode:srcSubnode];
}
subnode.rotation = srcSubnode.rotation;
subnode.position = srcSubnode.position;
subnode.anchorPoint = srcSubnode.anchorPoint;
subnode.zOrder = srcSubnode.zOrder;
[clone addChild:subnode];
}
return clone;
}

Moving Box2d Bodies Like CCSprite Objects

In cocos2d, you can ease in CCSprites and move them around in all kinds of ways. Most importantly - they can have easing in/out. For most games this is desirable for smooth movement etc.
id action = [CCMoveTo actionWithDuration:dur position:pos];
move = [CCEaseInOut actionWithAction:action rate:2];
[self runAction: move];
When moving a box2d body, the sprite attached to it is updated after the box2d step(). Moving the sprite and then updating the body is not an option here, as it entirely defeats the purpose of the physics framework.
So the other option, which I have successfully implemented, is to calculate the displacement, velocity and acceleration of a sprite by treating it as a mechanics entity in its own right. Each time I call my update() on the sprite so the character can decide where to move etc, my superclass also stores the previous position and velocity. These are stored as box2d compliant values by dividing by the PTM_RATIO.
In the subclass of CCSprite, called FMSprite:
-(CGPoint) displacement {
return ccpSub(self.position, lastPos);
}
-(b2Vec2) getSpriteVelocity:(ccTime)dt {
return b2Vec2(self.displacement.x / dt / PTM_RATIO,
self.displacement.y / dt / PTM_RATIO);
}
-(b2Vec2) getSpriteAccel:(ccTime)dt {
b2Vec2 currVel = [self getSpriteVelocity:dt];
if (dt == 0) {
return b2Vec2(0,0);
} else {
float accelX = (currVel.x - lastVel.x)/dt;
float accelY = (currVel.y - lastVel.y)/dt;
return b2Vec2(accelX, accelY);
}
}
// This is called each update()
-(void) updateLast:(ccTime)dt {
// MUST store lastVel before lastPos is updated since it uses displacement
lastVel = [self getSpriteVelocity:dt];
lastPos = ccp(self.X, self.Y);
}
// Leave this method untouched in subclasses
-(void) update:(ccTime)dt {
[self updateObject:dt];
// Store previous update values
[self updateLast:dt];
}
// Override this method in subclasses for custom functionality
-(void) updateObject:(ccTime)dt {
}
I have then subclassed "FMSprite" into "FMObject", which stores a b2Body etc.
In order to move the body, I must first move a sprite and track its acceleration, through which I can find the required force (using the mass) needed to follow the sprite's motion. Since I can't move the object's sprite (which is synchronized to the body), I make another sprite called a "beacon", add it as a child to the object, and move it around. All we need to do then is to have a function to synchronize the position of the box2d body with this beacon sprite using the forces I mentioned before.
-(void) followBeaconWithDelta:(ccTime)dt {
float forceX = [beacon getSpriteAccel:dt].x * self.mass;
float forceY = [beacon getSpriteAccel:dt].y * self.mass;
[self addForce:b2Vec2(forceX, forceY)];
}
The result is brilliant, a smooth easing motion of the b2body moving where ever you want it to, without playing around with any of its own forces, but rather copying that of a CCSprite and replicating its motion. Since it's all forces, it won't cause jittering and distortions when colliding with other b2Body objects. If anyone has any other methods to do this, please post an answer. Thanks!
What I do is different from yours, but can also Moving Box2d Bodies Like CCSprite Objects and even use the CCAction.
The most important thing is to create an object that contain ccSprite and b2body.
#interface RigidBody : CCNode {
b2Body *m_Body;
CCSprite *m_Sprite;
}
And then, rewrite the setPosition method.
-(void)setPosition:(CGPoint)position
{
CGPoint currentPosition = position_;
b2Transform transform = self.body->GetTransform();
b2Vec2 p = transform.p;
float32 angle = self.body->GetAngle();
p += [CCMethod toMeter:ccpSub(position, currentPosition)];
self.body->SetTransform(p, angle);
position_ = position;
}
The setPosition method calculate how much the position change,and set it to the b2body.
I hope I have understanding your question and the answer is helpful for you...

Repeatedly move a box2d body in a similar way to moving CCSprites with CCRepeatForever

I've got a problem with my current project.
What I'd like to do is make a b2Body move up and down repeatedly. I already know how to do this with a CCSprite:
[paddle runAction:[CCRepeatForever actionWithAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:1.0 position:ccp([paddle position].x,[paddle position].y+40)],
[CCMoveTo actionWithDuration:1.0 position:ccp([paddle position].x,[paddle position].y)],
nil
]]];
Can anybody help me do the same thing with a b2Body?
Thanks in advance!
You will have to implement the sequence yourself, which involves:
keeping track of the current target position
from the current position of the body, detect whether it has reached the target
if it has, change the target position
apply force, impulse or set velocity as necessary to move the body
You might be able to extend CCMoveTo to make your own class to do this... I would look into that first.
i've got it dude,
in almost explanation, every CCsprite move depend on b2body movement -that movement is placed at 'tick' method-.
in my case, i reverse that way, i move b2body according to CCsprite movement on tick method, so i give these code on tick method:
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if (sprite.tag == 4 || sprite.tag == 5) {
b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO,
sprite.position.y/PTM_RATIO);
float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation);
b->SetTransform(b2Position, b2Angle);
}
}
}