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;
Related
didBeginContact doesnt get called for some reason. How can I fix this? Thanks! I set the category bitmasks and the skphysicsContactDelegate, yet it is still not registering contacts. I've been stuck at this for some time now.
#import "MyScene.h"
#import "FuelNode.h"
#import "SKSpriteNode+DebugDraw.h"
typedef NS_OPTIONS(uint32_t, CollisionCategory) {
CollisionCategoryPlayer = 1 << 0,
CollisionCategoryFuel = 1 << 1,
};
#interface MyScene() <SKPhysicsContactDelegate>
#end
#implementation MyScene
{
SKNode *_playerNode;
SKNode *_backgroundNode;
SKNode *_foreGround;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
_backgroundNode = [self createBackground];
[self addChild:_backgroundNode];
_foreGround = [SKNode node];
[self addChild:_foreGround];
//add a fuelNode
FuelNode *fuel = [self createFuelAtPosition:CGPointMake(160, 440)];
[_foreGround addChild:fuel];
_playerNode = [self createPlayer];
[_foreGround addChild:_playerNode];
SKAction *actionMove = [SKAction moveToY:-100 duration:3.0];
[fuel runAction:actionMove];
NSLog(#"yea");
}
return self;
}
-(SKNode *)createPlayer
{
CGSize playerPhysicsBody;
//Create player
SKNode *playerNode = [SKNode node];
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:#"wship-3.png"];
player.position = CGPointMake(self.size.width/2, 120);
[playerNode addChild:player];
//Add physics
playerPhysicsBody = CGSizeMake(player.size.width/2, player.size.height/2);
playerNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:playerPhysicsBody];
playerNode.physicsBody.dynamic = NO;
//Setup collision settings
playerNode.physicsBody.usesPreciseCollisionDetection = YES;
playerNode.physicsBody.categoryBitMask = CollisionCategoryPlayer;
playerNode.physicsBody.collisionBitMask = 0;
playerNode.physicsBody.contactTestBitMask = CollisionCategoryFuel;
[player attachDebugRectWithSize:playerPhysicsBody];
return playerNode;
}
-(SKNode *)createBackground
{
//Create background
SKNode *bgNode = [SKNode node];
SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:#"purple"];
bg.anchorPoint = CGPointZero;
[bgNode addChild:bg];
return bgNode;
}
- (FuelNode *)createFuelAtPosition:(CGPoint)position
{
// 1
FuelNode *node = [FuelNode node];
[node setPosition:position];
[node setName:#"NODE_FUEL"];
// 2
SKSpriteNode *sprite;
sprite = [SKSpriteNode spriteNodeWithImageNamed:#"fuelBlue"];
[node addChild:sprite];
// 3
CGSize contactSize = CGSizeMake(sprite.size.width/2, sprite.size.height/2);
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
// 4
node.physicsBody.dynamic = NO;
//Setup collision settings
node.physicsBody.categoryBitMask = CollisionCategoryFuel;
node.physicsBody.collisionBitMask = 0;
//node.physicsBody.contactTestBitMask = CollisionCategoryPlayer;
[sprite attachDebugRectWithSize:contactSize];
//SKAction *actionMove = [SKAction moveToY:-100 duration:3.0];
//[node runAction:actionMove];
return node;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
BOOL fuelCollision = NO;
SKNode *other = (contact.bodyA.node != _playerNode) ? contact.bodyA.node : contact.bodyB.node;
NSLog(#"collision");
fuelCollision = [(GameObjectNode *)other collisionWithPlayer:_playerNode];
}
#end
In order to enable contact detection, you need to set
node.physicsBody.dynamic = YES;
Have a look at the documentation as well.
Fixed! In the create player method I have mistakenly set the position of the player sprite instead of setting the position of the player node!
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have just started developing a game, new to Objective-c. Basically at the moment you can move the spaceship around and the spaceship fires continually and there are asteroids coming from the top of screen. But my app freezes after a minute every time why is this?
CODE
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
moveLeft = NO;
moveRight = NO;
speed = 3;
fireSpeed = 8;
fireArray = [[NSMutableArray alloc] init];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *bg = [[CCSprite alloc] initWithFile:#"space_bg.jpg"];
[bg setPosition:ccp(160, 240)];
CGSize imageSize = bg.contentSize;
bg.scaleX = winSize.width / imageSize.width;
bg.scaleY = winSize.height / imageSize.height;
bg.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:bg];
ship = [[CCSprite alloc] initWithFile:#"ship.png"];
[ship setPosition:ccp(100, 100)];
[self addChild:ship];
[self schedule:#selector(fireLoop:)];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(fireCreate)
userInfo:nil
repeats:YES];
int t = arc4random() % 5;
timer3 = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
return self;
}
NSTimer *timer3;
NSTimer *timer2;
NSTimer *tmpTimer;
-(void)asteroidTimer {
int t = arc4random() % 10;
timer2 = [NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:#selector(asteroidCreate)
userInfo:nil
repeats:YES];
}
-(void)asteroidCreate {
[timer2 invalidate];
[tmpTimer invalidate];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"asteroids.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"asteroids.png"];
NSMutableArray *asteroidAnimFrames = [NSMutableArray array];
for(int i=1; i <= 8; i++) {
[asteroidAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"asteroid%d.png", i]]];
}
CCAnimation *moveAsteroidAnim = [CCAnimation animationWithFrames:asteroidAnimFrames delay:0.1f];
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"asteroid1.png"];
int x = arc4random() % 320;
int y = arc4random() % 480;
asteroid.position = ccp(x, 480);
CCAction *asteroidAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:moveAsteroidAnim restoreOriginalFrame:NO]];
int q = arc4random() % 320;
int r = arc4random() % 10;
CCAction *moveAction = [CCMoveTo actionWithDuration:r position:ccp(q, -50)];
[asteroid runAction:moveAction];
[asteroid runAction:asteroidAction];
[spriteSheet addChild:asteroid];
[self addChild:spriteSheet];
int t = arc4random() % 10;
tmpTimer = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
-(void)fireLoop:(ccTime)fl {
if(fireArray.count > 0) {
for(int i = 0; i < fireArray.count; i++){
CCSprite *tmpFire = [fireArray objectAtIndex:i];
if(tmpFire.position.y < 500){
[tmpFire setPosition:ccp([tmpFire position].x, [tmpFire position].y + fireSpeed)];
}else{
[fireArray removeObjectAtIndex:i];
}
}
} else
{
}
}
-(void)fireCreate {
int shootPositionX = [ship position].x;
int shootPositionY = ([ship position].y) + 35;
CCSprite *fire;
fire = [[CCSprite alloc] initWithFile:#"fire.png"];
[fire setPosition:ccp(shootPositionX, shootPositionY)];
[fireArray addObject:fire];
[self addChild:fire];
[fire release];
}
-(void)gameLoop:(ccTime)dt {
int shipPositionX = 41/2;
if([ship position].x > shipPositionX){
[ship setPosition:ccp([ship position].x - speed, [ship position].y)];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
//if(point.x <= 160){
// moveRight = NO;
// moveLeft = YES;
//}else{
// moveRight =YES;
// moveLeft = NO;
//}
if(allowedToMove)
[ship setPosition:ccp(point.x, point.y + 76)];
}
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
int shipX = [ship position].x;
int shipY = [ship position].y;
if (CGRectContainsPoint(CGRectMake (shipX - 20.5, shipY - 96, 50, 50), point))
{
allowedToMove = true;
}
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
pointXLeft = point.x;
pointYLeft = point.y;
allowedToMove = false;
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
In general track down what seems to be causing it. What does the debugger say? Did you stop the execution or set a breakpoint somewhere that might be problematic? What about exception breakpoints? If those don't work take out your timers and see if you have any change. What about your fire methods?
Also your use of timers look weird and should be the first area you look. Side note, for the asteroids why are you not running the two actions you want to run on the new asteroid using CCSpawn?
In addition are you ever removing projectiles that you shoot from the screen or the asteroids that are no longer needed? If not you should.
But to answer your question the problem looks most likely a timer issue. Your use of timers are wrong. You have timer-3 call asteroid timer, which in turns creates timer-2 that calls asteroid create, which in turns invalidates timer-2 (that had an interval of 0 which makes it look a little unnecessary) and also creates a tmp-timer that calls asteroid create. Timer 3 is left to continuously do what it wants, which is all the steps above, at every x number of seconds. Even though tmp-timer seems to be trying to spawn more asteroid creations and take over the process, instead that looks both incorrect and odd. And you are doing this with a few global timers that can, seemingly have a valid timer but is overridden by a new allocation, just to be invalidated by another method randomly soon after even though what it was trying to invalidate technically was leaked and no longer has a pointer to it, thus invalidating the wrong instance to the wrong timer. Keep in mind your tmp-timer and timer-3 has random intervals. If tmp-timer has an interval of 3 seconds and timer-3 has a 9 second interval at some point something looks like it is bound to go wrong. It is hard to say by glancing at it just how, but it looks inevitable.
Or at least I think that is what is happening. Again the code makes very weird and potentially error prone use of timers.
Also timer-1 is going to keep creating sprites unchecked without those sprites ever going away. Something to think about for sure.
Edit (Solution):
If I understand what you are trying to do you want to create timers every variable amount of seconds that, at the end, creates a new asteroid. There are two things that stand out with this. You want something to call the creation of an asteroid, and something else to do the actual create:
- (void)countDownToCreateNextAsteroid
{
int minTime = 1;
int maxTime = 10;
int randomTime = (arc4random() % (maxTime - minTime)) + minTime;
id countdownDelay = [CCDelayTime actionWithDuration:randomTime];
id creationMethod = [CCCallFunc actionWithTarget:self selector:#selector(createAsteroid)];
id countdownToCreateSeq = [CCSequence actions:countdownDelay, creationMethod, nil];
[self stopAllActions];
[self runAction:countdownToCreateSeq];
}
- (void)createAsteroid
{
CGPoint someStartPosition = ccp(1024.0f, 512.0f);
CCSprite* asteroid = [CCSprite spriteWithFile:#"someImage.png"];
asteroid.position = someStartPosition;
// asterdoid dot yada yada = ....
[self addChild:asteroid];
float someDuration = 10.0f;
CGPoint someOffscreenPos = ccp(-1024.0f, 512.0f);
// Move off screen, then remove yourself...
id asteroidMove = [CCMoveTo actionWithDuration:someDuration
position:someOffscreenPos];
id asteroidRemove = [CCCallBlock actionWithBlock:^
{
[asteroid removeFromParentAndCleanup:YES];
}];
id asteroidSeq = [CCSequence actions:asteroidMove, asteroidRemove, nil];
[asteroid runAction:asteroidSeq];
[self countDownToCreateNextAsteroid];
}
Therefore in your onEnter you could have something to kick it all off and that's it:
- (void)onEnter
{
[super onEnter];
// yada yada
[self countDownToCreateNextAsteroid];
}
And the rest would take care of itself. This assumes this quick and simple approach is all you were after. The count down method sets up the count down, calls a method to create an asteroid at the end of that time, then exits. After the asteroid is created a new call to the count down method is invoked, which will setup another asteroid to be created at another random time. This is one quick and simple way to do what it looks you are were actually trying to accomplish with all those timers.
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().
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
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.