CCAnimation crashes with unrecognized selector setDisplayFrame - ios

I don't know why but my animation does not work.
I am trying to add an animated unit now via sprite sheet but through a set of sprites.
And something goes wrong
Here is the code of init method:
#implementation Enemy
-(id) initAt:(CGPoint)pos
{
if([super init])
{
self.position = pos;
CCSprite *start_sprite = [CCSprite spriteWithFile:#"unit1_00000.png"];
start_sprite.position = ccp(0,0);
[self addChild:start_sprite z:2];
const int FRAMES_COUNT = 10;
NSMutableArray* frames = [[NSMutableArray alloc]initWithCapacity:FRAMES_COUNT];
for (int i = 0; i < FRAMES_COUNT; i++)
{
NSString* file = [NSString stringWithFormat:#"unit1_0000%i.png", i];
CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file];
CGSize texSize = texture.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect];
[frames addObject:frame];
}
_default_animation = [CCAnimation animationWithSpriteFrames:frames delay:0.1f];
_current_anim_action = [CCAnimate actionWithAnimation:_default_animation];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:_current_anim_action];
[self runAction:repeat];
}
return self;
}
The error is the following: -[Enemy setDisplayFrame:]: unrecognized selector sent to instance 0x8929bd0

setDisplayFrame is a method of CCSprite class, so in order to run a CCAnimation on Enemy class (which internally calls setDisplayFrame), Enemy must extend CCSprite, not CCNode.

Related

SpriteKit Animation Issue

I am having an issue with the sprite animation in my game.
In the current code posted below, the sprite will show on the screen but does not do the walking animation; it is only a static frame of one of the images in the walk cycle.
The init function:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize: size]) {
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// Create the game nodes
// Background
_backgroundNode = [self createBackgroundNode];
[self addChild: _backgroundNode];
// Foreground
_foregroundNode = [SKNode node];
[self addChild: _foregroundNode];
// Add the Player
_thePlayer = [self createPlayer];
[_foregroundNode addChild:_thePlayer];
[self walkingQueen];
}
return self;
}
Function creating the player / filling in the animation array:
-(SKSpriteNode *)createPlayer
{
SKSpriteNode *playerNode = [SKSpriteNode node];
//SKSpriteNode *_player = [SKSpriteNode node];
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenAnimatedAtlas = [SKTextureAtlas atlasNamed: #"QueenSprites"];
SKTexture *walk1 = [queenAnimatedAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenAnimatedAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenAnimatedAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
/*
int numImages = 9;
for (int i = 7; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"queen%d", i];
SKTexture *temp = [queenAnimatedAtlas textureNamed: textureName];
[walkFrames addObject: temp];
}
*/
//_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
SKSpriteNode *_player = [SKSpriteNode spriteNodeWithTexture:temp];
_player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
//[self addChild:_player];
//[self walkingQueen];
[playerNode addChild:_player];
[self walkingQueen];
return playerNode;
}
And the function for starting the walk animation:
-(void)walkingQueen
{
for (int i = 0; i < _queenWalkingFrames.count; i++) {
if (_queenWalkingFrames[i] == NULL) {
NSLog(#"ERROR!");
}
}
[_thePlayer runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:_queenWalkingFrames
timePerFrame:0.1f
resize:NO
restore:YES]] withKey:#"walkingInPlaceQueen"];
//return;
}
I played around with the code a bit and the animation DOES work IF I ignore the concept of layers (background / foreground layers) and stick the creation of the player into the init function (basically taking what's inside the createPlayer() and sticking it inside initWithSize()).
I do need to have the layers for my game and everything seems like it should work; had no idea that moving a few lines of code into its own function block would create such an issue. The check inside the walkingQueen() function did not return any errors so I assume my array has been filled with the sprite images.
_thePlayer = [self createPlayer];
This means _thePlayer is nil until after the createPlayer method returns. Thus in the walkingQueen method _thePlayer is nil and the actions don't run.
Replace this:
SKSpriteNode *playerNode = [SKSpriteNode node];
with
_thePlayer = [SKSpriteNode node];
and use _thePlayer instead of playerNode. You can also skip returning and assigning it, it's an ivar after all.
Re-wrote the createPlayer() function. Correct implementation is much simpler and as follows:
-(SKSpriteNode *)createPlayer
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenWalkingAtlas = [SKTextureAtlas atlasNamed:#"QueenSprites"];
SKTexture *walk1 = [queenWalkingAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenWalkingAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenWalkingAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
_thePlayer = [SKSpriteNode spriteNodeWithTexture:temp];
_thePlayer.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
return _thePlayer;
}
Basically doing as the post above suggested, get rid of the ivars, use the function ONLY to fill the array with the appropriate images. Then, in the init function, call createPlayer(), add it to the foreground layer, and then call walkingQueen().

Sprite Frame Animation Cocos2d 3.0

I've been trying to make an animated sprite, they're are a lot of tutorials but they're all for Cocos2d 2.x. My sprite sheet is named flappbird.png and the .plist is named flappbird.plist
I have this code but every time i start the scene it just crashes, this is in my init method
// -----------------------------------------------------------------------
_player = [CCSprite spriteWithImageNamed:#"monster1.png"]; // comes from your .plist file
_player.position = ccp(self.contentSize.width/28,self.contentSize.height/2);
_player.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _player.contentSize} cornerRadius:0]; // 1
_player.physicsBody.collisionGroup = #"playerGroup";
_player.physicsBody.type = CCPhysicsBodyTypeStatic;
CCSpriteBatchNode *batchNode = [CCSpriteBatchNode batchNodeWithFile:#"monster1.png"];
[batchNode addChild:_player];
[self addChild:batchNode];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 1; i < 5; i++)
{
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"flapbird%d.png",i]];
[animFrames addObject:frame];
}
CCAnimation *animation = [CCAnimation animationWithSpriteFrames:animFrames delay:0.2f];
[_player runAction:[CCActionRepeatForever actionWithAction:[CCActionAnimate actionWithAnimation:animation]]];
[_physicsWorld addChild:_player];
// -----------------------------------------------------------------------
Animate sprite with spritesheet in Cocos2d 3.0
Make sure to add #import "CCAnimation.h" at the beginning of your code
Also add the sprite sheet after the self.userInteractionEnabled = YES; in the init
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"your.plist"];
No add all this where the sprite will be
//The sprite animation
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 7; ++i)
{
[walkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"monster%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation
animationWithSpriteFrames:walkAnimFrames delay:0.1f]; //Speed in which the frames will go at
//Adding png to sprite
monstertest = [CCSprite spriteWithImageNamed:#"monster1.png"];
//Positioning the sprite
monstertest.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
//Repeating the sprite animation
CCActionAnimate *animationAction = [CCActionAnimate actionWithAnimation:walkAnim];
CCActionRepeatForever *repeatingAnimation = [CCActionRepeatForever actionWithAction:animationAction];
//Animation continuously repeating
[monstertest runAction:repeatingAnimation];
//Adding the Sprite to the Scene
[self addChild:monstertest];
Hope this helps somebody :D Cheers

Adding a stream of sprites using a CCSpriteBatchNode in cocos2d?

So I created a class called Berries that extends CCSpriteBatchNode. The Berries are basically like coins in my game, adding to the score value if 'collided' with. This is the code I have in my Berries class for CCSpriteBatchNode:
- (id) init
{
if((self = [super init]))
{
CCSpriteBatchNode* berryBatch1 = [CCSpriteBatchNode batchNodeWithFile:#"One.png"];
[self addChild:berryBatch1];
CCSpriteBatchNode* berryBatch2 = [CCSpriteBatchNode batchNodeWithFile:#"Two.png"];
[self addChild:berryBatch2];
CCSpriteBatchNode* berryBatch3 = [CCSpriteBatchNode batchNodeWithFile:#"Three.png"];
[self addChild:berryBatch3];
for (int i = 0; i < 100; i++)
{
CCSprite* berry1 = [CCSprite spriteWithFile: #"One.png"];
[berryBatch1 addChild:berry1];
CCSprite* berry2 = [CCSprite spriteWithFile: #"Two.png"];
[berryBatch2 addChild:berry2];
CCSprite* berry3 = [CCSprite spriteWithFile: #"Three.png"];
[berryBatch3 addChild:berry3];
}
numBerries =
}
return self;
}
numBerries is an int that I made. I am trying to figure out the number of berries in all three CCSpriteBatchNodes so that when I make a detectCollision method I can use it in the for loop.
Any ideas?
It's simple:
numBerries = berryBatch1.children.count +
berryBatch2.children.count +
berryBatch3.children.count;

game crashes 3-5 big sprites for background (1920*640 ~100kb each) ~ 400-500kb in each level

I've got a problem in my cocos2d game: In the Game's level layer I've 3-5 big sprites for background (1920*640 ~100kb each) ~ 400-500kb in each level. When I switch between menu and various game's level 4-5 times the game crashes to main IPhone menu
[[CCDirector sharedDirector] replaceScene:transition];
without this big sprites all work perfect!
-(id) init
{
....
if( (self=[super init])) {
_spriteBGLevel1 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_1.png", _currentLevel]];
_spriteBGLevel1.anchorPoint = ccp(0, 0);
_spriteBGLevel1.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel1 z:-5];
_spriteBGLevel2 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_2.png", _currentLevel]];
_spriteBGLevel2.anchorPoint = ccp(0, 0);
_spriteBGLevel2.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel2 z:-5];
_spriteBGLevel3 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_3.png", _currentLevel]];
_spriteBGLevel3.anchorPoint = ccp(0, 0);
_spriteBGLevel3.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel3 z:-5];
....
- (void) dealloc
{
....
_spriteBGLevel1=nil;
_spriteBGLevel2=nil;
_spriteBGLevel3=nil;
[super dealloc];
}
Background.m
#import "Background.h"
#import "GameConfig.h"
#import "XMLReader.h"
#import "CCParallaxNode-Extras.h"
#import "SettingsManager.h"
#implementation Background
#synthesize backgrounds=_backgrounds;
#synthesize backgroundNode=_backgroundNode;
+(id) scene
{
CCScene *scene = [CCScene node];
Background *layer = [Background node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
int currentLevel = [[SettingsManager sharedSettingsManager] getCurrentLevel];
_backgroundNode = [CCParallaxNode node];
_backgroundNode.anchorPoint = CGPointMake(0, 0);
_backgroundNode.position = CGPointMake(0, 0);
[self addChild:_backgroundNode z:-1];
NSData *xmlData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:#"gameScene" ofType:#"xml"]];
NSError *error = nil;
NSDictionary *dictionary = [XMLReader dictionaryForXMLData:xmlData error:&error];
NSDictionary *levelsDict = [dictionary valueForKeyPath:#"levels.level"];
NSDictionary *levelDict;
_backgrounds = [[[NSMutableArray alloc] init] retain];
// [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: [NSString stringWithFormat:#"level_fon_%i.plist", currentLevel]];
for (levelDict in levelsDict)
{
int idLevel = [[levelDict valueForKeyPath:#"id"] intValue];
if(idLevel==currentLevel)
{
NSDictionary *fonsDict = [levelDict valueForKeyPath:#"background.fon"];
NSDictionary *fonDict;
for (fonDict in fonsDict){
NSString *name=[fonDict valueForKeyPath:#"name"];
int zIndex=[[fonDict valueForKeyPath:#"z"] intValue];
float ratio=[[fonDict valueForKeyPath:#"ratio"] floatValue];
float offsetx=[[fonDict valueForKeyPath:#"offsetx"] floatValue];
float offsety=[[fonDict valueForKeyPath:#"offsety"] floatValue];
if(zIndex<0)
{
//CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:[NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_1 = [CCSprite spriteWithSpriteFrameName: [NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_2 = [CCSprite spriteWithSpriteFrameName: [NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_1 = [CCSprite spriteWithTexture:tex];
//CCSprite *fon_level_2 = [CCSprite spriteWithTexture:tex];
fon_level_1 = [CCSprite spriteWithFile:[NSString stringWithFormat:#"%#", name]];
fon_level_2 = [CCSprite spriteWithFile:[NSString stringWithFormat:#"%#", name]];
fon_level_1.anchorPoint = CGPointMake(0, 0);
fon_level_1.position = CGPointMake(0, 0);
fon_level_2.anchorPoint = CGPointMake(0, 0);
fon_level_2.position = CGPointMake(0, 0);
//[_backgroundNode addChild:fon_level_1 z:zIndex parallaxRatio:ccp(ratio, ratio) positionOffset:ccp(offsetx, offsety*screenSize.height)];
//[_backgroundNode addChild:fon_level_2 z:zIndex parallaxRatio:ccp(ratio, ratio) positionOffset:ccp(fon_level_1.contentSize.width, offsety*screenSize.height)];
[_backgrounds addObject:fon_level_1];
[_backgrounds addObject:fon_level_2];
fon_level_1=nil;
fon_level_2=nil;
}
}
break;
}
}
NSLog(#"count: %d",_backgrounds.count);
[self scheduleUpdate];
}
return self;
}
- (void)scheduleUpdate
{
[self schedule:#selector(updateBackgroud:)];
}
-(void) updateBackgroud:(ccTime)delta
{
CGPoint backgroundScrollVel = ccp(-1000, 0);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, delta));
for (CCSprite *background in _backgrounds) {
if (([_backgroundNode convertToWorldSpace:background.position].x+background.contentSize.width/10) < -(background.contentSize.width)) {
[_backgroundNode incrementOffset:ccp(background.contentSize.width*2,0) forChild:background];
}
}
}
- (void) dealloc
{
_backgroundNode = nil;
// _backgrounds = nil;
[_backgrounds removeAllChildren];
[_backgrounds release];
[super dealloc];
}
#end
Well, here's how I have gone about it:
Get as close as possible to a POT texture size. 1920x640 costs you as much memory as a 2048x2048. Maybe you could get your artists to combine the three-five sprites on a single 2048x2848 png, crop with an plist (like for animation).
Whenever possible, load textures 'just in time', ie before they are required to be visible. If load time became an issue (perceivable lag at an inappropriate game circumstance), convert the texture to .pvr format (faster load), later to .pvr.gz (still faster load time, smaller app download size). PVR will cause some artefacts, so check with graphic people on your project before comiting.
Coming out of any level transition (either to some kind of menu or to some cut-scene), cleanup memory used by textures. This means that if the game logic requires returning to the main scene, reload textures just-in-time, during the transition.
Last ditch, change the depth setting before loading these large textures (code snippet below). Setting to RGBA444 will save 75% of the memory requirement, BUT, you take a hit on some graphic quality, especially for high saturation images. Once again, if you need to go there, get your graphics people to optimize the images to this depth, so they are satisfied with the result on device.
cleanup:
NSLog(#"GESprite<removeUnusedSprites> : before purging all caches");
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[[CCDirector sharedDirector] purgeCachedData];
NSLog(#"GESprite<removeUnusedSprites> : after purging all caches");
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
depth (in a CCSprite derivative class, some utility methods), you can do this anywhere anytime:
+(void) mediumPixelFormat{
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA4444];
}
+(void) highPixelFormat{
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
}

ios/cocos2d how to make menuitem to disappear after being activated?

i"m new here :)
my first question :
i"m trying to make simple interaction ,- after menuitem is being pressed , i"m starting spritesheet animation of it.
look"s like i can"t straightly animate menuitem with spritesheet (but i think it wou"d be right)
also i can`t do -
menuItem.visible =false:
in function , that is activated by that button
also i can`t reOrder menuitem to z:-10
here is my code:
.h :
CCSprite *p1S2;
CCAction *p1A2;
.m :
#interface FirstPage ()
#property (nonatomic, retain) CCAction *p1A2;
#property (nonatomic, retain) CCSprite *p1S2;
#end
#implementation FirstPage
#synthesize p1S2 = _p1S2;
#synthesize p1A2 = _p1A2;
- (id)init
{
if ((self = [super init]))
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
bg1 = [CCSprite spriteWithFile:#"st_01_bg1.png"];
bg1.position = ccp(screenSize.width/2, 512);
[self addChild:bg1];
bg2 = [CCSprite spriteWithFile:#"st_01_bg2.png"];
bg2.position = ccp(screenSize.width/2, 128);
[self addChild:bg2 ];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"button.plist"];
CCSpriteBatchNode *spriteSheet2 = [CCSpriteBatchNode
batchNodeWithFile:#"button.png"];
[self addChild:spriteSheet2 z:6];
NSMutableArray *AnimFrames2 = [NSMutableArray array];
for(int i = 1; i <= 30; ++i) {
[AnimFrames2 addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"button%d.png", i]]];
}
CCAnimation *becomeAnim2 = [CCAnimation animationWithFrames:AnimFrames2 delay:0.04f];
self.p1S2 = [CCSprite spriteWithSpriteFrameName:#"rostok1.png"];
_p1S2.position = ccp(638,530);
self.p1A2=[CCSequence actions:[CCRepeat actionWithAction:[CCAnimate actionWithAnimation:becomeAnim2 restoreOriginalFrame:NO]times:200],nil];
[spriteSheet2 addChild:_p1S2 z:6];
[_p1S2 runAction:_p1A2];
_p1S2.visible=false;
CCMenuItem *menuItem1 = [CCMenuItemImage itemFromNormalImage:#"knopka_01_lisik.png"
selectedImage:#"knopka_02_lisik.png" target:self selector:#selector(button1Tapped:)];
CCMenu *Menu = [CCMenu menuWithItems:menuItem1, nil];
Menu.position = ccp(638,530);
[Menu alignItemsHorizontally];
[self addChild:Menu z:7];
}
return self;
}
- (void)button1Tapped:(id)sender {
_p1S2.visible = true;
}
}
#end
any advices on how to do it right ?
What you are doing is using an image for your menu item. You can use different things instead of that image, such as labels, or sprites. Give this a try:
CCMenuItemSprite *sprite1 = [CCMenuItemSprite itemFromNormalSprite:#"Your Sprite.png" selectedSprite:#"YourSelectedSrite.png" target:self selector:#selector(animation)];
You have your default sprite, then when it is selected your selected sprite, then the target (usually self), and then the selector. It is in that selector that you need to put in your animation code, and whatever code you want.

Resources