2

I'm following along with the Lynda.com iOS tutorial for game development which uses cocos2d (version 2). The instructor uses the following code to make the sprite image increase in size upon a touch event. The log statement in the code below is working (i.e. touch events are enabled) but the image is not getting larger when I click on the simulator. It turns out that mole in this line is nil

CCSprite *mole =  (CCSprite *)[self getChildByTag:1];

Here's the whole method

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView: [touch view]];
    location = [[CCDirector sharedDirector] convertToGL: location ];
    CCLOG(@"touch happened at x: %0.2f y: %0.2f",location.x, location.y );
    CCSprite *mole =  (CCSprite *)[self getChildByTag:1];

    if (CGRectContainsPoint([mole boundingBox], location)) {
        [mole runAction:[CCSequence actions:
                         [CCScaleTo actionWithDuration:.25 scale:1.1],
                         [CCScaleTo actionWithDuration:.25 scale:1.0],
                         nil]];
    }
}

The mole is appearing on the screen with this init method below. I'm assuming the line from above CCSprite *mole = (CCSprite *)[self getChildByTag:1]; is supposed to get a reference to the mole as a child but it's not obviously working.

-(id) init
{
    // always call "super" init
    // Apple recommends to re-assign "self" with the "super" return value
    if( (self=[super init])) {
        self.isTouchEnabled = YES;
        CGSize s = [[CCDirector sharedDirector] winSize];
        CCSprite *mole = [CCSprite spriteWithFile:@"mole.png"];
        mole.position = ccp(s.width/2,s.height/2);
        [self addChild:mole];
        mole.tag = 1;

        CCSprite *bg = [CCSprite spriteWithFile:@"bg.png"];
        bg.anchorPoint = ccp(0,0);
        [self addChild:bg z:-1];
    }
    return self;
}

Can you tell from the above code why mole here is nil?

 CCSprite *mole =  (CCSprite *)[self getChildByTag:1];

Update

HelloWorldLayer.h

#import <GameKit/GameKit.h>

// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"

// HelloWorldLayer
@interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
}

// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;

@end

HelloWorldLayer.m

// Import the interfaces
#import "HelloWorldLayer.h"

// Needed to obtain the Navigation Controller
#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
{
    // always call "super" init
    // Apple recommends to re-assign "self" with the "super's" return value
    if( (self=[super init]) ) {

        self.isTouchEnabled = YES;

        // create and initialize a Label
//      CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
//
//      // ask director for the window size
//      CGSize size = [[CCDirector sharedDirector] winSize];
//
//      // position the label on the center of the screen
//      label.position =  ccp( size.width /2 , size.height/2 );
//      
//      // add the label as a child to this Layer
//      [self addChild: label];

        CGSize s = [[CCDirector sharedDirector] winSize];
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"moles.plist"];
        CCSprite *mole = [CCSprite spriteWithSpriteFrameName:@"a0010.png"];
//      CCSprite *mole = [CCSprite spriteWithFile:@"mole.png"];
        mole.position = ccp(s.width/2,s.height/2);
        mole.scale = .25;
        //between 0 255
//        mole.opacity = 100;
        NSMutableArray *frames = [[NSMutableArray alloc] init];
        for (int i = 1; i <= 10; i++) {
            NSString *frameName = [NSString stringWithFormat:@"a%04i.png",i];
            [frames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:frameName]];
        }
        CCAnimation *a = [CCAnimation animationWithFrames:frames delay:1.0f/24.0f];
        [mole runAction:[CCAnimate actionWithAnimation:a restoreOriginalFrame:NO]];
        [self addChild: mole];


        CCSprite *bg = [CCSprite spriteWithFile:@"bg.png"];
        //supposed to fill whole screen but not

        //got next three lines from SO
    //stackoverflow.com/questions/12383228/how-to-set-background-image-for-the-entire-scene-in-cocos2d-xcode
        CGSize imageSize = bg.contentSize;
        bg.scaleX = s.width/ imageSize.width;
        bg.scaleY = s.height/ imageSize.height;

        bg.anchorPoint = ccp(0,0);
        [self addChild:bg z:-1];
        //
        // Leaderboards and Achievements
        // *

//      // Default font size will be 28 points.
//      [CCMenuItemFont setFontSize:28];
//      
//      // to avoid a retain-cycle with the menuitem and blocks
//      __block id copy_self = self;
//      
//      // Achievement Menu Item using blocks
//      CCMenuItem *itemAchievement = [CCMenuItemFont itemWithString:@"Achievements" block:^(id sender) {
//          
//          
//          GKAchievementViewController *achivementViewController = [[GKAchievementViewController alloc] init];
//          achivementViewController.achievementDelegate = copy_self;
//          
//          AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
//          
//          [[app navController] presentModalViewController:achivementViewController animated:YES];
//          
//          [achivementViewController release];
//      }];
//      
//      // Leaderboard Menu Item using blocks
//      CCMenuItem *itemLeaderboard = [CCMenuItemFont itemWithString:@"Leaderboard" block:^(id sender) {
//          
//          
//          GKLeaderboardViewController *leaderboardViewController = [[GKLeaderboardViewController alloc] init];
//          leaderboardViewController.leaderboardDelegate = copy_self;
//          
//          AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
//          
//          [[app navController] presentModalViewController:leaderboardViewController animated:YES];
//          
//          [leaderboardViewController release];
//      }];
//
//      
//      CCMenu *menu = [CCMenu menuWithItems:itemAchievement, itemLeaderboard, nil];
//      
//      [menu alignItemsHorizontallyWithPadding:20];
//      [menu setPosition:ccp( size.width/2, size.height/2 - 50)];
//      
//      // Add the menu to the layer
//      [self addChild:menu];

    }
    return self;
}

//- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
//{
//    acceleration.x
//}

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView: [touch view]];
    location = [[CCDirector sharedDirector] convertToGL: location ];
    CCLOG(@"touch happened at x: %0.2f y: %0.2f",location.x, location.y );
    CCSprite *mole =  (CCSprite *)[self getChildByTag:1 ];
    NSLog(@"mole %@", mole);
//    if (CGRectContainsPoint([mole boundingBox], location))
//    {
//        [mole runAction: [CCScaleBy actionWithDuration:.25 scale:1.1]];
//        
//    }
    if (CGRectContainsPoint([mole boundingBox], location)) {
        [mole runAction:[CCSequence actions:
                         [CCScaleTo actionWithDuration:.25 scale:1.1],
                         [CCScaleTo actionWithDuration:.25 scale:1.0],
                         nil]];
    }
}

// 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];
}

#pragma mark GameKit delegate

-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
    AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
    [[app navController] dismissModalViewControllerAnimated:YES];
}

-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
    AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
    [[app navController] dismissModalViewControllerAnimated:YES];
}
@end
BrainLikeADullPencil
  • 11,313
  • 24
  • 78
  • 134
  • Have you checked you `mole`? Is it nil ? Also the animation you are giving is very less prominent. Change scale `1.1` to `2.0` – Inder Kumar Rathore Mar 11 '14 at 18:30
  • @InderKumarRathore yes, thanks, mole is `nil`, but I don't know why. Just copied the code from the tutorial. The mole is on the screen. Do you see a problem with this line `CCSprite *mole = (CCSprite *)[self getChildByTag:1];` – BrainLikeADullPencil Mar 11 '14 at 21:00
  • @InderKumarRathore I changed the title and body of the question to reflect fact that mole is nil – BrainLikeADullPencil Mar 11 '14 at 21:08
  • 1
    The code here seems to be OK from first sight. Would you mind posting your whole project? – Tibor Udvari Mar 13 '14 at 19:03
  • I've had problems in the past setting tags to '1' because other views that I hadn't explicitly tagged, also had the tag '1'. Would you mind changing it to something like 14562 and see if it still has a problem? – Logan Mar 13 '14 at 22:38
  • @Logan tried it, doesn't fix the problem. Thanks for the suggestion – BrainLikeADullPencil Mar 14 '14 at 03:09
  • @TiborUdvari I posted a lot more code. The code I didn't post is just boilerplate cocos2d from the rootviewcontroller and game config – BrainLikeADullPencil Mar 14 '14 at 03:13
  • in your updated code(HelloWorldLayer.m), it doesn't appear that you are setting the tag on the mole sprite, so your getChildByTag will not return a sprite. As a side note; unless you are going to be dynamically changing the tag later, I would set up a `#define kMoleSprite 1234` or similar and use that to tag and later reference the sprite tag. Also, just for debugging, I would substitute the `getChildByTag` call with your own loop just to inspect the current tags of the children of the current node. – Mark Mar 14 '14 at 14:18
  • Are you running this with ARC? Is it possible that your object is being released from memory? – PWiggin Mar 14 '14 at 14:53
  • Yep in your new code you forgot to set the tag. Anyhow please consider using Cocos2d v3 for your future projects. – Tibor Udvari Mar 15 '14 at 19:58

3 Answers3

4
  // set the tag value mole sprite in init method (HelloWorldLayer.m).

   CCSprite *mole = [CCSprite spriteWithSpriteFrameName:@"a0010.png"];
    //      CCSprite *mole = [CCSprite spriteWithFile:@"mole.png"];
    mole.position = ccp(s.width/2,s.height/2);
    mole.scale = .25;
    mole.tag=1;
KARTHIK RA
  • 469
  • 2
  • 8
0
to check null value:

    if(mole!=NULL) //check null value
   {
      if (CGRectContainsPoint([mole boundingBox], location)) 
        {
          [mole stopAllActions];
          [mole runAction:[CCSequence actions:
                     [CCScaleTo actionWithDuration:.25 scale:1.1],
                     [CCScaleTo actionWithDuration:.25 scale:1.0],
                     nil]];
         }

   }
KARTHIK RA
  • 469
  • 2
  • 8
0

Do what KARTHIK RA says.

Alternatively, you can subclass CCSprite and add the touch detection and handlers within the subclass. I do this when I have several touchable sprites because:

  • It eliminates the needs to continually check all the children in a scene/layer on touch
  • You can create the action ahead of time within your sprite class and just run it when the touch is detected
  • It separates the action logic from the scene
  • When touch is detected, you know for a fact which object is touched

Good luck with cocos2d. It's a great engine. If you do migrate to version 3, keep in mind that "tag" is no longer a property of CCNode. It has been replaced by "name" which is actually a string type. Tag was an int type.

jd.
  • 84
  • 6