Как остановить все CCTouch под модальным слоем в cocos2d?
В моей игре cocos2d у меня есть кнопка "Настройки", которая запускает модальный слой и предназначена для блокировки всего, что находится под ним. Для этого я использую комбинацию метода состояния меню, который блокирует все CCMenuItems, и я использую слой покрытия; оба из которых ниже в коде.
Проблема в том, что ни одно из решений не работает на CCScrollLayers. Когда я нажимаю кнопку (которая запускает модальный режим), CCScrollLayer все еще можно прокручивать, а это не то, что мне нужно.
Я бы хотел:
- Нажатие кнопки отключает ВСЕ касания и отключает все элементы, включая CCScrollLayers
- Запустить модальный режим (разрешить касания только для модального)
Я пробовал:
- Использование Touch, чтобы проглотить все касания, используя
CCTargetedTouchDelegate
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
- я пробовал
self.isTouchEnabled = NO
на слое запуска модального
- Я пытался адаптировать
MenuStatus
метод, чтобы он работал для CCScrollLayers, но, похоже, не работает.
Я не уверен, что я делаю неправильно. Мой код теперь следует.
// 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 ответа
Вам просто нужно понять, как работает приоритет с CCTouchDispatcher. Слой с минимальным значением приоритета сначала получит сенсорные события. Теперь вам просто нужно соответствующим образом скорректировать приоритеты.
Создайте класс блокирующего слоя и установите его приоритет на минимум при регистрации в CCTouchDispatcher, переопределите ccTouchBegan и просто верните в него значение YES.
Если вы посмотрите на класс CCMenu, то заметите приоритет по умолчанию kCCMenuTouchPriority = -128, поэтому пункты меню имеют более высокий приоритет касания.
Слой, который поглощает все сенсорные события, должен быть зарегистрирован с более высоким приоритетом, чем любые нижележащие элементы управления. Обычно это пункты меню с приоритетом по умолчанию kCCMenuHandlerPriority = -128 (сначала обрабатываются самые низкие значения).
Затем слой глотания просто обрабатывает любые прикосновения, которые он получает, ничего не делая.
Любые элементы управления в вашем всплывающем окне должны иметь приоритет перед слоем глотания, поэтому, если вы используете меню, вам нужно установить новый приоритет. Эти прикосновения будут затем обрабатываться первым элементом (а не слоем глотания).
Соответствующие функции, показывающие регистрацию слоя проглатывания с приоритетом -1024, обработку (игнорирование) всех касаний и добавление пункта меню с более высоким приоритетом, чем слой проглатывания:
// 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];
}
Способ сделать это для бедняков: добавьте ccmenuitemsprite и ccmenu с непрозрачностью ccsprite, равной 0, покрывая все, что вы не хотите, чтобы щелкнули. работает для меня, когда я просто не хочу связываться с диспетчером касания.