Cocos2D setvisible =no after animation finishes - objective-c

In my code asteroids coming toward to the ship,
I want to implement explosion animation if laser hits the asteroid.
Asteroid should run explosion animation and switch to invisible mode.
Without animation when target is hit, target successfully switches to invisible mode. Without setting object to invisible, animation runs great. When I put it together because of procedural code without seeing animation it quickly set object to invisible.
How can I both see the animation then set it to invisible mode. (targets aka asteroids are in various speeds some of them are too fast while others slow)
The idea to put target to invisible is prevent them hitting to ship.
I tried this question&answer cocos2d autoremove sprite after animation didnt work
for (CCSprite *asteroid in _asteroids)
{
if (!asteroid.visible) continue;
for (CCSprite *shipLaser in _shipLasers)
{
if (!shipLaser.visible) continue;
if (CGRectIntersectsRect(shipLaser.boundingBox, asteroid.boundingBox))
{
[[SimpleAudioEngine sharedEngine] playEffect:#"explosion_large.caf"];
//explosion zombie animation starts
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 12; ++i)
{
[walkAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"zombieexplodes%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation
animationWithFrames:walkAnimFrames delay:0.1f];
_dieAction = [CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
[asteroid runAction:_dieAction];
//explosion zombie ends
[self addPoint];
//change meme to woohoo.png
[_ship setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: #"woohoo.png"]];
shipLaser.visible = NO;
[asteroid setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: #"zombieexplodes13.png"]];
//asteroid.visible=NO;
continue;
}
}
}

Use this style on calling:
CCAnimation *walkAnim = [CCAnimation
animationWithFrames:walkAnimFrames delay:0.1f];
id animate = [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
id calFuncN = [CCCallFuncN actionWithTarget:self selector:#selector(explodeAnimDone:)];
id sequence = [CCSequence actions:animate, calFuncN,nil];
[asteroid runAction:sequence];
Disable sprite when your animation is done.
-(void)explodeAnimDone:(id)sender
{
CCNode *myNode = (CCNode*)sender;
myNode.visible = false;
}

Haven't done game dev in Cocoa2D, but when I made games like this I would have a separate explosion object that removed itself when its animation ended. So you should generate an explosion object and turn the asteroid invisible right away. If you can't get autoremove on animation finish to work, time the explosion and then set a timer on your explosion object to remove itself.
I noticed that you're turning asteroids invisible... you should remove them instead - they still take up memory when they are invisible.

Related

Frames border does not move

I'm trying to build a simple game for the exercise sake and cannot figure out why the frame's border is not moving when everything else is.
When I reach the last pillar and move a bit to the right the game ends. This is somehow because the frames border does not move when the goat and pillars does.
I have three layers which is SKNode's. One called _mainLayer, second called _playerLayer and third called _squareLayer. _mainLayer contains _playerLayer and _squareLayer.
When I move the screen this code is being used:
-(void)moveScreen {
_xMoved += 100;
SKAction *move = [SKAction moveByX:-100 y:0 duration:0.5];
[_squareLayer runAction:move];
[_playerLayer runAction:move];
}
I have tried to use [_mainLayer runAction:move] instead of the two other calls but then the gameover screen is not moving along which is added in directly into the scene class (the "self").
_gameOverScreen = [[EndScene alloc] init];
_gameOverScreen.position = CGPointMake(self.size.width / 2, self.size.height / 2);
_gameOverScreen.gameOver.size = CGSizeMake(self.frame.size.width, _gameOverScreen.gameOver.size.height / 2);
[self addChild:_gameOverScreen];
The gameover screen is a SKNode class I made myself.
Does anyone have any suggestions?
I found a solution to my problem.
-(void)didSimulatePhysics {
// If the goat didn't get on top of a platform
[_playerLayer enumerateChildNodesWithName:#"Goat" usingBlock:^(SKNode *node, BOOL *stop) {
if (!CGRectContainsPoint(CGRectMake(_xMoved, 0, self.frame.size.width, self.frame.size.height), node.position)) {
_gameOverScreen.numberScore = _score;
[_gameOverScreen testNewScore:_score];
[_scoreLabel removeFromParent];
_gameOverScreen.hidden = NO;
_gameOver = YES;
}
}];
}
By making a new CGRect that simulates the screen moving I got it to work. But why this (the code below) won't work I don't know.
-(void)didSimulatePhysics {
// If the goat didn't get on top of a platform
[_playerLayer enumerateChildNodesWithName:#"Goat" usingBlock:^(SKNode *node, BOOL *stop) {
if (!CGRectContainsPoint(self.frame, node.position)) {
_gameOverScreen.numberScore = _score;
[_gameOverScreen testNewScore:_score];
[_scoreLabel removeFromParent];
_gameOverScreen.hidden = NO;
_gameOver = YES;
}
}];
}

Detecting collision between objects in array in sprite kit

i'm trying to make a game where obstacles fall from the top of the screen and the player has to try to avoid them, but the didBeginContact method is not getting called and its driving me crazy. here are the parts of my code relevant to the node collisions...
//myScene.h
#interface MyScene : SKScene <SKPhysicsContactDelegate>
//myScene.m
#implementation MyScene
static const uint32_t playerCategory = 0x1 << 0;
static const uint32_t obstacleCategory = 0x1 << 1;
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
//setup scene
self.backgroundColor = [SKColor whiteColor];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
obstacles = [NSMutableArray array];
//add player node
player = [SKNode node];
SKSpriteNode *spritePlayer = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
spritePlayer.size = CGSizeMake(50, 50);
spritePlayer.position = CGPointMake(self.frame.size.width/2, 100);
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size];
player.physicsBody.dynamic = NO;
player.physicsBody.mass = 0.02;
[player addChild:spritePlayer];
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.contactTestBitMask = obstacleCategory;
player.physicsBody.collisionBitMask = 0;
player.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:player];
[self spawnNewObstacles];
}
return self;
}
- (void)spawnNewObstacles
{
//spawn obstacles
obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
obstacle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle.frame.size];
obstacle.physicsBody.dynamic = YES;
obstacle.physicsBody.categoryBitMask = obstacleCategory;
obstacle.physicsBody.contactTestBitMask = playerCategory;
obstacle.physicsBody.collisionBitMask = 0;
obstacle.physicsBody.usesPreciseCollisionDetection = YES;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"didBeginContact Called!");
}
the didBeginContact method is not getting called and i can't find what wrong with my code
all help is appreciated...
sknode size isn't set correctly because there is no image reference, and if you log its size if should be inf, and when I tested your code I returned an exception.
to fix this error change the following line
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size]; change that to
to the following and it should work
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spritePlayer.size];
as I said below just ditch the player sknode, it is simply redundant unless there is a strong reason to use it.
///////previous answer
I couldn't help but to notice that you didn't add the obstacle node to any parent node, namely the scene, like what you did with the player node using this line
[self addChild:player];
you need to do the same thing with the obstacle node
[self addChild:obstacle];
, I am surprised that you didn't mention not seeing those nodes on the screen, unless you added them in a different section of your code that you did not include, then at this point I suggest you log the positions of both nodes to give an idea of how things are located on the screen.
as for the array you won't need it all you have to do is give a name for every obstacle node like #"obstacle", then you can use the following method to access all the nodes that has that name
obstacle.name = #"obstacle";
/////later when you want to check for node removal
[self enumerateChildNodesWithName:#"obstacle" usingBlock:^(SKNode *node, BOOL *stop) {
SKSpriteNode *child = (SKSpriteNode*)node;
if (child.position.y <0){ //or any arbitrary condition
[child removeFromParent];
}
}];
Lastly, I can't see the point from using the player as a sknode and then adding an skspritenode to it, unless I am missing something !!! :) . You can simply ditch the player sknode all together and simply use the spritePlayer node instead.
Hope this helps.

Make CCSprite move towards other CCSprite

I know this has been asked a lot before, but I can't find the solution. I have a CCSprite on the screen, the player, that is steered with the accelerometer. Then on top of the screen other CCSprites are spawned every 2 seconds, the enemies. I want all the enemies to follow the player, if the player moves the player the enemies should change direction and go towards that CCSprite. This is my code this far:
- (void)spawnEnemies
{
monsterTxt = [[CCTexture2D alloc] initWithCGImage:[UIImage imageNamed:#"obj.png"].CGImage resolutionType:kCCResolutionUnknown];
monster = [CCSprite spriteWithTexture:monsterTxt];
...
//random spawn position etc.
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
[_monsters addObject:monster]; //adds the sprite to a mutable array
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
...
//determines new position and move sprite there
[monster stopAllActions];
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
}
Now when I start the game the sprites are going towards the player, but when the player moves the enemies doesn't update their destination, they just continue down and stops at the y-coordinate of the player. And after a while the app crashes. What am I doing wrong?
My guess is that the problem may be in that section of code that you did not post.
In v2.1 of Cocos2d, to receive accelerometer measurements in your layer:
Do this in your init or onEnterTransitionDidFinish call:
self.accelerometerEnabled = YES;
And overload the is method:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CCLOG(#"Acceleration (%f,%f,%f)",
acceleration.x,
acceleration.y,
acceleration.z);
// Do something meaningful...you probably want to send a notification to
// some other scene/layer/logic. It should cache that the acceleration value
// and then do something on an update(...) call with it. Then reset it so that
// it does not get used the next update cycle untila new value comes in (here).
}
It seems you have this...and you have indicated you are receiving them...so I'm a bit confused.
SO (SANITY CHECK TIME)...
I created an example project (here) which has a single player sprite being chased by multiple monster sprites. When the user touches the screen (I did not use the accelerometer), the player sprite changes location and the sprites "chase" him. I added some random offset to their target positions so they would not all cluster on top of the player.
Here is the code for (the most important parts of) the file, which is a modified version of the HellowWorldLayer that comes with a cocos2d v2.1 project (I created it from the template):
-(void)createPlayer
{
playerMoved = NO;
if(self.player != nil)
{
[self removeChild:player];
self.player = nil;
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
CCSprite* playerSprite = [CCSprite spriteWithFile:#"Icon.png"];
playerSprite.scale = 2.0;
playerSprite.position = ccp(scrSize.width/2,scrSize.height/2);
[self addChild:playerSprite];
self.player = playerSprite;
}
-(void)createMonsters
{
const int MAX_MONSTERS = 4;
if(self.monsters.count > 0)
{ // Get rid of them
for(CCSprite* sprite in monsters)
{
[self removeChild:sprite];
}
[self.monsters removeAllObjects];
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
for(int idx = 0; idx < MAX_MONSTERS; idx++)
{
CCSprite* sprite = [CCSprite spriteWithFile:#"Icon.png"];
float randomX = (arc4random()%100)/100.0;
float randomY = (arc4random()%100)/100.0;
sprite.scale = 1.0;
sprite.position = ccp(scrSize.width*randomX,scrSize.height*randomY);
[self addChild:sprite];
[monsters addObject:sprite];
}
}
-(void)update:(ccTime)delta
{
if(playerMoved)
{ // Modify all the actions on all the monsters.
CGPoint playerPos = player.position;
for(CCSprite* sprite in monsters)
{
float randomX = (1.0)*(arc4random()%50);
float randomY = (1.0)*(arc4random()%50);
CGPoint position = ccp(playerPos.x+randomX,playerPos.y+randomY);
[sprite stopAllActions];
[sprite runAction:[CCMoveTo actionWithDuration:3.0 position:position]];
}
playerMoved = NO;
}
}
I have a retained array of CCSprite* objects (monsters) and a CCSprite* for the player. I have a flag to tell me if the player has changed position (player moved). In the update loop, if the player has moved, stop all the actions on the monsters and update them.
On the screen it looks like this:
And then when I move the main sprite...
They follow it...
Was this helpful?

How to add multiple sprite sheets in cocos2D and make it seem like one sprite?

Please help me anyone of possible. I am building a game where I will be loading multiple CCSpriteBatchNode objects and make them change co-ordinates and rotate the frames so it would seem as if the they are animated and they are moving. I have already achieved moving one CCSpriteBatchNode object from one coordinate to another and it is animated. Now I need it to do another very different animation and load another sprite sheet file and move somewhere else, how can I do this?
This is my code so far:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"PotkaEntry.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"PotkaEntry.pvr.ccz"];
[self addChild:spriteSheet];
NSMutableArray *entryAnimFrames = [NSMutableArray array];
for(int i = 1; i<=12; i++)
{
[entryAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Potka_entry%d.png", i]]];
}
CCAnimationCache *entryAnim = [CCAnimation animationWithFrames:entryAnimFrames delay:0.08f];
CGSize winSize = [CCDirector sharedDirector].winSize;
self->_body1 = [CCSprite spriteWithSpriteFrameName:#"Potka_entry1.png"];
_body1.position = CGPointMake(winSize.width/2, 0);
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:entryAnim restoreOriginalFrame:NO]];
[_body1 runAction:_walkAction];
_body1.scale = 0.4;
[spriteSheet addChild:_body1];
id entryAction = [CCMoveTo actionWithDuration:5.0f position:ccp(winSize.width/2,60)];
[_body1 runAction:entryAction];
You need to create a new CCSpriteBatchNode for each spritesheet you use (by spritesheet I mean the combined pvr.ccz file and .plist file)
The CCSpriteFrameCache is a single cache shared across all your scenes and classes. When you call this method:
[CCSpriteFrameCache sharedSpriteFrameCache]
You are not making a new CCSpriteFrameCache object everytime, there is just ONE instance. You store all your loaded spritesheets in this single cache. So you could load 2 spritesheets into the cache like so:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sheet1.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sheet2.plist"];
You then need to create a CCSpriteBatchNode for EACH spritesheet, you cannot have more than one sheet in a batch node:
CCSpriteBatchNode *spriteSheet1 = [CCSpriteBatchNode batchNodeWithFile:#"sheet1.pvr.ccz"];
CCSpriteBatchNode *spriteSheet2 = [CCSpriteBatchNode batchNodeWithFile:#"sheet2.pvr.ccz"];
You can then add both of these batch nodes to a layer if you wish. Sprites added to batch nodes must be from the spritesheet that batch node is using.

strange problem playing cocos2d animation from another class?

strange thing. if someone could please,please help me. its 3 days, and i think i am going to be fired :(
i have cocos2d class with animation function,and another xcode class.
if i call from the cocos2d class init function to animation function, the animation is being played when app is starts.
if i call from another class to the cocos2d class-animation, so it does the init function and enter the animation,but i cant see it playing.
so the animation is working only if called from within the cocos class only.
WHY ?
this is how i call the animation :
ran=[[HelloWorld alloc] init];
[ran animation];
this is the animation:
-(void)animation
{
//[self removeChild:background cleanup:YES];
//[b_pic.parent removeChild:b_pic cleanup:YES];
//animation
CCSpriteBatchNode *danceSheet = [ CCSpriteBatchNode batchNodeWithFile:#"head.png"];
[self addChild:danceSheet];
CCSprite *danceSprite = [CCSprite spriteWithTexture:danceSheet.texture rect:CGRectMake(0, 0, 480, 320)];
[danceSheet addChild:danceSprite];
//danceSprite.anchorPoint=CGPointMake(0, 0);
CGSize s = [[CCDirector sharedDirector] winSize];
danceSprite.position = ccp(s.width/2,s.height/2);
CCAnimation *danceAnimation = [CCAnimation animation];
[danceAnimation setDelay:0.1f];
int frameCount = 0;
for (int y = 0; y < 4; y++)
{
NSLog(#"%#",animation);
for (int x = 0; x < 5; x++)
{
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:danceSheet.texture rect:CGRectMake(x*320,y*440,320,440)];
[danceAnimation addFrame:frame];
frameCount++;
if (frameCount == 25)
break;
}
}
Can you call the animation function twice within that same class...Try
[self animation];
and
[[CCScheduler sharedScheduler] scheduleSelector:#selector(animation) forTarget:self
interval:10 paused:NO];
and see if the animation gets called the second time.
If it doesn't which at this point I don't think it will, you should define all that you can in the init and then only run the pieces you need in the animation method (i.e. add the batchNode and Sprite in the init as ivars and reuse them in the animation).
solved , and i dont know why.
but if you want to call a cocos2d function from the outside, you have to do :
[(HelloWorld*)[[[CCDirector sharedDirector] runningScene] getChildByTag:42] HardwareEvent:DollPart];
and tag your layer like :
layer.tag=42;
on the scene method !
thats the only way it works great.