Making a button-click exclusive to the picture of the button? - ios

I'm having an odd, irritating issue.
For example, in my main menu screen, I have a button that says "Instructions."
Once I click that, in the instructions layer, there is a button that takes you back to the main menu.
However, for some reason, the button action is not exclusive to the sprite image. If i click 3 inches away from the 'backtomenu' button, it still takes me back to the main menu.
So, my question is, how can I make a button be clicked only if you click the actual image?
(this is how I create a button)
- (id) init
{
if((self = [super init]))
{
[self instructions];
}
return self;
}
- (void) instructions
{
bgI = [CCSprite spriteWithFile:#"testbackground11.png"];
[bgI setPosition:ccp(160,240)];
ccTexParams params = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
[bgI.texture setTexParameters:&params];
[self addChild:bgI z:0];
returnToMenu = [CCMenuItemImage itemFromNormalImage:#"berry2.png"
selectedImage:#"berry2_selected.png"
target : self
selector: #selector (ifReturnToMenu:)];
CCMenu *myReturnMenu = [CCMenu menuWithItems:returnToMenu, nil];
[myReturnMenu alignItemsVertically];
[self addChild: myReturnMenu];
}
- (void) ifReturnToMenu: (CCMenuItem *) menuItem
{
if(menuItem == returnToMenu)
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:0.5f scene: [MainMenu scene]]];
}

I am not sure how 'isReturnToMenu' is fired, but you can try this
- (void) ifReturnToMenu: (CCMenuItem *) menuItem{
if(menuitem == returnToMenu){
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:0.5f scene: [MainMenu scene]]];
}
}
If it doesnt work, you'll need to post the code that fires it so we can help you more

Related

CCButton not working while touchMoved Implemented

I have to place functionality where CCButton can be dragged to proper position, wherever user needs to place them.
I have create a custom class for this but the issue is click method of the button is not being called when user clicks on the button.
touchyButton.h
#import "cocos2d.h"
#import "cocos2d-ui.h"
#interface touchyButton : CCButton { ... }
#property (nonatomic, assign) BOOL touchMoved;
#end
touchyButton.m
#import "touchyButton.h"
#implementation touchyButton
- (void) touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"touchMoved...", nil);
self.touchMoved = YES;
self.anchorPoint = ccp(0.5, 0.5);
CGPoint touchLoc = [touch locationInNode:self.parent];
//CGPoint inTouchLoc = [self convertToNodeSpace:self.anchorPoint];
//CGPoint touchP = ccpAdd(touchLoc, inTouchLoc);
//self.position = [self.parent convertToNodeSpace: touchP];
self.position = touchLoc;
}
- (void) touchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
self.touchMoved = NO;
NSLog(#"touchEnded...", nil);
}
#end
As the code explains, we are just trying to move the button wherever on the screen when user drags on the button.
Calling of the button in main code where it needs to be displayed.
touchyButton *btnRight = [touchyButton buttonWithTitle: #"" spriteFrame:[[CCSprite spriteWithImageNamed: #"arrR.png"] spriteFrame]];
[btnRight setBackgroundOpacity:0.5f forState: CCControlStateNormal];
[btnRight setAnchorPoint: ccp(1, 0.5)];
[btnRight setPosition: ccp(viewS.width - 10.f, viewS.height/2)];
[self addChild: btnRight];
[btnRight setTarget:self selector: #selector(performRightJump:)];
Now, when user clicks on the button, the button goes into selected state but performRightJump never fires. Can anyone suggest any alternative how I can implement the button with dragging behaviour with target action working..? Any hint would be appreciated as well.
One more thing is, in current code I can only be able to move the button's anchor point to the new touch point. Any idea how I can move the button in real fashion? The current method causes problem of tapping of first time for move, button's anchor point jumps to the tapped point.
this code is cocos2dx but useful for you
create a new class classA and classB
add this code
classA.h
{
//here implement
CC_SYNTHESIZE_RETAIN(classB *, _ classB, _ ClassB);
}
classA.cpp
bool classA::init()
{
set_classB(classB::initwithclass());
this->addChild(get_ClassB(),0);
//here create button
return true;
}
bool classA:: onTouchBegan (Touch *touch ,Event *event)
{
_classB->setposition(touchLoc);
return true;
}
void classA:: onTouchMoved(Touch *touch, Event *event)
{
}
void classA:: onTouchEnded(Touch *touch ,Event *event)
{
}
//as well as this code use in cocos2d

Objective C if statement, change scene - ios app

Ok so I'm still on my learning journey and am trying to add a new scene to the end of a game. Currently after a level ends, there is a Next button to take you to the next level. On the final stage, the next button originally didn't do anything and just had a 'return' function. I'm trying to make it so that if the current stage is the last level, the next button (nextAction) will load up the next scene "EndLayer". Here is what I have but it is not working:
- (void) nextAction {
if (g_SoundFlag) {
[sd_button play];
}
_clickedButtonIndex = 1;
NSInteger currentStage = [AppSettings getCurrentStage];
if (currentStage >= 24) {
//return;
NSString *r = #"CCTransitionFade";
Class transion = NSClassFromString(r);
[[CCDirector sharedDirector] replaceScene:[transion transitionWithDuration:0.3 scene:[EndLayer scene]]];
}
[[CCTextureCache sharedTextureCache] removeUnusedTextures];
[AppSettings setCurrentStage: currentStage+1];
[[CCDirector sharedDirector] replaceScene:[Game scene]];
}

Cocos2D magnification - making the code dynamic and using an image

I have found the following code and I need help with editing it. I am not really familiar with texture rendering.
First of all, init method takes a rect and magnifies only that area? How can I make it more dynamic and magnify only whatever is underneath the magnifying glass?
Secondly, Is it possible to change the shape to circle rather than rectangle?
Or Can I use an image as the frame of the magnifying glass?
Here is the code..
Cheers..
.h file
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Magnify : CCNode {
BOOL active;
CGRect rect;
CGFloat magnifyScale;
CCNode *renderNode;
CCRenderTexture *renderTexture;
}
- (id)initWithNodeToMagnify:(CCNode *)n rect:(CGRect)rectToMagnify scale:(CGFloat)scale;
- (void)enable;
- (void)disable;
.m file
#import "Magnify.h"
#implementation Magnify
- (id)initWithNodeToMagnify:(CCNode *)n rect:(CGRect)rectToMagnify scale:(CGFloat)scale
{
if (self = [super init]) {
self.visible = active = NO;
renderNode = n;
rect = rectToMagnify;
magnifyScale = scale;
renderTexture = [[CCRenderTexture renderTextureWithWidth:rect.size.width height:rect.size.height] retain];
[self addChild:renderTexture];
}
return self;
}
- (void)enable
{
self.visible = active = YES;
[self scheduleUpdate];
}
- (void)disable
{
self.visible = active = NO;
[self unscheduleUpdate];
}
- (void)drawAreaToTexture
{
[renderTexture beginWithClear:0.0 g:0.0 b:0.0 a:1.0];
// shift the renderNode's position to capture exactly the rect we need
CGPoint originalPosition = renderNode.position;
renderNode.position = ccpSub(originalPosition, rect.origin);
// scale the node as we want
CGFloat originalScale = renderNode.scale;
renderNode.scale = magnifyScale;
[renderNode visit];
// shift renderNode's position back
renderNode.position = originalPosition;
// scale back
renderNode.scale = originalScale;
[renderTexture end];
}
- (void)update:(ccTime)dt
{
[self drawAreaToTexture];
}
- (void)dealloc
{
[renderTexture release];
[super dealloc];
}
#end
OK, so, as I mentioned above for something like this, one possible answer is to use the CCLens3D class to get the "effect" of magnifying something in a circular manner.
I found using this to be a little tricky because it doesn't seem to work unless it's a child of the top level node of your 'scene'.
Here is some code I use to create a lens that moves around the screen, and then disappears:
// Create the lens object first.
//
CCLens3D *lens =
[CCLens3D actionWithPosition:fromPos
radius:50
grid:ccg(50, 50)
duration:2.0];
// Set the "size" of the lens effect to suit your needs.
//
[lens setLensEffect:1.0];
// In my case, I then move the lens to a new position. To apply an action on
// a lens, you need to give the actions to the actionManager in the
// CCDirector instance.
//
CCMoveTo *move = [CCMoveTo actionWithDuration:2.0 position:toPos];
// I had another action in this array, but this will do.
//
CCSequence *seq = [CCSequence actions:move, nil];
// Now tell the actionManager to move the lens. This is odd, but it works.
//
[[[CCDirector sharedDirector] actionManager] addAction:seq target:lens paused:NO];
// Now just for some more weirdness, to actually make the lens appear and operate
// you run it as an action on the node it would normally be a child of. In my case
// 'self' is the CCLayer object that is the root of the current scene.
//
// Note that the first action is the lens itself, and the second is a special
// one that stops the lens (which is a "grid" object).
//
[self runAction:[CCSequence actions:lens, [CCStopGrid action], nil]];
I imagine that you should be able to stop the grid by running the CCStopGrid action when you want to. In my case it is a programmed thing. In yours it might be when the user lets go of a button.

Cocos2d game crashing shortly after CCCallFunc

Situation:
I'm getting some mysterious crashing shortly after a CCCallFunc. In short, we have a button. The button has a tag to identify it later. When the button is pressed, we run some actions to animate it, and when the animation is done, we CCCallFunc another method to transition to another scene. We crash shortly after the CCCallFunc. Source and errors below.
Point Of Crash (in cocos2d source):
// From CCActionInstant.m of cocos2d
-(void) execute
{
/*** EXC_BAD_ACCESS on line 287 of CCActionInstant.m ***/
[targetCallback_ performSelector:selector_];
}
#end
Snapshot of Thread 1:
My Code:
Below is some source taken from MenuLayer.m (a simple menu to display a button).
// from MenuLayer.m
// …
#implementation MenuLayer
-(id) init{
if((self=[super init])) {
/****** Create The Play Button (As a CCMenu) ********/
CCSprite *playSprite = [CCSprite spriteWithFile:#"playbutton.png"];
CCMenuItemSprite *playItem = [CCMenuItemSprite itemFromNormalSprite:playSprite selectedSprite:nil target:self selector:#selector(animateButton:)];
playItem.tag = 3001;
playItem.position = ccp(160.0f, 240.0f);
CCMenu *menu = [CCMenu menuWithItems:playItem, nil];
menu.position = ccp(0.0f, 0.0f);
[self addChild:menu z:0];
}
}
// ...
- (void)animateButton:(id)sender{
/*** Run an animation on the button and then call a function ***/
id a1 = [CCScaleTo actionWithDuration:0.05 scale:1.25];
id a2 = [CCScaleTo actionWithDuration:0.05 scale:1.0];
id aDone = [CCCallFunc actionWithTarget:self selector:#selector(animationDone:)];
[sender runAction:[CCSequence actions:a1,a2,aDone, nil]];
}
- (void)animationDone:(id)sender{
/*** Identify button by tag ***/
/*** Call appropriate method based on tag ***/
if([(CCNode*)sender tag] == 3001){
/*** crashes around here (see CCActionInstant.m) ***/
[self goGame:sender];
}
}
-(void)goGame:(id)sender{
/*** Switch to another scene ***/
CCScene *newScene = [CCScene node];
[newScene addChild:[StageSelectLayer node]];
if ([[CCDirector sharedDirector] runningScene]) {
[[CCDirector sharedDirector] replaceScene:newScene]];
}else {
[[CCDirector sharedDirector] runWithScene:newScene];
}
}
Use CCCallFuncN instead of CCCallFun.
CCCallFuncN passes the Node as parameter, the problem with CCCallFun is that you are loosing reference of the node.
I test your code with CCCallFuncN and works ok.
Just a hunch. Besides checking for memory leaks, try to schedule a selector with a 0 second interval instead of directly sending the goGame message. I have a suspicion that director's replaceScene causes a cleanup of the scene and all objects associated with it. That in turn could leave the CCCallFunc action in an undefined state. Although normally it works fine - which is to say that this is just another indication about something sketchy, memory- respectively object-lifetime-management-wise.
Btw, if you support iOS 4 as a minimum, use CCCallBlock instead of CCCallFunc. That's safer and cleaner.

On a UILongPressGestureRecognizer how do I detect which object generated the event?

I have a view with several UIButtons. I have successfully implemented using UILongPressGestureRecognizer with the following as the selector;
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateEnded ) {
NSLog(#"Long Press");
}
}
What I need to know within this method is which UIButton received the longpress since I need to do something different, depending on which button received the longpress.
Hopefully the answer is not some issue of mapping the coordinates of where the longpress occured to the bounds of the buttons - would rather not go there.
Any suggestions?
Thanks!
This is available in gesture.view.
Are you adding the long tap gesture controller to the UIView that has the UIButtons as subviews? If so, something along the lines of #Magic Bullet Dave's approach is probably the way to go.
An alternative is to subclass UIButton and add to each UIButton a longTapGestureRecogniser. You can then get your button to do what you like. For example, it could send a message identifying itself to a view controller. The following snippet illustrates methods for the subclass.
- (void) setupLongPressForTarget: (id) target;
{
[self setTarget: target]; // property used to hold target (add #property and #synthesise as appropriate)
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:button action:#selector(longPress:)];
[self addGestureRecognizer:longPress];
[longPress release];
}
- (void) longPress: (UIGestureRecognizer*) recogniser;
{
if (![recogniser isEnabled]) return; // code to prevent multiple long press messages
[recogniser setEnabled:NO];
[recogniser performSelector:#selector(setEnabled:) withObject: [NSNumber numberWithBool:YES] afterDelay:0.2];
NSLog(#"long press detected on button");
if ([[self target] respondsToSelector:#selector(longPressOnButton:)])
{
[[self target] longPressOnButton: self];
}
}
In your view controller you might have code something like this:
- (void) viewDidLoad;
{
// set up buttons (if not already done in Interface Builder)
[buttonA setupLongPressForTarget: self];
[buttonB setupLongPressForTarget: self];
// finish any other set up
}
- (void) longPressOnButton: (id) sender;
{
if (sender = [self buttonA])
{
// handle button A long press
}
if (sender = [self buttonB])
{
// handle button B long press
}
// etc.
}
If your view contains multiple subViews (like lots of buttons) you can determine what was tapped:
// Get the position of the point tapped in the window co-ordinate system
CGPoint tapPoint = [gesture locationInView:nil];
UIView *viewAtBottomOfHeirachy = [self.window hitTest:tapPoint withEvent:nil];
if ([viewAtBottomOfHeirachy isKindOfClass:[UIButton class]])

Resources