I'm making a game in cocos2d, I have a chicken as character that use a CCSprite. This chicken have multiple images doing different movements.
There are so many images for the movements that I have to use multiple plist files for the sprite.
My problem is that I can switch between the differents sprites
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"chicken-1to3.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"chicken-4to5.plist"];
spriteSheet1to3 = [CCSpriteBatchNode
batchNodeWithFile:#"chicken-1to3.png"];
spriteSheet4to5 = [CCSpriteBatchNode
batchNodeWithFile:#"chicken-4to5.png"];
[self addChild:spriteSheet1to3 z:1];
[self addChild:spriteSheet4to5 z:2];
NSMutableArray *chickenManAni1Imgs = [NSMutableArray array];
NSMutableArray *chickenManAni2Imgs = [NSMutableArray array];
NSMutableArray *chickenManAni3Imgs = [NSMutableArray array];
NSMutableArray *chickenManAni4Imgs = [NSMutableArray array];
NSMutableArray *chickenManAni5Imgs = [NSMutableArray array];
for(int i = 1; i <= 9; ++i) {
[chickenManAni1Imgs addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"chicken-1-%d.png", i]]];
}
for(int i = 1; i <= 19; ++i) {
[chickenManAni2Imgs addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"chicken-2-%d.png", i]]];
}
for(int i = 1; i <= 21; ++i) {
[chickenManAni3Imgs addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"chicken-3-%d.png", i]]];
}
for(int i = 1; i <= 16; ++i) {
[chickenManAni4Imgs addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"chicken-4-%d.png", i]]];
}
for(int i = 1; i <= 36; ++i) {
[chickenManAni5Imgs addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"chicken-5-%d.png", i]]];
}
chickenManAni1 = [CCAnimation
animationWithSpriteFrames:chickenManAni1Imgs delay:0.04f];
chickenManAni2 = [CCAnimation
animationWithSpriteFrames:chickenManAni2Imgs delay:0.04f];
chickenManAni3 = [CCAnimation
animationWithSpriteFrames:chickenManAni3Imgs delay:0.04f];
chickenManAni4 = [CCAnimation
animationWithSpriteFrames:chickenManAni4Imgs delay:0.04f];
chickenManAni5 = [CCAnimation
animationWithSpriteFrames:chickenManAni5Imgs delay:0.04f];
chickenMan = [CCSprite spriteWithSpriteFrameName:#"chicken-5-1.png"];
chickenMan.position = ccp(winSize.width/2, winSize.height/2);
[spriteSheet4to5 addChild:chickenMan];
[chickenMan runAction:[CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:chickenManAni5 restoreOriginalFrame:NO]]];
chickenManAniRunning = 1;
Here is the code when I try to change the sprite
[chickenMan stopAllActions];
[spriteSheet4to5 removeChild:chickenMan cleanup:YES];
[spriteSheet1to3 addChild:chickenMan];
[chickenMan runAction:[CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:chickenManAni2 restoreOriginalFrame:NO]]];
After that I get this error:
'CCSprite is not using the same texture id'
UPDATE: I'm using two differents sprites:
chickenMan1to3 = [CCSprite spriteWithSpriteFrameName:#"chicken-1-1.png"];
chickenMan1to3.position = ccp(winSize.width/2, winSize.height/2);
[chickenMan1to3 setVisible:YES];
chickenMan4to5 = [CCSprite spriteWithSpriteFrameName:#"chicken-5-1.png"];
chickenMan4to5.position = ccp(winSize.width/2, winSize.height/2);
[chickenMan4to5 setVisible:NO];
And I switch them this way:
[chickenMan1to3 stopAllActions];
[chickenMan1to3 setVisible:NO];
[chickenMan4to5 setVisible:YES];
[chickenMan4to5 runAction:[CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:chickenManAni4 restoreOriginalFrame:NO]]];
use two sprites, one for each batch node. When switching to anim from the other batch node, set the animating sprite visible and hide the other. To make them animate in synch add both sprites to a ccnode which will then represent your chicken character as far as position, actions and other logic is concerned.
If the chicken is the only sprite child in the batch node you can simply remove the batch node since batching is only helping when you have muliple instances of the same sprite and texture.
Related
I have a Helper Class that sets up animation but the code keeps failing when i call CCCallBlock.
Here is the Helper Class:
+(CCSpriteBatchNode*)setupAnimation:(NSString*)spriteNameWithoutPNG :(int)numberOfFrames :(CCSprite*)spriteObj :(int)startRandomX :(int)endRandomX :(int)endDuration{
CGSize winSize = [[CCDirector sharedDirector] winSize];
NSString *plist = [NSString stringWithFormat:#"%#.plist",spriteNameWithoutPNG];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:plist];
NSString *spriteFileName = [NSString stringWithFormat:#"%#.png",spriteNameWithoutPNG];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:spriteFileName];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i=1; i <= numberOfFrames; i++){
NSString *frameName = [NSString stringWithFormat:#"%#%d.png",spriteNameWithoutPNG, i];
[animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:frameName]];
}
CCAnimation *moveAnim = [CCAnimation animationWithFrames:animFrames delay:0.1f];
NSString *spriteFrameName = [NSString stringWithFormat:#"%#1.png",spriteNameWithoutPNG];
spriteObj = [CCSprite spriteWithSpriteFrameName:spriteFrameName];
int x = arc4random() % startRandomX;
spriteObj.position = ccp(x, 480);
CCAction *actionFrames = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:moveAnim restoreOriginalFrame:NO]];
int q = arc4random() % endRandomX;
int r = arc4random() % endDuration;
CCAction *moveAction = [CCMoveTo actionWithDuration:r position:ccp(q, -50)];
id spriteObjRemove = [CCCallBlock actionWithBlock:^
{
[spriteObjRemove removeFromParentAndCleanup:YES];
//[asteroidArray removeObject:asteroid];
}];
id spriteSeq = [CCSequence actions:moveAction, spriteObjRemove, nil];
[spriteObj runAction:spriteSeq];
[spriteObj runAction:actionFrames];
[spriteSheet addChild:spriteObj];
return spriteSheet;
}
And this is how the code gets called in the parent:
-(void)asteroidCreate{
Asteroid *asteroid = [[Asteroid alloc] init:#"asteroid1.png"];
CCSpriteBatchNode *spritesheet = [Helper setupAnimation:#"asteroid" :8 :asteroid :320 :320 :10];
[self addChild:spriteSheet];
[self countDownToCreateNextAsteroid];
}
ERROR
Printing description of block:
<__NSStackBlock__: 0xbfffab90>
i doubt this is correct :
id spriteObjRemove = [CCCallBlock actionWithBlock:^
{
[spriteObjRemove removeFromParentAndCleanup:YES]; <=== using action instead of sprite
//[asteroidArray removeObject:asteroid];
}];
try
[spriteObj removeFromParentAndCleanup:YES];
I have tried to run multiple animation 1.Player will stand on screen loading 2.Then he starts running when his frames finish 3.Player will stop and Go action performed
problem is go action is executed before run action finishes
can any one help me to perform this task like player run 0.6 secs then he stops running action and Go action execute and after 4 sec it stops
- (void)didLoadFromCCB
{
CGSize winSize = [[CCDirector sharedDirector] viewSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"cc_player.plist"];
NSMutableArray *runAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 12; i++)
{
[runAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"cc_player/cc_player_run_pistol_%d.png", i]]];
}
NSMutableArray *goAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 11; i++)
{
[goAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"cc_player/cc_player_go_%d.png", i]]];
}
CCSprite *playerSprite = [CCSprite spriteWithImageNamed:#"cc_player/cc_player_idle_0.png"];
playerSprite.scale = 1.3;
playerSprite.position = CGPointMake(100, ( winSize.height / 2) - 60);
[self addChild:playerSprite z:3];
CCActionMoveTo *moveTo = [CCActionMoveTo actionWithDuration:8 position:CGPointMake(winSize.width+100 / 2, playerSprite.position.y)];
CCAnimation *runAnim = [CCAnimation animationWithSpriteFrames:runAnimFrames delay:0.06];
CCAnimation *goAnim = [CCAnimation animationWithSpriteFrames:goAnimFrames delay:0.06];
CCActionAnimate *runAnimationAction = [CCActionAnimate actionWithAnimation:runAnim];
CCActionAnimate *goAnimationAction = [CCActionAnimate actionWithAnimation:goAnim];
CCActionCallBlock *next=[CCActionCallBlock actionWithBlock:^
{
playerSprite.position = CGPointMake(playerSprite.position.x - 8, playerSprite.position.y + 6);
id goAction = [CCActionRepeat actionWithAction:goAnimationAction times:1];
CCActionCallBlock *nextStep=[CCActionCallBlock actionWithBlock:^
{
NSLog(#"***********not done");
// [playerSprite stopAllActions];
}];
CCActionSpawn *groupAction1 = [CCActionSpawn actionWithArray:#[goAction,nextStep]];
CCActionSequence *sequence2 = [CCActionSequence actionWithArray:#[groupAction1]];
// [playerSprite stopAllActions];
//CCActionSequence *goSequence = [CCActionSequence actions:moveTo,repeatingAnimation, nil];
[playerSprite runAction:sequence2];
}];
CCActionSpawn *groupAction = [CCActionSpawn actionWithArray:#[moveTo, next]];
CCActionSequence *sequence = [CCActionSequence actionWithArray:#[groupAction]];
[playerSprite runAction:sequence];
[playerSprite runAction:[CCActionRepeatForever actionWithAction:runAnimationAction]];
}
i have removed opticity line from code animation started showing now i have change sequance of my animation and its now working nice
(void)didLoadFromCCB
{
CGSize winSize = [[CCDirector sharedDirector] viewSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"cc_player.plist"];
NSMutableArray *runAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 12; i++)
{
[runAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"cc_player/cc_player_run_pistol_%d.png", i]]];
}
NSMutableArray *goAnimFrames = [NSMutableArray array];
for(int i = 0; i <= 11; i++)
{
[goAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"cc_player/cc_player_go_%d.png", i]]];
}
CCSprite *playerSprite = [CCSprite spriteWithImageNamed:#"cc_player/cc_player_idle_0.png"];
playerSprite.scale = 1.3;
playerSprite.position = CGPointMake(100, ( winSize.height / 2) - 60);
[self addChild:playerSprite z:3];
CCActionMoveTo *moveTo = [CCActionMoveTo actionWithDuration:3 position:CGPointMake(300, playerSprite.position.y)];
CCAnimation *runAnim = [CCAnimation animationWithSpriteFrames:runAnimFrames delay:0.06];
CCAnimation *goAnim = [CCAnimation animationWithSpriteFrames:goAnimFrames delay:0.06];
CCActionAnimate *runAnimationAction = [CCActionAnimate actionWithAnimation:runAnim];
CCActionAnimate *goAnimationAction = [CCActionAnimate actionWithAnimation:goAnim];
CCActionCallBlock *next=[CCActionCallBlock actionWithBlock:^
{
playerSprite.position = CGPointMake(playerSprite.position.x - 8, playerSprite.position.y + 6);
id goAction = [CCActionRepeat actionWithAction:goAnimationAction times:1];
CCActionCallBlock *nextStep=[CCActionCallBlock actionWithBlock:^
{
// [playerSprite stopAllActions];
}];
CCActionSpawn *groupAction1 = [CCActionSpawn actionWithArray:#[goAction,nextStep]];
CCActionSequence *sequence2 = [CCActionSequence actionWithArray:#[groupAction1]];
// [playerSprite stopAllActions];
//CCActionSequence *goSequence = [CCActionSequence actions:moveTo,repeatingAnimation, nil];
[playerSprite runAction:sequence2];
}];
[playerSprite runAction:[CCActionRepeatForever actionWithAction:runAnimationAction]];
CCActionSpawn *groupAction = [CCActionSpawn actionWithArray:#[moveTo,runAnimationAction]];
CCActionSequence *sequence = [CCActionSequence actionWithArray:#[groupAction,next]];
[playerSprite runAction:sequence];
}
I'm loading an animation using textures from a TextureAtlas.
But the animation does not appear on screen.
I appears only when I NSLog the Textures! Looks like a bug.
Does somebody made similar experience?
SKTextureAtlas *atlas = [SKTextureAtlas atlasWithRetinaCorrection:spriteSheetName_];
if ([atlas.textureNames count] == 0) return;
NSArray *r = [self getSprites:WEAPON_FIREANIMATION ofDict:atlas.textureNames];
if ([r count] == 0)return;
SKSpriteNode *firstSprite = (SKSpriteNode*)[self childNodeWithName:tFIRSTSPRITE];
[firstSprite removeActionForKey:aFIRE_ANIM];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
int k =0;
if (self.weaponAnimDuration == 0)
{
k = (self.weaponShootInterval / 0.1f)/[r count];
} else
{
k = (self.weaponAnimDuration / 0.1f)/[r count];
}
for (int i = 0; i<k; i++)
{
for (NSString *sname in r)
{
[walkAnimFrames addObject:[SKTexture textureWithImageNamed:sname]];
}
}
NSLog(#"%#",walkAnimFrames);
SKAction *weaponAnim = [SKAction animateWithTextures:walkAnimFrames timePerFrame:0.1f];
[firstSprite runAction:weaponAnim withKey:aFIRE_ANIM];
#import "SKTextureAtlas+MySKTextureAtlas.h"
#implementation SKTextureAtlas (MySKTextureAtlas)
+(SKTextureAtlas*)atlasWithRetinaCorrection:(NSString*)name
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00)
{
name = [NSString stringWithFormat:#"%##2x",name];
}
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:#"atlasc"];
if (path == Nil) return Nil;
return [SKTextureAtlas atlasNamed:name];
}
#implementation NSArray (Tools)
-(NSArray*)animationTextureFrames
{
NSMutableArray *rr = [NSMutableArray new];
for (NSString *name in self)
{
SKTexture *tex = [SKTexture textureWithImageNamed:name];
[rr addObject:tex];
}
return rr;
}
I found the bug!
I had to preload the textures.
[SKTexture preloadTextures:explosionTextures withCompletionHandler:^(void){}];
This is how I do mine, and havent had any issues yet.
NSMutableArray *textures = [NSMutableArray arrayWithCapacity:5];
for (int i = 1; i < 5; i++) {
NSString *textureName = [NSString stringWithFormat:#"rocket%d", i];
SKTexture *texture = [SKTexture textureWithImageNamed:textureName];
[textures addObject:texture];
}
self.rocketAnimation = [SKAction animateWithTextures:textures timePerFrame:0.1];
// add animation to my sprite
[self.projectile runAction:[SKAction repeatActionForever:self.rocketAnimation]];
// add spite to screen etc...
[self addChild:self.projectile];
See this post, there is a bug when images have a lot of transparent space using texture atlas.
SKTexture returns wrong size?
NSLog is very slow compared to most other operations. Just inserting that call adds a significant pause to the main thread of your app. I'm guessing that your texture loading is sending tasks off to the GPU. Without the NSLog call the GPU may not have enough time to complete the tasks. Maybe you can preload the textures.
Go to you'r Project and follow these steps:- 1> Go to Build Settings, 2> Search for Enable Texture Atlas Generation and select YES
I add one sprite per body and run animation, but when I try to remove this sprite after reloading scene(calling afterLoadProcessing once again) - removes the only first with animation. I'm new to iOS & Cococs2D programming, please help me with advice.
Here is my code:
-(void)afterLoadProcessing:(b2dJson*)json
{
[super afterLoadProcessing:json];
[self removeChild:enemysprite cleanup:YES];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"player_enemy.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"player_enemy.png"];
[self addChild:spriteSheet];
NSMutableArray *enemyGusenitsaAnimFrames = [NSMutableArray array];
for (int i = 31; i <=36; ++i) {
[enemyGusenitsaAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"%d.png",i]]];
}
CCAnimation *gusenitsaMovAnim = [CCAnimation animationWithSpriteFrames:enemyGusenitsaAnimFrames delay:0.1f];
_enemyGusenitsaMovement = [CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:gusenitsaMovAnim]];
_enemyGusenitsaMovement.tag = 109;
std::vector<b2Body*> enemySprites;
json->getBodiesByName("enemy", enemySprites);
for (int i = 0; i < enemySprites.size(); i++) {
b2Body *bod = enemySprites[i];
enemysprite = [[CCSprite alloc] init];
[enemysprite stopAllActions];
enemysprite.scale = 0.04;
[self addChild:enemysprite z:50];
bod->SetUserData(enemysprite);
[[enemysprite runAction:[_enemyGusenitsaMovement copy]] autorelease];
}
}
Instead of use [self removeChild:enemysprite cleanup:YES];, you can remove all children
[self removeAllChildrenWithCleanup:YES];
Or loop all bodies and remove it :
for (int i = 0; i < enemySprites.size(); i++) {
b2Body *bod = enemySprites[i];
CCSprite *sprite = (CCSprite *)bod->GetUserData();
[self removeChild:sprite];
}
This is the code for creating single sprite, then animating it:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"crowfl.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"crowfl.png"];
[self addChild:spriteSheet];
NSMutableArray *crowAnimFrames = [NSMutableArray array];
for (int i=1; i<=8; i++) {
[crowAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"crow%d.png", i]]];
}
CCAnimation *crowAnim = [CCAnimation animationWithSpriteFrames:crowAnimFrames delay:0.1f];
_crow = [CCSprite spriteWithSpriteFrameName:#"crow1.png"];
_crow.position = ccp(windowSize.width + _crow.contentSize.width/2, _crowFlightHeight);
id crowAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:crowAnim]];
[_crow runAction:crowAction];
[spriteSheet addChild:_crow];
...
id crowMoveAction = [CCSequence actions:
[CCMoveTo actionWithDuration:5.0 position:ccp(_flipCrow ? (windowSize.width + _crow.contentSize.width/2) : (-_crow.contentSize.width/2), _crowFlightHeight)],
crowMoveComplete,
nil];
[_crow runAction:crowMoveAction];
What I need is to create multiple sprites(_crow) and animate them asynchronously. I need to know what part of code can be reusable/shared among multiple sprites and what part should be unique for each animating sprite.
Cache animation in CCAnimationCache and that reduces delay in loading again and again:
CCAnimation* crowAnim = nil;
crowAnim = [[CCAnimationCache sharedAnimationCache] animationByName:#"crowAnim"];
if(!crowAnim)
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"crowfl.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"crowfl.png"];
[self addChild:spriteSheet];
NSMutableArray *crowAnimFrames = [NSMutableArray array];
for (int i=1; i<=8; i++) {
[crowAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"crow%d.png", i]]];
}
crowAnim = [CCAnimation animationWithSpriteFrames:crowAnimFrames delay:0.1f];
[[CCAnimationCache sharedAnimationCache] addAnimation:crowAnim name:#"crowAnim"];
}
_crow = [CCSprite spriteWithSpriteFrameName:#"crow1.png"];
_crow.position = ccp(windowSize.width + _crow.contentSize.width/2, _crowFlightHeight);
id crowAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:crowAnim]];
[_crow runAction:crowAction];
[spriteSheet addChild:_crow];
...
id crowMoveAction = [CCSequence actions:
[CCMoveTo actionWithDuration:5.0 position:ccp(_flipCrow ? (windowSize.width + _crow.contentSize.width/2) : (-_crow.contentSize.width/2), _crowFlightHeight)],
crowMoveComplete,
nil];
[_crow runAction:crowMoveAction];