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

来源:未知 责任编辑:智问网络 发表时间:2013-10-22 19:17 点击:

问题情境
模拟一个类似游戏提示信息的层:
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响应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];

绑定触摸事件代理(CCLayer默认遵守TargetedTouchDelegate),而且priority(触摸响应优先级)设置为最高(KCCMenuHandlerPriority=-128,数值越小优先级越高)
将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特有的触摸方法。


 

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    用户名: 验证码:点击我更换图片
    最新评论 更多>>

    推荐热点

    • cocos2d-x学习笔记(19)--label 、label atlas
    • cocos2d-x学习笔记(23)--地图的使用3--CCTMXLayer
    • Cocos2d-x学习(一):HelloWorld
    • cocos2dx在xcode下开发,编译到android上(2)
    • cocos2d 设置屏幕默认方向
    • Cocos2d-x 2.0 之 Actions “三板斧” 之一
    • cocos2d-x学习笔记(22)--地图的使用2(TMX) --Z-Order、AnchorPoi
    • cocos2d-x学习笔记(18)--游戏打包(windows平台)
    • cocos2d-x学习笔记(16)--spritesheet(精灵表单)
    网站首页 - 友情链接 - 网站地图 - TAG标签 - RSS订阅 - 内容搜索
    Copyright © 2008-2015 计算机技术学习交流网. 版权所有

    豫ICP备11007008号-1