CCMenuItemImage not responding to touches! - iphone

So I am adding a CCMenuItemImage to my layer like so:
CCMenuItemImage *pauseButton = [CCMenuItemImage itemFromNormalImage:#"pausebutton.png"
selectedImage:#"pausebutton.png" // TODO add selected image
disabledImage:#"pausebutton.png"
target:self
selector:#selector(pauseGame:)];
pauseButton.position = ccp(24, 292);
[self addChild:pauseButton];
The problem is my pauseGame: selector is never triggered when I touch the pause button!
I have verified that the selector is set properly by doing a [pauseButton activate] (calls the selector).
Also, I have verified that my layer is responding to touches by outputting logging information in ccTouchesBegan and ccTouchesEnded.
It is also worth noting that I have sprites in my layer which register themselves for touches like so:
- (void) onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
What could the problem be?

Hmm... You didnt add to a CCMenu...
CCMenu* menu = [CCMenu menuWithItems:pauseButton, nil];
menu.isTouchEnabled = YES;
[self addChild:menu];
Note that your pausegame should be:
-(void)pauseGame:(id)sender
{
//pause game!!!
}

Related

How to register touch event cocos3d

I have tried to find a way of registering a touch event in cocos3d such as TOUCHESBEGAN but that does not exist, only
-(void) touchEvent: (uint) touchType at: (CGPoint) touchPoint {
NSLog(#"hello");
}
Bu that does not log "hello".
How can this be done?
Not sure if you are still interested with the answer, but here goes.
In order to use touchEvent in cocos3d, you need to enable touch in the CC3Layer (in the initializeControls method)
self.isTouchEnabled = YES;
Then you can use the touchEvent:(uint)touchType at:(CGPoint)touchPoint in the CC3Scene.
Is started to work for me when I have put this code in my CC3Layer subclass
-(void) initializeControls {
[self scheduleUpdate];
self.userInteractionEnabled = YES;
[self setTouchEnabled:YES];
}

Swallow touches, unless touching a child of my current layer

I am writing a pause menu using a CCLayer. I need the layer to swallow touches so that you cannot press the layer below, however I also need to be able to use the buttons on the pause layer itself.
I can get the layer to swallow touches, but the menu won't work either.
Here is my code:
pauseLayer.m
#import "PauseLayer.h"
#implementation PauseLayer
#synthesize delegate;
+ (id) layerWithColor:(ccColor4B)color delegate:(id)_delegate
{
return [[[self alloc] initWithColor:color delegate:_delegate] autorelease];
}
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
NSLog(#"Init");
self.isTouchEnabled = YES;
CGSize wins = [[CCDirector sharedDirector] winSize];
delegate = _delegate;
[self pauseDelegate];
CCSprite * background = [CCSprite spriteWithFile:#"pause_background.png"];
[self addChild:background];
CCMenuItemImage *resume = [CCMenuItemImage itemFromNormalImage:#"back_normal.png"
selectedImage:#"back_clicked.png"
target:self
selector:#selector(doResume:)];
resume.tag = 10;
CCMenu * menu = [CCMenu menuWithItems:resume,nil];
[menu setPosition:ccp(0,0)];
[resume setPosition:ccp([background boundingBox].size.width/2,[background boundingBox].size.height/2)];
[background addChild:menu];
[background setPosition:ccp(wins.width/2,wins.height/2)];
}
return self;
}
-(void)pauseDelegate
{
NSLog(#"pause delegate");
if([delegate respondsToSelector:#selector(pauseLayerDidPause)])
[delegate pauseLayerDidPause];
}
-(void)doResume: (id)sender
{
if([delegate respondsToSelector:#selector(pauseLayerDidUnpause)])
[delegate pauseLayerDidUnpause];
[self.parent removeChild:self cleanup:YES];
}
- (void)onEnter {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
[super onEnter];
}
- (void)onExit {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return YES;
}
-(void)dealloc
{
[super dealloc];
}
#end
why dont you just disable touches on the game layer?
like in the onEnter method disable the touches on the game layer..and onExit re enable them
something like
-onEnter{
gameLayer.isTouchEnabled=NO;
....
}
-onExit{
gameLater.isTouchEnabled=YES;
...
}
also you wont need CCTouchDispatcher
According to your code, the problem is the modal layer is swallowing events even if it's for the own children.
To solve this problem, you have to set the touch priority of the children even higher than the modal layer itself.
In other words, set the menu's touch priority value below modal layer's.
There are two solutions.
Simply override "CCMenu::registerWithTouchDispatcher()" method and set the priority higher from the beginning.
Change menu's touch priority using "setPriority" method of the touchDispatcher or "setHandlerPriority" of menu itself.
To achieve second solution, you have to pay attention to the timing.
"CCMenu::registerWithTouchDispatcher()" is called somewhere AFTER "onEnter" and "onEnterTransitionDidFinish".
So, use "scheduleOnce" or something like that.
Sample codes.
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
//your codes...... put CCMenu in instance
[self scheduleOnce:#selector(setMenuPriority:) delay:0];
}
return self;
}
- (void) setMenuPriority (ccTime)dt {
[[[CCDirector sharedDirector] touchDispatcher] setPriority:INT_MIN forDelegate:menu];
//priority "INT_MIN" because you set the layer's priority to "INT_MIN+1".
}
PS: My english is not so good, so if there are loose sentences, correction will be very pleased :)
The problem is, that the layer/node hierarchy is not considered when propagating touches.
The touches are handed from the touch handler with the smallest priority value to the ones with the highest.
The touches are not forwarded anymore, once one of the responders swallows the touch.
You can use an approach similar to CCMenu. CCMenu handles all touches and detects which CCMenuItemhas been selected, based on the position of these items.
If you implement this the same way, you let your PauseLayer handle and swallow all touches and use a seperate mechanism to determine which child element in your PauseLayer has been selected.
Example Implementation: CCMenu Subclass
I have implemented a solution in this repository:
https://github.com/Ben-G/MGWU-Widgets/tree/master/Projectfiles/Components/CCMenuBlocking
That component is a CCMenuSubclass that swallows all touches and does not forward them.
Example Implementation: CCNode
Here is a more general solution of a CCNode that swallows touches and only forwards them to its own children:
https://github.com/Ben-G/MGWU-Widgets/blob/master/Projectfiles/Components/Popup/PopUp.m

Cocos2d - CCMenu with multiple buttons calls wrong selector on first load

Here's an interesting dilemma. I have two CCMenus being loaded on a page, each with two CCMenuItemImages as buttons. All four buttons call the same function, which decides what to do using a switch statement that goes off the caller's tag value.
The four buttons are start, tutorial, options, and credits. I have them split into two menus so that I can horizontally and vertically align them in a faux grid. This layer is the main menu layer, so it is the first thing to load after the game starts.
The problem is that when the game first loads, pressing any button will call the "options" button. Not just the function, pressing any button on the menu activates the options button's selected state. If I press "start," for instance, the start button's selected state (a glow around the image) doesn't work–the options button glows instead.
Once I get into the options menu, and then back out of it, the main menu works as expected, with each button activating its requisite function.
I should note that I've also run a clean, and removed the app from both the simulator and my iPhone and rebuilt it.
Here's my .h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Constants.h"
#import "GameManager.h"
#interface MainMenuLayer : CCLayer {
CCMenu *mainMenuTop;
CCMenu *mainMenuBottom;
}
#end
And this is my .m:
#import "MainMenuLayer.h"
// Private methods
#interface MainMenuLayer()
- (void)displayMainMenu;
#end
#implementation MainMenuLayer
- (void)playScene:(CCMenuItemFont*) itemPassedIn {
if ([itemPassedIn tag] == 1) {
CCLOG(#"Tag 1 found, Scene 1");
[[GameManager sharedGameManager] runSceneWithID:kGameplayScene];
} else if ([itemPassedIn tag] == 2) {
CCLOG(#"Tag was: %d", [itemPassedIn tag]);
CCLOG(#"Placeholder for next chapters");
} else if ([itemPassedIn tag] == 3) {
CCLOG(#"Tag 3, Options");
[[GameManager sharedGameManager] runSceneWithID:kOptionsScene];
} else if ([itemPassedIn tag] == 4) {
CCLOG(#"Tag 4, Credits");
[[GameManager sharedGameManager] runSceneWithID:kCreditsScene];
}
}
- (void)displayMainMenu {
CGSize winSize = [CCDirector sharedDirector].winSize;
// Main Menu Top Layer Buttons
CCMenuItemImage *playGameButton = [CCMenuItemImage itemFromNormalImage:#"button-start-up.png" selectedImage:#"button-start-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[playGameButton setTag:1];
CCMenuItemImage *tutorialButton = [CCMenuItemImage itemFromNormalImage:#"button-tutorial-up.png" selectedImage:#"button-tutorial-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[tutorialButton setTag:2];
// Main Menu Bottom Layer Buttons
CCMenuItemImage *optionsButton = [CCMenuItemImage itemFromNormalImage:#"button-options-up.png" selectedImage:#"button-options-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[optionsButton setTag:3];
CCMenuItemImage *creditsButton = [CCMenuItemImage itemFromNormalImage:#"button-credits-up.png" selectedImage:#"button-credits-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[creditsButton setTag:4];
mainMenuTop = [CCMenu menuWithItems:playGameButton,tutorialButton,nil];
mainMenuBottom = [CCMenu menuWithItems:optionsButton,creditsButton,nil];
[mainMenuTop alignItemsHorizontallyWithPadding: 10.0f];
[mainMenuTop setPosition: ccp(winSize.width/2, -500)];
[mainMenuBottom alignItemsHorizontallyWithPadding:10.0f];
[mainMenuBottom setPosition:ccp(winSize.width/2, -600)];
id moveActionTop = [CCMoveTo actionWithDuration:0.5f position:ccp(winSize.width/2, 150)];
id moveEffectTop = [CCEaseIn actionWithAction:moveActionTop rate:1.0f];
[mainMenuTop runAction:moveEffectTop];
[self addChild:mainMenuTop z:2 tag:kMainMenuTagValue];
id moveActionBottom = [CCMoveTo actionWithDuration:0.5f position:ccp(winSize.width/2, 75)];
id moveEffectBottom = [CCEaseIn actionWithAction:moveActionBottom rate:1.0f];
[mainMenuBottom runAction:moveEffectBottom];
[self addChild:mainMenuBottom z:3 tag:kMainMenuBottomTagValue];
}
-(id)init {
self = [super init];
if (self != nil) {
[self displayMainMenu];
}
return self;
}
#end
I ended up just positioning things manually. I tried to use the method linked by LearnCocos2D, but I couldn't determine how to make it work properly; the items didn't end up in a proper grid.

cocos2d-iOS - Gesture recognisers

Has anyone managed to get the gesture recognition working in cocos-2d?
I have read a post here that claimed to have achieved it, here: http://www.cocos2d-iphone.org/forum/topic/8929
I patched from the git hub here: https://github.com/xemus/cocos2d-GestureRecognizers/blob/master/README
I made a subclass of CCSprite (which is a subclass of CCNode):
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect {
if( (self=[super initWithTexture:texture rect:rect]) )
{
CCGestureRecognizer* recognizer;
recognizer = [CCGestureRecognizer
CCRecognizerWithRecognizerTargetAction:[[[UITapGestureRecognizer alloc]init] autorelease]
target:self
action:#selector(tap:node:)];
[self addGestureRecognizer:recognizer];
}
return self;
}
Delegate method:
- (void) swipe:(UIGestureRecognizer*)recognizer node:(CCNode*)node
{
NSLog(#" I never get called :( ");
}
My tap event never gets called.
Has anyone got this working? How difficult is it to do gesture recognition manually for swipe detection?
You need to attach the gesture recognizer to something "up the chain". Don't attach them to the individual nodes; attach them to the UIView (i.e., [[CCDirector sharedDirector] openGLView]).
Here's what I did:
- (UIPanGestureRecognizer *)watchForPan:(SEL)selector number:(int)tapsRequired {
UIPanGestureRecognizer *recognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:selector] autorelease];
recognizer.minimumNumberOfTouches = tapsRequired;
[[[CCDirector sharedDirector] openGLView] addGestureRecognizer:recognizer];
return recognizer;
}
- (void)unwatch:(UIGestureRecognizer *)gr {
[[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:gr];
}
This particular code is used in a superclass for scene controllers, so the target for the selector is hard-coded to "self", but you could easily abstract that to a passed-in object. Also, you could extrapolate the above to easily create gesture recognizers for taps, pinches, etc.
In the subclass for the controller, then, I just do this:
- (MyController *)init {
if ((self = [super init])) {
[self watchForPan:#selector(panning:) number:1];
}
return self;
}
- (void)panning:(UIPanGestureRecognizer *)recognizer {
CGPoint p;
CGPoint v;
switch( recognizer.state ) {
case UIGestureRecognizerStatePossible:
case UIGestureRecognizerStateBegan:
p = [recognizer locationInView:[CCDirector sharedDirector].openGLView];
(do something when the pan begins)
break;
case UIGestureRecognizerStateChanged:
p = [recognizer locationInView:[CCDirector sharedDirector].openGLView];
(do something while the pan is in progress)
break;
case UIGestureRecognizerStateFailed:
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
(do something when the pan ends)
(the below gets the velocity; good for letting player "fling" things)
v = [recognizer velocityInView:[CCDirector sharedDirector].openGLView];
break;
}
}
If you don't want to handle everything manually I created a simple category that will add gesture recognizers to any cocos2d version
read more at:
http://www.merowing.info/2012/03/using-gesturerecognizers-in-cocos2d/
or grab it from github
https://github.com/krzysztofzablocki/CCNode-SFGestureRecognizers

action won't run on a CCSprite from within ccTouchBegan in cocos2d for iphone

I'm trying to basically scale up a button as soon as a touch is detected. Here's my Scene:
#implementation HomeScene
-(id) init
{
if((self = [super init])) {
...
// sp_btn_story is retained...
sp_btn_story = [[CCSprite spriteWithFile:#"main_menu_btn.png"] retain];
sp_btn_story.position = ccp(size.width - 146, 110);
[self addChild: sp_btn_story];
...
}
return self;
}
-(void) onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
-(void) onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
NSLog(#"tapped!");
sp_btn_story.scaleX = 2;
[sp_btn_story stopAllActions];
[sp_btn_story runAction: [CCScaleTo actionWithDuration:0.5f scale:1.2f]];
return YES;
}
...
#end
It scales the X just fine, as expected. (I threw that in there to test.) But the action is not running for some reason. :( Anyone have any ideas?
Edit: using cocos2d 0.99 btw.
The answer for this question (reposted from the asker's own comment to make it more visible):
It's quite important to call
[super onEnter];
if you overwrite this method in a subclass or strange things may happen. Cocos2D will not be happy.
This applies to other (if not all) methods as well, e. g. always call
[super onExit];
as well.
Not complete sure what could be going on. Looks like what you have should work fine but you may want to try applying a different action to sp_btn_story like fade in or fade out to see if at least any action will work. Failing that you could also try applying the action in a different part of your code. These are not solutions but they may provide evidence to indicate what exactly is going on.