how to remove shape and body after a delay in collision callback (cocos2d-chipmunk) - cocos2d-iphone

can anyone help me to remove shape and body after a delay in collision callback.
By using cpSpaceAddPostStepCallback(sapce, (cpPostStepFunc)postStepRemove, blockShape, NULL);
i could safely remove the shape .
But i need some delay before i call the function.
I play an animation once the collision in detected.
At the end of the code i need to remove the shape.
Can anyone please help me with some sample code.
The code that i have written is as follows.
int collisionSapusBlock(cpArbiter *arb, struct cpSpace *sapce, void *data)
{
cpShape *sapusShape, *blockShape;
cpArbiterGetShapes(arb, &sapusShape, &blockShape);
cpBody *BlockBody = blockShape->body;
cpBody *sapusBody = sapusShape->body;
CCNode *parent = (CCNode*)data;
if (cpvlength(sapusBody->v) > 45)
{
NSLog(#"Collision2 called %f",cpvlength(sapusBody->v));
CCSprite *sprite = blockShape->data;
[parent removeChild:sprite cleanup:YES];
///////
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"Block2.plist"];
CCSpriteBatchNode *sheet1 = [CCSpriteBatchNode batchNodeWithFile:#"Block2.png" capacity:2];
[parent addChild:sheet1];
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCSprite *sapusSprite1 = [[CCSprite alloc] initWithSpriteFrameName:#"Block2001.png"];
CCAnimation *sapusAnim = [[CCAnimation alloc] initWithName:#"select" delay:0.15f];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2001.png"]];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2002.png"]];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2003.png"]];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2004.png"]];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2005.png"]];
[sapusAnim addFrame:[cache spriteFrameByName:#"Block2006.png"]];
[sapusSprite1 addAnimation: sapusAnim];
[sheet1 addChild:sapusSprite1];
CCAnimate *animate = [CCAnimate actionWithAnimation: sapusAnim restoreOriginalFrame:NO];
[sapusSprite1 runAction:animate];
blockShape->data = sapusSprite1;
/////here after playing the animation i need to delete the blockShape. Help plzzz
}
return 1;
}
Can anyone please help me with some sample code.
Thanks

I haven't tried this, but it should work. In the collision handler, kick off the animation as part of a sequence and then execute the postStepCallback as the last item in the sequence:
CCSequence *finalSeq = [CCSequence actions:[CCAnimate actionWithAnimation:sapusAnim restoreOriginalFrame:NO],
[CCCallFunc actionWithTarget:self selector:#selector(removeCpShapeAndBody)], nil];
[sapusSprite1 runAction:finalSeq];
Then you'd create a method called removeCPShapeAndBody which would call cpSpaceAddPostStepCallback. Ideally you'd have all of this in an object that would hold reference to the shape and body (e.g. a subclass of CCSprite) so you don't have to pass those around.

Related

Move a whole animation sprite sheet

I have some sprite-sheet that i have to animate forever , and i would like to add it as a CCLayer
to my scene .
Later on , i have to move this whole animation sprite on the screen.
So, for example, i have some animation of a dog walking, from sprite sheet, this one is running forever. than i want to be able to move this dog on screen while animating.
What is the best way to do this ? (or the right way)
This is how i animate the frames :
CCSprite *boom;
boom = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"%#_00000.png",file]];
boom.position=touch;
[self addChild:boom];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:
#"%#_0000%i.png",file,i]];
[animFrames addObject:frame];
}
CCAnimation* boxAnimation = [CCAnimation animationWithSpriteFrames:animFrames delay:0.075f];
CCAnimate * boxAction = [CCAnimate actionWithAnimation:boxAnimation];
CCAction *call=[CCCallBlock actionWithBlock:^{[self removeFromParentAndCleanup:YES];}];
CCAction * sequence=[CCSequence actions:boxAction,[CCHide action],call,nil];
[boom runAction:sequence];
return self;
How would you move this whole thing ?
There are a few ways to do this. If you are not preocupied with collision detection, then one way would be to :
CGPoint egressPosition = ccp(0,0); // figure this out in your app
float moveDuration = 1.5f ; // whatever time you compute for desired speed and distance
id move = [CCMoveTo actionWithDuration:moveDuration position:egressPosition];
id spawn = [CCSpawn actions:sequence,move,nil];
[boom runAction:spawn];
otherwise, using your code as is
[self schedule:#selector(moveBox:)]; // optional, you could do this in update method
[boom runAction:sequence];
-(void) moveBoom:(CCTime) dt {
CGPoint newPosition;
delta = ccp(dt*speedX,dt*speedY); // crude , just to get the idea
newPosition = ccpAdd(boom.position,delta);
// here you can figure out collisions at newPosition before the collision
// and do whatever seems appropriate
boom.position = newPosition;
}

Advanced Spritesheeting - using a spritesheet in multiple classes

So after taking a little break after being very frustrated for months on end with this issue, I am now back and attempting to solve my issue once and for all.
I’m not going to post code, because the code I have is messy and patchy and is from countless points of view.
My issue is this:
I have a sprite, and his information is stored in a class called player.m. I have a game level, and it is stored in GameLevelLayer.m
I have player.m store velocity, direction, and all the information about the character, and then I have the GameLevelLayer.m implement, move, use bounds to check collisions, etc.
I now want to make the character move using a spritesheet (I know how to do spritesheeting, I just don’t know how to do it while working with two different classes)
My question is, do I create the batchnodes and spritesheet information (CCBatchnodes, caches, etc) and all of the actions and everything in player.m, and then run them in GameLevelLayer.m? Or do I create all that in GameLevelLayer.m
I really need some help here because I’ve been stuck on this for months
EDIT:
Here is where I am, thanks to a suggestion from a community member.
This is my entire player.m (Long story short, I define everything about the player 'you might just want to pay attention to the init, because that seems to be the problem. The other stuff is my physics engine')
#import "Player.h"
#import "SimpleAudioEngine.h"
#import "GameLevelLayer.h"
#implementation Player
#synthesize velocity = _velocity;
#synthesize desiredPosition = _desiredPosition;
#synthesize onGround = _onGround;
#synthesize forwardMarch = _forwardMarch, mightAsWellJump = _mightAsWellJump, isGoingLeft = _isGoingLeft;
#synthesize WalkAction = _WalkAction;
#synthesize isMoving = _isMoving;
-(id)initWithTexture:(CCTexture2D *)texture{
if (self = [super init]) {
self.velocity = ccp(0.0, 0.0);
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: #"BankerSpriteSheet_default.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"BankerSpriteSheet_default.png"];
[self addChild:spriteSheet];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <=6; ++i) {
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"banker%d.png", i]]];
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.1f];
self = [super initWithSpriteFrameName:#"banker1.png"];
self.WalkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
//[_Banker runAction:_WalkAction];
[spriteSheet addChild:self];
[[CCAnimationCache sharedAnimationCache] addAnimation:walkAnim name:#"walkAnim"];
}
}
return self;
}
-(void)update:(ccTime)dt {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults synchronize];
CGPoint jumpForce = ccp(0.0, 310.0);
float jumpCutoff = 150.0;
if (self.mightAsWellJump && self.onGround) {
self.velocity = ccpAdd(self.velocity, jumpForce);
if (![defaults boolForKey:#"All Muted"]) {
if (![defaults boolForKey:#"SFX Muted"]) {
[[SimpleAudioEngine sharedEngine] playEffect:#"jump.wav"];
}
}
} else if (!self.mightAsWellJump && self.velocity.y > jumpCutoff) {
self.velocity = ccp(self.velocity.x, jumpCutoff);
}
CGPoint gravity = ccp(0.0, -450.0);
CGPoint gravityStep = ccpMult(gravity, dt);
CGPoint forwardMove = ccp(800.0, 0.0);
CGPoint forwardStep = ccpMult(forwardMove, dt);
self.velocity = ccp(self.velocity.x * 0.90, self.velocity.y); //2
if (self.forwardMarch) {
self.velocity = ccpAdd(self.velocity, forwardStep);
if (!self.isMoving) {
//[self runAction: _WalkAction];
//self.isMoving = YES;
}
} //3
CGPoint minMovement = ccp(0.0, -450.0);
CGPoint maxMovement = ccp(120.0, 250.0);
self.velocity = ccpClamp(self.velocity, minMovement, maxMovement);
self.velocity = ccpAdd(self.velocity, gravityStep);
CGPoint stepVelocity = ccpMult(self.velocity, dt);
if (!self.isGoingLeft) {
self.desiredPosition = ccpAdd(self.position, stepVelocity);
}else{
self.desiredPosition = ccp(self.position.x-stepVelocity.x, self.position.y+stepVelocity.y);
}
}
-(CGRect)collisionBoundingBox {
CGRect collisionBox = CGRectInset(self.boundingBox, 3, 0);
//CGRect collisionBox = self.boundingBox;
//collisionBox = CGRectOffset(collisionBox, 0, -2);
CGPoint diff = ccpSub(self.desiredPosition, self.position);
CGRect returnBoundingBox = CGRectOffset(collisionBox, diff.x, diff.y);
return returnBoundingBox;
}
#end
So now the issue is, how do i get the player to appear in the game like a normal sprite, and then how do I run the animations when the player begins to move forward
Your game architecture is very good! Keep player properties on player and use GameLayer only to send player actions is the best approach.
If your Player Class extends from CCSprite, put everithing related to player in your Player Class: Run animations on it, load the cache on player constructor (use the CCSprite initWithTexture:rect:rotated: or a custom autorelease constructor, like a +(id)player ).
Then, in your GameLevelLayer you will need a CCSpriteBatchNode to add your player to it. You can use this batch node to add other objects that use same spritesheet.
EDIT:
Reviewing your code, your initWithTexture is wrong. The correct is to call [super initWithTexture:]:
-(id)initWithTexture:(CCTexture2D *)texture{
if (self = [super initWithTexture:texture]) {
Then, in your GameLevelLayer, create your player and add it to a CCSpriteBatchNode:
// *** In GameLevelLayer.m ***
// Create player
Player *player = [Player spriteWithFile:#"player.png"];
// Create batch node
CCSpriteBatchNode *batchNode = [CCSpriteBatchNode batchNodeWithTexture:player.texture];
// Add batch node as child
[self addChild:batchNode];
// Add player as batch node's child
[batchNode addChild:player];
// Set player position
player.position = ccp(100,100);
Your GameLevelLayer needs to do only this to create your Player. And when it detects user input, call methods like "jump", "moveRight", "moveLeft" in your player. Also detect collisions and send actions to player like "die", "getSpecialItem".
// *** In GameLevelLayer.m ***
[player jump];
[player die];
So, your player needs to handle these methods and performs its own logic to do the actions:
// *** In Player.m ***
- (void)moveRight
{
self.position = ccpAdd(self.position, ccp(10,0));
}

Animate CCSprite

I am newer to cocos2d and preparing my demo game. I am moving a sprite from right to left using just a single image like a bird image moving from left to right. But I want to animate that sprite through various images so that it may look like a flying bird. I don't know how to accomplish that.
Here is my code:
CCSprite *target = [CCSprite spriteWithFile:#"Target.png" rect:CGRectMake(0, 0, 27, 40)]
id actionMove = [CCMoveTo actionWithDuration:actualDuration position:ccp(-target.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)];
[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
For Animating particular sprite You would require sprite-sheet to be there in your Resource. You can create the Sprite-sheet from eitherTexture Packer OR Zwoptex Tools which I normally use.
Next you can implement the Below Code
CCSpriteBatchNode *sheet = [CCSpriteBatchNode batchNodeWithFile:#"drawing1-i3.png"]; // Png File Name
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"drawing1-i3.plist"]; // Plist File Name
[self addChild:sheet];
//Generating the Animation
NSMutableArray *arr_anim =[NSMutableArray array];
for(int i=1; i<30; i++) // i< number of frames in the plist File Name
{
NSString *str_fileNm = [NSString stringWithFormat:#"drawing1%d.png",i];
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:str_fileNm];
[arr_anim addObject:frame];
}
CCSprite *startAnim = [CCSprite spriteWithSpriteFrameName:#"drawing11.png"];
[startAnim setPosition:ccp(150,150)];
[self addChild:startAnim];
//Starting the Animation
CCAnimation *animation = [CCAnimation animationWithFrames:arr_anim delay:0.15f];
// id action =[CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation restoreOriginalFrame:YES]];
id action =[CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO];
[startAnim runAction:action];
I think it would help you for creating the Animations.
Use the CCAnimation class.
In particular, use methods like animationWithFrames: and supply your images as an array.

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: Preloading animation causes a crash

I am trying to preload an animation in the init method of my layer. I then call the animation if the screen is touched. The app crashes with no error message as soon as I touch the screen and seems it is to do with calling the preloaded animation. I would like to do it this way as it seems expensive to create the animation every time the screen is touched - which does seems to work though. Any tips greatly appreciated.
Sample Code:
In my header:
#interface Test : CCLayer {
NSMutableArray *wake;
CCSprite* ani;
CCAnimate *animate;
}
#end
In my implementation:
-(id) init {
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ani.plist" texture:[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:#"ani.png"]]];
ani = [CCSprite spriteWithSpriteFrameName:#"ani1.png"]; //comes from .plist file
ani.anchorPoint=ccp(0,0);
ani.position = ccp(700,65);
[self addChild:ani z:30];
wake = [NSMutableArray array];
for(int i = 1; i <= 4; i++) {
[wake addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"ani%d.png",i]]];
}
animate = [CCAnimate actionWithAnimation:[CCAnimation animationWithFrames:wake delay:1.0f] restoreOriginalFrame:FALSE];
}
return self;
}
Handling the touch:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// run the animation
[ani runAction:animate];
}
Animations in Cocos2d are not designed to be reused. You need to create a new one every time.
Problem solved by creating properties for the array and animation on the class using nonatomic,retain.
You only need to retain the animation but the array can be local.
self.myAnimation = [[CCAnimation animationWithFrames:myAniFramesArray delay:0.1f] retain];
Remember to make the property nonatomic, retain as stated by Chev and to release any objects you retain in the appropriate dealloc method.