Cocos-2d 详解CCTouchDispatche类和触摸注册原理

来源:互联网 发布:制作海报用什么软件 编辑:程序博客网 时间:2024/04/28 12:48

在之前的博客中,我们提到了触摸分发机制,解释了CCTargetedTouchDelegate和CCStandardTouchDelegate和其中的touchBegan,touchMove,touchEnd方法;

但在此之前,一般是在onEnter的时候注册触摸响应事件。

 

-(void) registerWithTouchDispatcher { [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0]; }
下面主要介绍下CCTouchDispatcher类

声明:本文主要转自:MOMO雨松研究院相关文章

在这里对原作者表示感谢!


CCTouchDispatcher是个单例,程序刚启动时,导演类会将其设为EAGLView触摸代理,当玩家有触摸操作时,系统会自动调用它的对应方法。CCLayer通过registerWithTouchDispatcher方法将自己注册到CCTouchDispatcher单例中,CCTouchDispatcher检测到触摸信息后,会在注册队列中遍历这些对象,调用它们的响应方法,使它们可以响应触摸事件。

NSMutableArray* standardHandlers

标准注册队列,其实就是一个数组,当CCLayer对象调用registerWithTouchDispatcher方法时,会将其添加到该数组中,所有该数组中的对象在玩家触摸屏幕时都会被调用自身的相关方法,这些方法有(ccTouchesBegan:withEvent:)、(ccTouchesMoved:withEvent:)、(ccTouchesEnded:withEvent:)和(ccTouchesCancelled:withEvent:)。

NSMutableArray* targetedHandlers

目标注册队列,和standardHandlers一样也是个数组,它的作用和standardHandlers大体相同,唯一的区别就是targetedHandlers可以“吞掉”自身响应的事件,以阻止信息继续向下传递,而standardHandlers没有此功能。CCMenu就是向该队列注册的。

BOOL locked

是否被锁定,当玩家做了触摸操作,CCTouchDispatcher开始遍历注册队列时,该变量会被置为YES,此时是不能向队列中添加新的对象,或者从队列中删除对象的,只有当遍历结束,locked被置回NO后,才可以添加或删除,如果在锁定过程中执行了这两种操作,那么操作对象会被放入对应的临时队列中,解琐后再把他们同步到注册队列中。

注解:也就是说,如果你正在响应一个对象的触摸事件,此时是不能再给另一个对象绑定触摸或者删除触摸的。

BOOL toAdd

是否有等待添加的对象,当CCTouchDispatcher被锁定时,如果有新的对象要注册进来,则会被添加进一个临时队列,该队列的所有成员都是待注册对象,如果队列不为空,toAdd就会被值为YES,那么当锁定解除后,就会把该队列的所有成员全部移入standardHandlers或targetedHandlers(它会根据对象的类型自行区分),操作完成后清空临时队列,toAdd置回NO。

BOOL toRemove

是否有等待删除的对象,同添加一样,在CCTouchDispatcher被锁定时,需要删除的对象会被放入另一个临时队列,该队列的成员都是等待注销的,如果队列不为空,toRemove就会被值为YES,锁定解除后再将队列成员依此从注册队列中删除(具体哪个队列会自动判断),操作完成后清空临时队列,toRemove置回NO。

NSMutableArray* handlersToAdd

存放等待注册对象的队列,如前面介绍的,这个数组就是存放待注册对象的临时队列。

NSMutableArray* handlersToRemove

存放等待注销对象的队列,如前面介绍的,这个数组就是存放待注销对象的临时队列。

BOOL toQuit

是否要清空注册队列,同toRemove一样,清空队列同样要等到解锁后才能执行,这个变量就是标记是否需要清空的。

BOOL dispatchEvents

有触摸操作时,是否遍历注册队列,默认一直是YES,需要时可以手动将其置为NO。

struct ccTouchHandlerHelperData handlerHelperData[kCCTouchMax]

一个长度为kCCTouchMax的结构体队列,kCCTouchMax = 4,每个成员都是一个ccTouchHandlerHelperData结构体,用来存储kCCTouchBegan、kCCTouchMoved、kCCTouchEnded和kCCTouchCancelled四种触摸事件的调用方法。结构体有三个数据,前两个是SEL,standardHandlers中的对象会调用touchesSel,targetedHandlers中的对象会调用touchSel,最后的ccTouchSelectorFlag是枚举,用来标记触摸事件的类型。

*四种类型对应的touchesSel分别为:(ccTouchesBegan:withEvent:)、(ccTouchesMoved:withEvent:)、(ccTouchesEnded:withEvent:)、(ccTouchesCancelled:withEvent:)。

*四种类型对应的touchSel分别为:(ccTouchBegan:withEvent:)、(ccTouchMoved:withEvent:)、(ccTouchEnded:withEvent:)、(ccTouchCancelled:withEvent:)。

*也就是说注册队列中的对象至少要实现上述两组方法中的一组,否则有可能会抛异常。

以下是CCTouchDispatcher的方法

+ (CCTouchDispatcher*)sharedDispatcher

获取单例对象,单例类的惯用方法。

-(id) init

初始化方法,将dispatchEvents置为YES,toRemove、toAdd、toQuit、locked均置为NO,给targetedHandlers、standardHandlers、handlersToAdd、handlersToRemove开辟内存空间,并给handlerHelperData中的成员赋值。

-(void) forceAddHandler:(CCTouchHandler*)handler array:(NSMutableArray*)array

向队列array中添加CCTouchHandler对象,CCTouchHandler类的作用就是封装要注册触摸功能的对象,它有两个重要属性,delegate和priority,delegate就是要注册的对象,而priority是它的优先级,优先级越高的对象越先接收到触摸信息。此方法中会根据handler.priority的大小把它插入到队列的适合的位置上。

-(void) addStandardDelegate:(id<CCStandardTouchDelegate>) delegate priority:(int)priority

向standardHandlers队列中添加对象,参数priority为优先级,在一开始会先根据delegate和priority创建一个CCStandardTouchHandler对象(CCStandardTouchHandler是CCTouchHandler的子类,delegate是它的代理),然后再判断当前是否被锁定,如未锁定,则调用前面说的forceAddHandler方法直接添加进standardHandlers数组,如已被锁定,则添加到handlersToAdd数组中等待同步。

*CCLayer中的registerWithTouchDispatcher方法其实就是调用了该函数,priority为0。

-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches

向targetedHandlers队列中添加对象,参数swallowsTouches表示是否有“吞”事件的功能,YES为有,其他同上。

*CCMenu中的registerWithTouchDispatcher方法其实就是调用了该函数,priority为-128,swallowsTouches为YES。

-(void) forceRemoveDelegate:(id)delegate

从注册队列中删除一个对象,即取消它的触摸功能。该方法会依此遍历standardHandlers和targetedHandlers两个数组,如果某个成员的delegate参数和要注销的对象是同一个,那么就将该成员从队列中删除。

-(void) removeDelegate:(id) delegate

注销一个对象,如果未锁定,则调用上面的forceRemoveDelegate直接将其删除,如果被锁定,则添加进toRemove等待同步。

-(void) forceRemoveAllDelegates

清空standardHandlers和targetedHandlers数组,即取消所有对象的触摸功能。

-(void) removeAllDelegates

注销全部对象,如果未锁定,则调用上面的forceRemoveAllDelegates直接清空队列,如果被锁定,则将toQuit置为NO,等解锁后再清空。

-(CCTouchHandler*) findHandler:(id)delegate

根据代理对象delegate在队列中查找封装它的CCTouchHandler对象,并返回。

NSComparisonResult sortByPriority(id first, id second, void *context)

数组成员的排序方式,该方法用来重新整理注册队列的顺序,将priority值小的放在后面,大的放在前面,因为队列是从前向后遍历的,所以这样可以保证优先级高的对象先收到信息。

-(void) rearrangeHandlers:(NSMutableArray*)array

根据sortByPriority规则给数组array重新排序(由规则可知该数组存的必须是CCTouchHandler对象)。

-(void) setPriority:(int) priority forDelegate:(id) delegate

改变对象的优先级,之后会调用rearrangeHandlers方法给standardHandlers和targetedHandlers重新排序。

-(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigned int)idx

CCTouchDispatcher最重要、最核心的方法,作用就是将玩家的触摸信息传递给在它这里注册过的对象(比如CCLayer、CCMenu)。touches和event是系统提供的,不多说了,idx就是触摸的类型,包括:kCCTouchBegan、kCCTouchMoved、kCCTouchEnded和kCCTouchCancelled。工作流程如下:

首先,将locked置为YES,锁定队列;接着就是遍历targetedHandlers(CCMenu注册的队列,即可以“吞掉”事件),如果idx为kCCTouchBegan,那么会调用(ccTouchBegan:withEvent:)方法,并判断是否是有效触碰,如果是,将该信息添加到对象的claimedTouches中。如果事件的claimedTouches集合已经包含了该触摸信息,则表示现在是kCCTouchMoved、kCCTouchEnded或者kCCTouchCancelled状态,那么就调用这些状态对应的方法,最后判断是否要吞掉事件,如果是则停止遍历;然后遍历standardHandlers,流程和targetedHandlers大致相同,只是没有吞事件的步骤了;两个队列都遍历完成后,locked被置回NO,接下来的工作就是同步添加和删除的操作了。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

玩家刚刚触碰屏幕时系统调用的方法(导演已将其设为触摸代理),如果dispatchEvents为YES,则调用(touches:withEvent:withTouchType:)方法,类型为kCCTouchBegan。

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

手指在屏幕上滑动时系统调用的方法,如果dispatchEvents为YES,则调用(touches:withEvent:withTouchType:)方法,类型为kCCTouchMoved。

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

手指离开屏幕时系统调用的方法,如果dispatchEvents为YES,则调用(touches:withEvent:withTouchType:)方法,类型为kCCTouchEnded。

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

系统中断时调用的方法,如果dispatchEvents为YES,则调用(touches:withEvent:withTouchType:)方法,类型为kCCTouchCancelled。

以上就是CCTouchDispatcher的工作原理,如果读者一时没有看懂也没关系,因为它在cocos2d中的作用就是底层支持,很少需要对其进行直接操作,我们只要知道如何使用CCLayer和CCMenu,以及注册触摸功能就已经足够开发游戏了。

声明:本文主要转自:MOMO雨松研究院相关文章

在这里对原作者表示感谢!




原创粉丝点击