cocos2d-x 建立自己的层级窗口消息机制-续
如果没有看过cocos2d-x 建立自己的层级窗口消息机制,请先看它,否则,下面论述你可能不知道我在说什么。
最近在学习《design patterns》的时,看到其中的decorator设计模式,就想到了自己的最近刚写的cocos2d-x 建立自己的层级窗口消息机制,并思考比较了一下。因为decorator(wrapper)模式,通过继承基类并转接基类接口来达到一个添加额外功能或者职责的目的。这个模式在书中解决这样一个问题,不用为所有的类重新派生新类来实现添加边框的功能。这正是之前的层级窗口消息机制的弊端:
- 为了让控件加入我们的层级窗口消息机制。我们必须要派生所有cocos2d控件,去掉他们的消息注册功能。还需要为每个新添加的控件,到wmTouchDelegate里面添加nonTrivialTouchHandler标志。
- 原本的所有cocos2d控件,不需经过任何修改即可加入层级窗口消息机制。且新添加的控件不要到wmTouchDelegate中添加任何标志。
- 其实能实现清除消息注册功能的方法,不止继承,派生这一种。这里使用了另一种,通过在祖层(实际是BYLayer)的onEnter中添加部分功能(并调用原来的CCLayer::onEnter()),来实现清除消息注册。onEnter里的这个功能是通过遍历所有的子对象,判断他们是否继承自CCLayer,如果是,我们将调用setTouchEnable( false )来去掉他们消息注册功能。
通过权衡后,我放弃完全以改变cocos2d源码的承诺,因为仅仅为了区分某些继承自CCLayer的类是否重载了ccTouchX()函数而添加大量辅助功能去判断,实在是没太大必要。当初这么做主要是为了避免CCLayer::ccTouchBegin()中的CCAssert语句。但其实这个语句只是起警醒的作用。所以我唯一对cocos2d-x的修改就是将这个断言注释掉。这样的Debug模式下,就不会被中断了。
另外新的机制中,我定位BYTouchDelegate(旧版本中叫wmTouchDelegate)为一个内部转接类,就外界而言,并不知道有BYTouchDelegate的存在,任何继承自BYTouchDelegate的窗体,都要将ccTouchX()转接到byTouchX()(旧版本叫ccTouchX)。这样做方便我们对所有窗体都只调用ccTouchX(),而不用区分他们到底要使用ccTouchX()还是byTouchX()。除此以外,修改了部分代码,以提高BYTouchDelegate的效率。

上次由于种种原因没有给出完整源码,让部分读者只能私下给发信息才能获得源码。这次直接展示6个完整源码文件。以方便读者测试,使用,修改,请提出宝贵意见。
//// BYTouchDelegate.cpp// TableTest//// Created by jason on 12-12-25.////#include "BYTouchDelegate.h"#include "BYUtility.h"#pragma mark- input touchebool BYTouchDelegate::byTouchBegan(CCTouch *pTouch, CCEvent *pEvent){ //pass message to all children return passMessage( m_pOwner, pTouch, pEvent );}void BYTouchDelegate::byTouchMoved(CCTouch *pTouch, CCEvent *pEvent){ //special process for menu, we won't pass ccTouchMoved message to menu. Because we think menu doesn't need ccTouchMoved message in ios device where user always want to dray layer instead menu. The fllowing block for menu will only go once. int iNumMenus = m_pMenusClaimTouch->count(); for( int i = 0; i < iNumMenus; ++i ) { ( ( CCMenu* )m_pMenusClaimTouch->objectAtIndex( i ) )->ccTouchCancelled( pTouch, pEvent ); } if( iNumMenus > 0 ) { m_pMenusClaimTouch->removeAllObjects(); } //pass ccTouchMoved message to un-CCMenu item for( int i = 0; i < m_pItemsClaimTouch->count(); ++i ) { ( ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i ) )->ccTouchMoved( pTouch, pEvent ); }}void BYTouchDelegate::byTouchEnded(CCTouch *pTouch, CCEvent *pEvent){ //for menus for( int i = 0; i < m_pMenusClaimTouch->count(); ++i ) { ( ( CCMenu* )m_pMenusClaimTouch->objectAtIndex( i ) )->ccTouchEnded( pTouch, pEvent ); } m_pMenusClaimTouch->removeAllObjects(); //for items not menu for( int i = 0; i < m_pItemsClaimTouch->count(); ++i ) { ( ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i ) )->ccTouchEnded( pTouch, pEvent ); } m_pItemsClaimTouch->removeAllObjects();}void BYTouchDelegate::byTouchCancelled(CCTouch *pTouch, CCEvent *pEvent){ //for menus for( int i = 0; i < m_pMenusClaimTouch->count(); ++i ) { ( ( CCMenu* )m_pMenusClaimTouch->objectAtIndex( i ) )->ccTouchCancelled( pTouch, pEvent ); } m_pMenusClaimTouch->removeAllObjects(); //for items not menu for( int i = 0; i < m_pItemsClaimTouch->count(); ++i ) { ( ( CCLayer* )m_pItemsClaimTouch->objectAtIndex( i ) )->ccTouchCancelled( pTouch, pEvent ); } m_pItemsClaimTouch->removeAllObjects();}bool BYTouchDelegate::passMessage( CCNode* pParent, CCTouch *pTouch, CCEvent *pEvent ){ if( !pParent || !pParent->isVisible() ) { return false; } //hande message to items int iNumChildren = 0; CCArray* pChildren = NULL; //if the item'size > 1, check whether use touches it. Such as TableView. //some items doesn't get size. they are medium for maintaining some children. Such as CCTableViewCell. if( pParent->getContentSize().width * pParent->getContentSize().height > 1.0f ) { CCPoint pt = pTouch->getLocation(); CCRect rcBoundingBox( 0, 0, pParent->getContentSize().width, pParent->getContentSize().height ); //do this only for efficiency, because convertToNodeSpace is heavier than nodeToWorldTransform(). rcBoundingBox = CCRectApplyAffineTransform( rcBoundingBox, pParent->nodeToWorldTransform() ); //whether hit the node if( !rcBoundingBox.containsPoint( pt ) ) { return false; } } pChildren = pParent->getChildren(); //no children, but user touch this item, so return true. if( !pChildren ) { return true; } iNumChildren = pParent->getChildren()->count(); //pass to all children for( int iChildIndex = 0; iChildIndex < iNumChildren; ++iChildIndex ) { //if the item claims the touch message bool bClaim = false; CCLayer* pLayer = NULL; CCNode* pNode = NULL; pNode = ( CCNode* )( pChildren->objectAtIndex( iChildIndex ) ); assert( pNode ); //if it's layer, we should invoke its ccTouchBegan()//Make sure that you have commented the CCAssertion statement in CCLayer::ccTouchBegan(). if( ( pLayer = dynamic_cast< CCLayer* >( pNode ) ) ) { bClaim = pLayer->ccTouchBegan( pTouch, pEvent ); } //pass message for its child if it doesn't derive BYTouchDelegate. Because child deriving BYTouchDelegate has passed message via ccTouchBegan(). if( !dynamic_cast< BYTouchDelegate* >( pNode ) ) { //items who doesn't derive from BYTouchDelegate can't pass touch message to its children, //so we have to help them to pass touch message. passMessage( pNode, pTouch, pEvent ); } //if this item is interested in this message, add it to array for other messages if( bClaim ) { //we don't use condition of &typeid( *pNode ) == &typeid( CCMenu ) since user may derive CCMenu. if ( dynamic_cast< CCMenu* >( pNode ) ) { m_pMenusClaimTouch->addObject( pNode ); } else { m_pItemsClaimTouch->addObject( pNode ); } } } return true;}
- 工程使用时,只需包含BYCocos.h即可。确保cocos2d-x中CCLayer.cpp内的CCLayer::ccTouchBegan()函数内的CCAssertion()语句被注释。这是新层级窗口消息机制对cocos2d-x源码的唯一修改。这个体系中有一点需要注意,因为我们添加了窗体点击判断,所以加入这个体系的窗口应该显示地,正确地设置自己的窗体大小。(这经常是一些bug的原因所在)另外由于使用了dynamic_cast,可能会让部分读者担心效率问题。但个人认为dynamic_cast并不是C++的垃圾,其带来的性能影响应该是很有限的。代码上有比较多的注释,如果注释有误还请告诉我。习惯练习英文注释了,希望我的注释没太大语法错误,能够让你理解。这里就不多讲述了。
- 1楼kangkangr2011昨天 23:02
- 不错。。。。