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

问题情境

模拟一个类似游戏提示信息的层:

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;

}


@end

SwallowTouchLayer.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];        

    }

   returnself;

}


- (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,则不阻拦触摸消息

   returnYES;

}  

@end


原理解释

1.当点击HellowWorldLayer中的addSwallowTouch按钮,

[selfaddChild:[[SwallowTouchLayeralloc]init]];添加SwallowTouchLayer


2.init函数中

self.isTouchEnabled =YES;开启触摸事件。在此处设置断点,运行程序,跟踪一下运行实质:

2.1响应CCLayersetIsTouchEnable消息:

-(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];调用父类CCLayeronEnter函数:


-(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函数中已经修改了isTouchEnableYES,所以自身调用注册触摸消息分发registerWithTouchDispatcher

我们在SwallowTouchLayer中重写了这个函数,在这个函数中

CCTouchDispatcher *touchDispatcher = [[CCDirectorsharedDirector]touchDispatcher];

[touchDispatcheraddTargetedDelegate:self

                            priority:kCCMenuHandlerPriority -1

                                         swallowsTouches:YES];


绑定触摸事件代理(CCLayer默认遵守TargetedTouchDelegate),而且priority(触摸响应优先级)设置为最高(KCCMenuHandlerPriority=-128,数值越小优先级越高)

swallowTouches设为YES,即在触摸响应层中,结束触摸事件处理,不再交予别的层继续处理。


(2)[super onEnter];找到CCNodeonEnter 

-(void) onEnter

{

[children_ makeObjectsPerformSelector:@selector(onEnter)];

[self resumeSchedulerAndActions];

    

isRunning_ =YES;

}


让所有children响应onEnter;确认运行(isRunning_=YES);此时CCLayeronEnter事件响应完毕

6.4中我们只是了解了SwallowTouchLayeronEnter中的第一步 [super onEnter];

- (void)onEnter

{

         //1.super onEnter中调用自身和孩子节点的registerWithTouchDispatcher,添加触摸代理

        [superonEnter];


   //2.重新调整menu响应优先级,使其能够响应触摸事件,视实际情况,可以省略该步

        [menusetHandlerPriority:kCCMenuHandlerPriority -2];//-130

}

2步,调整触摸优先级,将menu按钮的优先级跳到最高

还记得我们在[super onEnter];中调用SwallowTouchLayerregisterWithTouchDispacher;我们将SwallowTouchLayer自身的响应优先级设置为kCCMenuHandlerPriority -1;(-129)

所以,在Cocos-2d中触摸优先级响应最高时-128;而我们将SwallowTouchLayer的优先级设为-129,又将menu的优先级设为了-130;

menu默认SwallowTouch=YES;我们在SwallowTouchLayerregisterWithTouchDispacher;函数中同样设置了SwallowTouch设为了YES

因此,当触摸menu时,menu的优先级最高,所以第一个响应;然后将触摸Swallow掉,

当触摸屏幕非menu处,menu没有接收到触摸,SwallowTouchLayer接收到触摸,响应然后Swallow掉,所以HellowWorld不会响应触摸,看起来就是HelloWorld层的按钮全部失效。

(关于Swallow具体可以参照上一篇文章Cocos-2d CCLayer的触摸响应CCTouchDelegateCCStandardTouchDelegate CCTargetedTouchDelegate)


7.在点击menu按钮,移除SwallowTouchLayer

onExit退出该层时,移除掉触摸代理

HelloWorldLayer又可继续交互,响应触摸。


8.说明:

这里只是一个小例子,演示了SwallowTouchLayer优先拦截touch响应。当然我们可以在SwallowTouchLayer中添加touchBegantouchMovetouchEnd代理方法,实现SwallowTouchLayer特有的触摸方法。




  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值