passing userData via ccmenuitem with ARC enabled - cocos2d-iphone

I'm trying to pass an NSString via a menuitem using the following code
CCMenuItem * buyButton = [CCMenuItemLabel itemWithLabel:buyLabel target:self selector:#selector(buyItem:)];
buyButton.userData = (__bridge void *)((NSString*)(itemName));
to the following selector
-(void) buyItem:(CCMenuItemLabel*)sender {
NSString * itemName = (NSString *)sender.userData;
}
but i'm crashing in the selector. I am using cocos2d with arc enabled, hence the bridge in the userdata assigment. (kobold2d). any ideas?

Your actual crash problem is this:
NSString * itemName = (NSString *)sender.userData;
Look over it, what are you casting here? Right: you're casting sender to NSString* and then you're sending sender (the CCMenuItemLabel) a userData message. BAM!
Brackets to the rescue:
NSString * itemName = (__bridge NSString *)(sender.userData);
Also, why make it overly complicated when there's userObject?
buyButton.userObject = itemName;
userObject is an id type and requires no bridge casting, userData is void* and requires bridge casting

Try this, its working
CCMenuItem * buyButton = [CCMenuItemLabel itemWithLabel:buyLabel target:self selector:#selector(buyItem:)];
NSString *userDataString = [NSString stringWithFormat:#"kidnim"];
buyButton.userData = (__bridge void *)userDataString;
CCMenu *menu = [CCMenu menuWithItems:buyButton, nil];
menu.position = ccp(240, 160);
[self addChild:menu];
And buyItem function:
-(void) buyItem:(CCMenuItemLabel*)sender {
NSString * itemName = (__bridge NSString*)sender.userData;
printf("NSString: %s\n", [itemName UTF8String]);
}
And you will get Out put as
NSString:kidnim

Related

Making SpriteSheets Work cocos2d

So I’ve been working on my cocos2d game for a little while now just using a static one frame sprite because I hadn’t gotten the graphics for the full spritesheet yet. Now I have the spritesheet, and am having trouble implementing it.
The way I currently make my “Player” (the sprite) is by having a separate player class, shown below
Player.h
#interface Player : CCSprite{
CCAction *_WalkAction;
}
#property (nonatomic, assign) CCAction *WalkAction;
#property (nonatomic, assign) CGPoint velocity;
#property (nonatomic, assign) CGPoint desiredPosition;
#property (nonatomic, assign) BOOL onGround;
#property (nonatomic, assign) BOOL forwardMarch;
#property (nonatomic, assign) BOOL mightAsWellJump;
#property (nonatomic, assign) BOOL isGoingLeft;
-(void)update:(ccTime)dt;
-(CGRect)collisionBoundingBox;
#end
And Player.m (I won’t show the full thing, because it’s pretty long, it just defines everything about the character)
-(id)initWithFile:(NSString *)filename {
if (self = [super initWithFile:filename]) {
self.velocity = ccp(0.0, 0.0);
}
return self;
}
//THIS IS ONLY A FRACTION OF MY UPDATE METHOD, THE REST OF IT IS ALL SETTING VELOCITY AND WHATNOT FOR MY PHYSICS ENGINE, BUT THIS IS THE ONLY RELEVANT PART
-(void)update:(ccTime)dt {
if (self.forwardMarch) {
self.velocity = ccpAdd(self.velocity, forwardStep);
//[self runAction: _WalkAction];
}
}
Now in my GameLevelLayer.m I init the sprite like this (This is where we get filename from from the init in player.m):
//In my init
map = [[CCTMXTiledMap alloc] initWithTMXFile:#"level1.tmx"];
[self addChild:map];
player = [[Player alloc] initWithFile:#"banker1.png"];
player.position = ccp(100, 50);
[map addChild:player z:15];
So that all works perfectly fine. The problem comes when I try to turn that sprite into a sprite with a spritesheet. So I try to do this in player.m
-(id)initWithFile:(NSString *)filename {
if (self = [super initWithFile:filename]) {
self.velocity = ccp(0.0, 0.0);
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: #"BankerSpriteSheet_default.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"BankerSpriteSheet_default.png"];
[self addChild:spriteSheet];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <=6; ++i) {
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"banker%d.png", i]]];
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.1f];
self = [CCSprite spriteWithSpriteFrameName:#"banker1.png"];
//HERE IS WHERE self.WalkAction IS DEFINED
self.WalkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
[spriteSheet addChild:self];
}
}
return self;
}
//And then run it in my update method
if (self.forwardMarch) {
self.velocity = ccpAdd(self.velocity, forwardStep);
[self runAction: _WalkAction];
}
Just for reference, self.forwardMarch is a bool that is set every time the user pushes the button to move the player, so whenever its true I move the player like this.
But when I try this I get the error
'NSInvalidArgumentException', reason: '-[CCSprite setWalkAction:]: unrecognized selector sent to instance 0x88fab50'
Any Help is appreciated.
I also posted this on the cocos2d forums if you like their code display system more (color coded and whatnot)
http://www.cocos2d-iphone.org/forums/topic/making-spritesheets-work/
EDIT:
Okay so I have made a little progress, but I'm still a bit stuck. I've defined the batchnodes and all the actions in the init of GameLevelLayer.m
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: #"BankerSpriteSheet_default.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"BankerSpriteSheet_default.png"];
[self addChild:spriteSheet];
[player addChild:spriteSheet];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <=6; ++i) {
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"banker%d.png", i]]];
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.1f];
player = [[Player alloc] initWithSpriteFrameName:#"banker1.png"];
player.WalkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
//[spriteSheet addChild:player];
}
//player = [[Player alloc] initWithFile:#"koalio_stand.png"];
player.position = ccp(100, 50);
player.scale = 0.2;
[map addChild:player z:15];
And then in my Player.m I run
[self runAction: self.WalkAction];
//I've also tried [self runAction: _WalkAction]; The result is the same
I used NSLogs to find out that the above section ([self runAction: self.WalkAction]; is being called but not finishing. This is where my crash happens, but there is NO ouput to the console as to why there is a crash.. literally just says (lldb)
The problem is one line above the actual error. You assign self with the result from [CCSprite spriteWith...] which replaces the existing self and replaces it with a CCSprite. Instead change the init line to self = [super initWithSpriteFrameName:..]

Implement animated Button in cocos2d

I wish to clone the effects of the button animations found in Candy Crush Saga.
And also I wish to know how to do such fluid & beautiful animations.
Is it possible with Cocos2d-iPhone?
This is the link of Candy Crush Sage:
http://www.youtube.com/watch?v=KAMUWIqYN24
Is it done using image sequences?
It is possible. Just run animation on buttons normal sprite.
GTAnimSprite *frame_normal = [GTAnimSprite spriteWithFile:#"play_button_normal.png"];
GTAnimSprite *frame_sel = [GTAnimSprite spriteWithFile:#"play_button_selected.png"];
frame_sel.color = ccc3(128,128,128);
CCMenuItemSprite *plyBtn = [CCMenuItemSprite itemWithNormalSprite:frame_normal
selectedSprite:frame_sel
target:self
selector:#selector(playBtnPress:) ];
plyBtn.position = ccp(size.width*0.77f, size.height*0.1f);
CCMenu *menu2 = [CCMenu menuWithItems:plyBtn, nil];
menu2.position = ccp(0.0f,0.0f);
[self addChild:menu2 z:2 ];
//Here is class file:GTAnimSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GTAnimSprite : CCSprite
{
bool bouncing;
float counter;
}
#end
//Here is class file:GTAnimSprite.mm
#import "GTAnimSprite.h"
#implementation GTAnimSprite
-(void)onEnter
{
[super onEnter];
counter = 0.0f;
bouncing = true;
[self scheduleUpdate];
}
-(void)update:(ccTime)dt
{
if (bouncing)
{
counter += dt;
self.scaleX = ( (sin(counter*10) + 1)/2.0 * 0.1 + 1);
self.scaleY = ( (cos(counter*10) + 1)/2.0 * 0.1 + 1);
if (counter > M_PI*10){
counter = 0;
}
}
}
-(void)onExit
{
[self unscheduleUpdate];
[super onExit];
}
#end
HERE IS XCODE SAMPLE SOURCE: https://www.box.com/s/52i4xyznetyyc329evcu
This is how I do it using CCActions
First, I define the CCAction:
id scaleHorDown = [CCScaleTo actionWithDuration:duration * 5/30.f scaleX:0.75f scaleY:1.0f];
id scaleHorBouncing = [CCEaseBounceIn actionWithAction:scaleHorDown];
id scaleVerDown = [CCScaleTo actionWithDuration:duration * 5/30.f scaleX:1.0f scaleY:0.65f];
id scaleVerBouncing = [CCEaseBounceInOut actionWithAction:scaleVerDown];
id shrink = [CCSequence actions:scaleHorBouncing,scaleVerBouncing, nil];
id swell = [CCScaleTo actionWithDuration: duration * 15/30.f scale:1.10f];
id swellEase = [CCEaseElasticOut actionWithAction:swell];
id resetScale = [CCScaleTo actionWithDuration:duration * 5/30.f scale:1.0f];
id resetScaleBouncing = [CCEaseInOut actionWithAction:resetScale];
id buttonAction = [CCSequence actions: shrink, swellEase, resetScaleBouncing, nil];
Then I run the animation over the desired sprites when CCMenuItem is initialized
CCMenuItem aMenuItem = [CCMenuItemSprite itemFromNormalSprite:buttonNormalSprite
selectedSprite:buttonSelectedSprite
block:^(id sender) {
//play animation highlighting button
[buttonSelectedSprite runAction:buttonAction]];
}}];
In my case I'm only running the animation when the button is pressed.

cocos2d-Menu's label shown on layer but doesn't react to touch

I don't know whats wrong, maybe because i'm using more layers?
I think that my other layer (sliding menu grid subclass is stealing the touches...)
//
// BGLayer.m
// MainProject
//
// Created by NSSL1 on 8/30/12.
// Copyright (c) 2012 MyCompanyName. All rights reserved.
//
#import "BGLayer.h"
#import "GameManager.h"
#import "MainMenuLayer.h"
#interface BGLayer()
-(void)displayBGMenu;
#end
#implementation BGLayer
-(void)backtomenu:(CCMenuItem*)itemPassedIn{
CCLOG(#"why I can't reach here?");
[[GameManager sharedGameManager] runSceneWithID:kMainMenuScene];}
-(id)init {
self = [super init];
if (self != nil) {
self.isTouchEnabled=YES;
[self displayBGMenu];
}
return self;
}
-(void)displayBGMenu{
NSString* backLabelstring = [NSString stringWithFormat:#"Back to Menu"];
CGSize screenSize = [CCDirector sharedDirector].winSize;
//Shadow
CCLabelTTF *backLabel = [CCLabelTTF labelWithString:backLabelstring fontName:#"Marker Felt" fontSize:32];
backLabel.position=CGPointMake(screenSize.height*0.5f, screenSize.width*0.1f);
backLabel.color = ccBLUE;
CCMenuItem* backbtnitem=[CCMenuItemLabel itemWithLabel:backLabel target: self
selector:#selector(backtomenu:)];
[backbtnitem setTag:21];
menu = [CCMenu menuWithItems:backbtnitem ,nil];
menu.position = CGPointMake((screenSize.width / 2), screenSize.height*0.1f);
menu.tag = 200;
[self addChild:menu z:20 tag:200];
}
#end
I had an issue with layers swallowing touches in the same type of class, and to avoid this someone suggested me to add this in the grid button item class init method:
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES];
The effect is that the class calling this method would not be greedy towards the parent's layer touch detector by detecting only the touches in its area, and would allow the user to scroll the menu in a smooth way as well as pressing other items in the parent layer (such as your menu).

Trying to make a menu with buttons; program crashes

The title is pretty self explanatory. I am currently trying to make a cocos2d menu and it compiles flawlessly however it crashes right before fully launching and my log reads this...
+[NSInvocation invocationWithMethodSignature:]: method signature argument cannot be nil'
I don't know what this means here is my code though
// Standard method to create a button
CCMenuItem *Earth = [CCMenuItemImage itemFromNormalImage:#"1.png"
selectedImage:#"1.png"
target:self
selector:#selector(loadLevel:)];
Earth.position = ccp(160, 0);
CCMenuItem *Mars = [CCMenuItemImage itemFromNormalImage:#"2.png"
selectedImage:#"2.png"
target:self
selector:#selector(loadLevel:)];
Mars.position = ccp(160, 240);
CCMenuItem *Moon = [CCMenuItemImage itemFromNormalImage:#"3.png"
selectedImage:#"3.png"
target:self
selector:#selector(loadLevel:)];
Moon.position = ccp(160, 480);
CCMenu *myMenu = [CCMenu menuWithItems:Earth, Mars,Moon, nil];
[myMenu setPosition:ccp(160,240)];
[self addChild:myMenu z:0];
After adding this my program began to crash. Thanks for any help guys you are the best.
you need to implement loadLevel like this
-(void) loadLevel:(NSObject*) sender
{
}

CCLabelBMFont for a menu item?

Hey I am crazy stuck on this. I want to use my cool heiro font sheet i got the CCLabelBMFont working great as labels and score variables but can't get them into the menu as clickable items! but it the CCLabelMBFont says specifically that you can use them as menu items -- see here:
Detailed Description
CCLabelBMFont is a subclass of CCSpriteBatchNode
Features:
* Treats each character like a CCSprite. This means that each individual character can be:
o rotated
o scaled
o translated
o tinted
o chage the opacity
* It can be used as part of a menu item.
* anchorPoint can be used to align the "label"
* Supports AngelCode text format
Yet i've looked all over the web and can't find one example of anybody getting a CCLabelBMFont as a menu item. Here's the code I have so far:
-(id) init
{
if( (self=[super init] )) {
CCLabelBMFont *homeTest = [CCLabelBMFont labelWithString:#"HomeTEST" fntFile:#"hieroTitle2.fnt"];
homeTest.position = ccp(0, 0);
//finalScoreFont.position = ccp(wrapper.position.x, wrapper.position.y-40);
[self addChild:homeTest z:2 tag:kTagHomeTest];
/* here's where I am lost and want to put the CCLabelBMFont
CCMenuItem *homeButton =
home.position = ccp(0, 0);
CCMenu *menu = [CCMenu menuWithItems:homeButton, nil];
menu.position = ccp(60, 50);
*/
[self addChild:menu z:3];
}
return self;
}
i found a clue and possible solution! Check out this guys code:
CCLabelBMFont *tlabel = [CCLabelBMFont labelWithString:#"Page 2" fntFile:#"customfont.fnt"];
CCMenuItemLabel *titem = [CCMenuItemLabel itemWithLabel:tlabel target:self selector:#selector(testCallback:)];
CCMenu *menu = [CCMenu menuWithItems: titem, nil];
menu.position = ccp(screenSize.width/2, screenSize.height/2);
Nice! he first makes the label and then uses that CCLabelBMFont as the CCMenuItemLabel Weeee! I would have never figured that out. I'm going to try it