全面剖析Cocos2d游戏触摸机制 (下)

【触摸消息的分发】


当我们触摸屏幕时触摸事件被CCTouchDispatcher类全盘接管,所以触摸消息也就由CCTouchDispatcher类来负责分发:
分发触摸消息的四个方法,注意接管的四个方法和Cocoa Touch的触摸方法的第一个参数一样是:(NSSet *)touches.

// 分发touchesBegan消息
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if( dispatchEvents )
        [self touches:touches withEvent:event withTouchType:kCCTouchBegan];
}

// 分发touchesMoved消息
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if( dispatchEvents ) 
        [self touches:touches withEvent:event withTouchType:kCCTouchMoved];
}

// 分发touchesEnded消息
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if( dispatchEvents )
        [self touches:touches withEvent:event withTouchType:kCCTouchEnded];
}

// 分发touchesCancelled消息
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    if( dispatchEvents )
        [self touches:touches withEvent:event withTouchType:kCCTouchCancelled];
}
@end

上述四个方法都调用同一个方法:
-(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigned int)idx;

该方法太长,我采取分段讲解:
第一段代码:
              //断言检测idx的值,最多四个触摸方法,下标0、1、2、3
    NSAssert(idx < 4, @"Invalid idx value");

    id mutableTouches;
      //注意开始分发触摸事件之前,先上锁
    locked = YES;
    
    // optimization to prevent a mutable copy when it is not necessary
      //获取关于标准协议的触摸事件处理器的个数以及关于目标协议 
                   的触摸事件处理器的个数
    unsigned int targetedHandlersCount = [targetedHandlers count];
    unsigned int standardHandlersCount = [standardHandlers count];
      //此处的needsMutableSet是为了判断这两个个数是否都不为
                   零,为什么要进行判断呢?请看下面!
           
    BOOL needsMutableSet = (targetedHandlersCount && standardHandlersCount);

//若这两个个数都不为零,说明我们既要分发关于标准协议的触
                 摸方法也要分发关于目标协议的触摸方法,那就必须两次使用  
                touches对象的值,为了安全起见,此处使用mutableCopy来
                拷贝一份touches.
    mutableTouches = (needsMutableSet ? [touches mutableCopy] : touches);
      //根据idx值来取出handlerHelperData[idx]值,从而知道
                  了代理对象以及转发的方法选择器。
    struct ccTouchHandlerHelperData helper = handlerHelperData[idx];

第二段代码:

//
    //分发关于标准协议的触摸消息
//
if( targetedHandlersCount > 0 ) {
        //标准协议的四个触摸方法的第一参数类型为UITouch *,
          所以先从touches取出touch
    for( UITouch *touch in touches ) {
         //循环取出关于目标协议触摸事件处理器数组中的每一个触
                           摸事件处理器
        for(CCTargetedTouchHandler *handler in targetedHandlers) {
            
              //声明一个claimed标志来获取目标协议的ccTouchBegan方法的返回值,因为我们前面提到过关于目标协议的ccTouchBegan方法如果返回YES,后续的ccTouchMoved、ccTouchEnded、ccTouchCancelled才会被分发到触摸消息。
            BOOL claimed = NO;
            if( idx == kCCTouchBegan ) {
                claimed = [handler.delegate ccTouchBegan:touch withEvent:event];
                  //如果ccTouchBegan方法返回YES就将触摸对象touch,存储到claimedTouches数组中。
                if( claimed )
                    [handler.claimedTouches addObject:touch];
            } 
            
            // else (moved, ended, cancelled)
              //判断claimedTouches数组中是否包含touch对象,就可以知道是否哟转发后续的三个触摸方法了。
            else if( [handler.claimedTouches containsObject:touch] ) {
                claimed = YES;
                  //此处的按位与操作是为了验证enabledSelectors标记中是否包含helper.type—触摸方法的标记,若有则转发后续的三个触摸方法。
                if( handler.enabledSelectors & helper.type )
                    [handler.delegate performSelector:helper.touchSel withObject:touch withObject:event];
                //当我们触摸屏幕时必定会触发两个触摸事件:ccTouchBegan和ccTouchEnded,所以此处采用这种按位与操作是为了在ccTouchEnded或者ccTouchCancelled消息分发过来时移除claimedTouches数组中的touch对象。
                if( helper.type & (kCCTouchSelectorCancelledBit | kCCTouchSelectorEndedBit) )
                    [handler.claimedTouches removeObject:touch];
            }
        //如果ccTouchBegan返回YES并且阻止消息进一步转发的话,那么就需要将    mutableTouches中的触摸点touch删除,这样会导致[mutableTouches count] == 0,从而保证对于CCLayer---标准协议的触摸事件也无法继续转发下去,从而达到swallowsTouches的目的。        
            if( claimed && handler.swallowsTouches ) {
                if( needsMutableSet )
                    [mutableTouches removeObject:touch];
                break;
            }
        }
    }
}

第三段代码:

//
// 关于标准协议的触摸消息的转发    
//
    //验证注册的个数以及mutableTouches中触摸点的个数
    if( standardHandlersCount > 0 && [mutableTouches count]>0 ) {

     // 遍历standardHandlers数组取出每一个触摸事件处理器
        for( CCTouchHandler *handler in standardHandlers ) {
        //验证触摸方法的实现与否,并转发响应的触摸消息
            if( handler.enabledSelectors & helper.type )
                [handler.delegate performSelector:helper.touchesSel withObject:mutableTouches withObject:event];
        }
    }
    //最后记得释放mutableTouches
    if( needsMutableSet )
        [mutableTouches release];    


第四段代码:
    
    //触摸消息全部分发完后,记得解锁
    locked = NO;
    //将等待移除的代理对象移除
    if( toRemove ) {
        toRemove = NO;
        for( id delegate in handlersToRemove )
            [self forceRemoveDelegate:delegate];
        [handlersToRemove removeAllObjects];
    }
    //将等待加入的标准协议或者目标协议的触摸事件处理器加入到相
              应的数组中
    if( toAdd ) {
        toAdd = NO;
        for( CCTouchHandler *handler in handlersToAdd ) {
            Class targetedClass = [CCTargetedTouchHandler class];
            if( [handler isKindOfClass:targetedClass] )
                [self forceAddHandler:handler array:targetedHandlers];
            else
                [self forceAddHandler:handler array:standardHandlers];
        }
        [handlersToAdd removeAllObjects];
    }

    //根据toQuit标记是否移除所有的代理对象
    if( toQuit ) {
        toQuit = NO;    
        [self forceRemoveAllDelegates];
    }


至此,触摸消息分发完成。

最后介绍一下onExit方法:
i:
-(void)onExit
{
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
    [super onExit];
}
该方法中一般要移除代理对象,因为切换场景后,要避免再次触摸时将触摸消息分发到该对象上,所以必须要将此代理对象移除,告诉CCTouchDispatcher不要将触摸消息分发给我了!!!

ii:
-(void) removeDelegate:(id) delegate方法实现:

-(void) removeDelegate:(id) delegate
{
    if( delegate == nil )
        return;
    //没有上锁的情况下,直接移除。
    if( ! locked ) {
        [self forceRemoveDelegate:delegate];
    } else {
         //若上锁的情况下,先暂时存在handlersToRemove数组
                          中,等待消息分发完成解锁后,再移除delegate    
         [handlersToRemove addObject:delegate];
        toRemove = YES;
    }
}

iii:最终移除delegate的方法:
//遍历targetedHandlers以及standardHandlers数组移除关于delegate的触摸事件处理器。
-(void) forceRemoveDelegate:(id)delegate
{
    for( CCTouchHandler *handler in targetedHandlers ) {
        if( handler.delegate == delegate ) {
            [targetedHandlers removeObject:handler];
            break;
        }
    }
    
    for( CCTouchHandler *handler in standardHandlers ) {
        if( handler.delegate == delegate ) {
            [standardHandlers removeObject:handler];
            break;
        }
    }    
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值