How to stop all CCTouch under a modal layer in coc

2019-06-26 02:37发布

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

The problem is that neither solution seems to work on CCScrollLayers. When I click the button (which launches the modal) the CCScrollLayer can still be scrolled which is not what i want.

I'd like to:

  1. Press a button disabled ALL touches and disable all elements including CCScrollLayers
  2. Launch the modal (allow touches for the modal only)

I've tried:

  1. Using Touch to swallow all touches using the CCTargetedTouchDelegate

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

  1. I've tried

self.isTouchEnabled = NO on the layer launching the modal

  1. I've tried adapting the MenuStatus method so that it works for CCScrollLayers but it does not seem to work.

I'm not sure what I'm doing wrong. My code now follows.

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

3条回答
霸刀☆藐视天下
2楼-- · 2019-06-26 03:17

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

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

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

查看更多
Root(大扎)
3楼-- · 2019-06-26 03:32

The layer that swallows all the touch events needs to be registered with more priority than any underlying controls. Usually these would be menu items, with a default priority of kCCMenuHandlerPriority = -128 (lowest values are handled first).

The swallowing layer then simply handles any touches it receives, do nothing.

Any controls on your popup then need to be prioritised before the swallowing layer, so if you are using menus you need to set the new priority. These touches will then be handled by the item first (and not the swallowing layer).

Relevant functions showing registering the swallowing layer with priority -1024, handling (ignoring) all touches, and adding a menu item with more 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];
}
查看更多
该账号已被封号
4楼-- · 2019-06-26 03:38

the poor mans way of doing it : Add a ccmenuitemsprite and ccmenu with the ccsprite opacity set to 0 covering whatever you dont want clicked. works for me when i just dont want to get messy with the touch dispatcher.

查看更多
登录 后发表回答