How to stop all CCTouch under the modal layer in cocos2d?

In my cocos2d game, I have a Settings button that launches the modal layer and is designed to lock everything under it. To do this, I use a combination of a menu state method that blocks all CCMenuItems, and I use coverlayer; both of which are below in code.

The problem is that no solution seems to work in CCScrollLayers. When I click the button (launches the modal), CCScrollLayer can still scroll, which is not what I want.

I would like to:

  • Click the button disabled ALL touch and disable all items including CCScrollLayers
  • Run modal (allow touch only for modal)

I tried:

  • Using Touch to swallow all touches using CCTargetedTouchDelegate

[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];

  • I tried

self.isTouchEnabled = NO on the layer starting the modal

  • I tried to adapt the MenuStatus method MenuStatus that it works for CCScrollLayers, but it does not work.

I'm not sure what I'm doing wrong. Now my code.

 // My main layer which launches the Settings Modal Layer #pragma mark - Lock/Unlock layers -(void) doSettings { [self lockLayers]; SettingsModalLayer *sml = [[[SettingsModalLayer alloc] init] autorelease]; [sml showSettingsOnLayer:self closeBlock:^{[self unlockLayers];}]; } -(void) lockLayers { [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; [self MenuStatus:NO Node:self]; } -(void) unlockLayers { [self MenuStatus:YES Node:self]; } // Disabled/Enable layers -(void) MenuStatus:(BOOL)_enable Node:(id)_node { for (id result in ((CCNode *)_node).children) { if ([result isKindOfClass:[CCMenu class]]) { for (id result1 in ((CCMenu *)result).children) { if ([result1 isKindOfClass:[CCMenuItem class]]) { ((CCMenuItem *)result1).isEnabled = _enable; } } // next } } // next } -(void) registerWithTouchDispatcher { [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; } - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"Event: %@", event); for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; CCLayer *gl = (CCLayer *)[self getChildByTag:4]; [gl setIsTouchEnabled:NO]; } } -(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event { } -(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event { [self removeFromParentAndCleanup:YES]; } -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { return YES; } // Settings Modal Layer -(void) showSettingsOnLayer:(CCLayer *)layer closeBlock:(void (^)())noBlock { CoverLayer *coverLayer = [[CoverLayer alloc] init]; [layer addChild:coverLayer z:1000]; [coverLayer runAction:[CCFadeTo actionWithDuration:kAnimationTime opacity:155]]; // smooth fade-in to dim with semi-transparency ... // Other stuff goes here } // CoverLayer // This is meant to stop all touches, but it doesn't really work on CCScrollLayer #define kDialogTag 1234 #import "CoverLayer.h" // class that implements a black colored layer that will cover the whole screen // and eats all touches except within the dialog box child @implementation CoverLayer - (id)init { self = [super init]; if (self) { [self initWithColor:ccc4(0,0,0,0) width:[CCDirector sharedDirector].winSize.width height:[CCDirector sharedDirector].winSize.height]; self.isTouchEnabled = YES; } return self; } - (void)dealloc { [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; [super dealloc]; } - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [self convertTouchToNodeSpace: touch]; CCNode *dialogBox = [self getChildByTag: kDialogTag]; // eat all touches outside of dialog box return !CGRectContainsPoint(dialogBox.boundingBox, touchLocation); } 
+4
source share
3 answers

You just need to understand how priority works with CCTouchDispatcher. A layer with a minimum priority value will first receive touch events. Now you just need to adjust the priorities accordingly.

Create a blocking layer class and set its priority at least when registering with CCTouchDispatcher and override ccTouchBegan and just return YES to it.

If you look at the CCMenu class, you will notice that the default priority is kCCMenuTouchPriority = -128, that the reason menu items have a higher touch priority.

+4
source

A layer that swallows all touch events must be registered with higher priority than any basic controls. Usually these will be menu items with the default priority kCCMenuHandlerPriority = -128 (the lowest values ​​are processed first).

Then the swallowing layer simply processes any touches it receives, to do nothing.

Any controls in your popup should then be prioritized over the swallowing layer, so if you use the menu, you need to set a new priority. Then these touches will be processed first (and not by the swallowing layer).

Relevant functions that show registration of a swallowing layer with a priority of -1024, processing (ignoring) all touches and adding a menu item with a higher priority than the swallowing layer:

 // Ensure dialog background, which swallows all touches, is prioritised before normal menus (-128) // Menus displayed on the dialog, then need to be prioritised before that. #define kDialogSwallowTouchPriority -1024 #define kDialogMenuPriority -1032 - (void) registerWithTouchDispatcher { [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:kDialogSwallowTouchPriority swallowsTouches:YES]; } - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { // Swallow all touches return YES; } ... - (void)addCloseMenu { CCSprite *close = [CCSprite spriteWithSpriteFrameName:@"closebutton.png"]; CCMenuItem *closeMenuItem = [CCMenuItemSprite itemWithNormalSprite:close selectedSprite:nil target:self selector:@selector(closeTapped:)]; closeMenuItem.anchorPoint = ccp( 1, 1 ); closeMenuItem.position = ccp( self.dialog.contentSize.width - 10, self.dialog.contentSize.height - 10 ); self.closeMenu = [CCMenu menuWithItems:closeMenuItem, nil]; self.closeMenu.anchorPoint = ccp( 1, 1 ); self.closeMenu.position = CGPointZero; // Set the priority above the swallowing layer self.closeMenu.touchPriority = kDialogMenuPriority; [self.dialog addChild:self.closeMenu]; } 
+2
source

poor mans way to do this: Add ccmenuitemsprite and ccmenu with opacity ccsprite set to 0, covering what you don't want to click. works for me when I just don't want to get confused in the touch manager.

0
source

Source: https://habr.com/ru/post/1434586/


All Articles