您的位置:首页 > 移动开发 > Cocos引擎

Cocos-2d 关于SwallowTouch,进一步解释触摸事件分发机制

2013-05-17 09:52 501 查看
问题情境模拟一个类似游戏提示信息的层:1.游戏主场景可触摸,可交互;2.当提示显示提示信息时,只有提示信息这一层可触摸同用户交互,其背景则不能继续响应触摸事件3.当提示信息层从主场景中移除之后,游戏主场景才能继续响应触摸事件进行交互。这里,我们暂时把“提示信息层”称为SwallowTouchLayer;将游戏主场景曾称为GameLayer进一步描述上述情景的实质问题:添加一个层“吃掉”(swallow)原有层touch事件1.SwallowTouchLayer必须swallowTouches = YES;2.SwallowTouchLayer的触摸事件优先级 > GameLayer的触摸事件优先级为了解决上述问题,我们首先要了解cocos-2d中触摸事件的分发机制,问题解决就明了了。这里以HellowWorld为例,在HellowWorld之上添加一个SwallowTouchLayer参照下图





代码实现1.首先在HellowWorldLayer.m中添加一个新的按钮项 CCMenuItem *addSwallowTouchLayerItem = [CCMenuItemFontitemWithString:@"addSwallowTouchLayer"block:^(id sender){ [selfaddChild:[[SwallowTouchLayeralloc]init]]; }]; CCMenu *menu = [CCMenumenuWithItems:itemAchievement, itemLeaderboard,addSwallowTouchLayerItem,nil];

2.实现SwallowTouchLayer

SwallowTouchLayer.h
#import<Foundation/Foundation.h>#import"cocos2d.h"
@interface SwallowTouchLayer :CCLayer{ CCSprite *grayBackground; CCMenu *menu;}
@endSwallowTouchLayer.m

//// SwallowTouchLayer.m// SwallowTouch//
#import"SwallowTouchLayer.h"
@implementation SwallowTouchLayer- (id)init{ if(self = [super init]) { //开启接收触摸事件 self.isTouchEnabled = YES;
//添加灰度背景 grayBackground = [CCSprite spriteWithFile:@"swallowBackground.png"]; grayBackground.position =ccp(240,160); [self addChild:grayBackground];
//初始化Menu CCMenuItem *menuItem = [CCMenuItemToggle itemWithTarget:self selector:@selector(removeTeaching) items:[CCMenuItemImage itemWithNormalImage:@"Icon.png" selectedImage:nil], [CCMenuItemImage itemWithNormalImage:@"Icon.png" selectedImage:nil], nil]; //设置第一步教程的位置 menuItem.position =ccp(0,0);
menu = [CCMenu menuWithItems:menuItem,nil]; menu.position =ccp(240,160);
//添加教程 [self addChild:menu]; } return self;}
- (void)onEnter{ //在super onEnter中调用自身和孩子节点的registerWithTouchDispatcher,添加触摸代理 [super onEnter];
//重新调整menu响应优先级,使其能够响应触摸事件,视实际情况,可以省略该步 [menu setHandlerPriority:kCCMenuHandlerPriority -2];}
- (void)onExit{ [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self]; [super onExit];}
- (void)dealloc{ [super dealloc];}
//将自己从父场景中移除- (void)removeTeaching{ [self removeFromParentAndCleanup:YES];}
#pragma mark - Swallow Touch Input/* *将CCLayer注册到CCTargetedTouchDelegate中,并将其响应优先级调至大于(等于)要覆盖的 *优先级对象的响应优先级 */- (void)registerWithTouchDispatcher{ CCTouchDispatcher *touchDispatcher = [[CCDirector sharedDirector] touchDispatcher]; [touchDispatcher addTargetedDelegate:self priority:kCCMenuHandlerPriority -1 swallowsTouches:YES];}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{ //如果return NO,则不阻拦触摸消息 return YES;} @end

原理解释
1.当点击HellowWorldLayer中的addSwallowTouch按钮,[selfaddChild:[[SwallowTouchLayeralloc]init]];添加SwallowTouchLayer。
2.在init函数中self.isTouchEnabled =YES;开启触摸事件。在此处设置断点,运行程序,跟踪一下运行实质:2.1响应CCLayer的setIsTouchEnable消息:-(void) setIsTouchEnabled:(BOOL)enabled{ if( isTouchEnabled_ != enabled ) { isTouchEnabled_ = enabled; //这里是属性修改由NO改成了YES if( isRunning_ ) { if( enabled ) [self registerWithTouchDispatcher]; else { CCDirector *director = [CCDirector sharedDirector]; [[director touchDispatcher] removeDelegate:self]; } } }}
2.2此时SwallowTouchLayer只是初始化并未添加到场景中,所以isRunnig_为NO,因此实际上在init函数中虽然self.isTouchEnabled = YES;但是只属性的修改,并没有真正将触摸事件注册到消息分发列表中(registerWithTouchDispatcher没有执行)
3.在init函数中初始化黄色背景和menu按钮,为按钮添加绑定事件selector:@selector(removeTeaching)这里removeTeaching是将SwallowTouchLayer自身从父场景中移除掉
4.上述只是SwallowTouchLayer的初始化工作,当HellowWorld中[self addChild:...];将SwallowTouchLayer添加到场景中,首先调用的是OnEnter();进入场景消息响应函数
5.首先[super onEnter];调用父类CCLayer的onEnter函数:
-(void) onEnter{ #ifdef __CC_PLATFORM_IOS // register 'parent' nodes first // since events are propagated in reverse order if (isTouchEnabled_) [self registerWithTouchDispatcher]; //这个函数,我们在SwallowTouchLayer中已重写 #elif defined(__CC_PLATFORM_MAC) CCDirector *director = [CCDirector sharedDirector]; CCEventDispatcher *eventDispatcher = [director eventDispatcher]; if( isMouseEnabled_ ) [eventDispatcher addMouseDelegate:self priority:[self mouseDelegatePriority]]; if( isKeyboardEnabled_) [eventDispatcher addKeyboardDelegate:self priority:[self keyboardDelegatePriority]]; if( isTouchEnabled_) [eventDispatcher addTouchDelegate:self priority:[self touchDelegatePriority]];#endif // then iterate over all the children [super onEnter]; //继续找父类CCNode调用onEnter}详解:(1)由于我们在init函数中已经修改了isTouchEnable为YES,所以自身调用注册触摸消息分发registerWithTouchDispatcher我们在SwallowTouchLayer中重写了这个函数,在这个函数中CCTouchDispatcher *touchDispatcher = [[CCDirectorsharedDirector]touchDispatcher];[touchDispatcheraddTargetedDelegate:self priority:kCCMenuHandlerPriority -1 swallowsTouches:YES];
[b]绑定触摸事件代理(CCLayer默认遵守TargetedTouchDelegate),而且priority(触摸响应优先级)设置为最高(KCCMenuHandlerPriority=-128,数值越小优先级越高)[/b]将swallowTouches设为YES,即在触摸响应层中,结束触摸事件处理,不再交予别的层继续处理。
(2)[super onEnter];找到CCNode的onEnter -(void) onEnter{ [children_ makeObjectsPerformSelector:@selector(onEnter)]; [self resumeSchedulerAndActions]; isRunning_ =YES;}
让所有children响应onEnter;确认运行(isRunning_=YES);此时CCLayer的onEnter事件响应完毕6.在4中我们只是了解了SwallowTouchLayer的onEnter中的第一步 [super onEnter];- (void)onEnter{//1.super onEnter中调用自身和孩子节点的registerWithTouchDispatcher,添加触摸代理 [superonEnter];
//2.重新调整menu响应优先级,使其能够响应触摸事件,视实际情况,可以省略该步 [menusetHandlerPriority:kCCMenuHandlerPriority -2];//-130}第2步,调整触摸优先级,将menu按钮的优先级跳到最高还记得我们在[super onEnter];中调用SwallowTouchLayer的registerWithTouchDispacher;我们将SwallowTouchLayer自身的响应优先级设置为kCCMenuHandlerPriority -1;(-129)所以,在Cocos-2d中触摸优先级响应最高时-128;而我们将SwallowTouchLayer的优先级设为-129,又将menu的优先级设为了-130;而menu默认SwallowTouch=YES;我们在SwallowTouchLayer的registerWithTouchDispacher;函数中同样设置了SwallowTouch设为了YES;因此,当触摸menu时,menu的优先级最高,所以第一个响应;然后将触摸Swallow掉,当触摸屏幕非menu处,menu没有接收到触摸,SwallowTouchLayer接收到触摸,响应然后Swallow掉,所以HellowWorld不会响应触摸,看起来就是HelloWorld层的按钮全部失效。(关于Swallow具体可以参照上一篇文章Cocos-2d CCLayer的触摸响应CCTouchDelegate和CCStandardTouchDelegate和 CCTargetedTouchDelegate)
7.在点击menu按钮,移除SwallowTouchLayer。在onExit退出该层时,移除掉触摸代理HelloWorldLayer又可继续交互,响应触摸。
8.说明:这里只是一个小例子,演示了SwallowTouchLayer优先拦截touch响应。当然我们可以在SwallowTouchLayer中添加touchBegan、touchMove、touchEnd代理方法,实现SwallowTouchLayer特有的触摸方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息