cocos2d-x CCMenu详细源码分析

来源:互联网 发布:美国签证 知乎 编辑:程序博客网 时间:2024/05/16 01:27

.h

typedef enum {    kCCMenuStateWaiting, // 表示没有菜单项被选中    kCCMenuStateTrackingTouch // 表示有菜单项被选中} tCCMenuState; enum {    //* priority used by the menu for the event handler    kCCMenuHandlerPriority = -128,}; class CC_DLL CCMenu : public CCLayerRGBA // CCMenu的基类居然是CCLayer!{    bool m_bEnabled; // CCMenu是否接收触摸事件     public:    CCMenu() : m_pSelectedItem(NULL) {}    virtual ~CCMenu(){}     // 创建一个空菜单    static CCMenu* create();     // 根据多个菜单项列创建菜单    static CCMenu* create(CCMenuItem* item, ...);     // 通过子菜单项的数组创建菜单    static CCMenu* createWithArray(CCArray* pArrayOfItems);     // 通过一个菜单项创建菜单    static CCMenu* createWithItem(CCMenuItem* item);         // 通过多个菜单项列创建菜单    static CCMenu* createWithItems(CCMenuItem *firstItem, va_list args);     // 初始化空菜单    bool init();     // 通过子菜单项数组初始化菜单    bool initWithArray(CCArray* pArrayOfItems);     // 垂直方向默认间隙排列    void alignItemsVertically();    //垂直方向指定间隙排列    void alignItemsVerticallyWithPadding(float padding);     // 水平方向默认间隙排列    void alignItemsHorizontally();    // 水平方向指定间隙排列    void alignItemsHorizontallyWithPadding(float padding);     // 通过每行个数排列    void alignItemsInColumns(unsigned int columns, ...);    void alignItemsInColumns(unsigned int columns, va_list args);    // ?    void alignItemsInColumnsWithArray(CCArray* rows);     // 通过每列个数排列    void alignItemsInRows(unsigned int rows, ...);    void alignItemsInRows(unsigned int rows, va_list args);    // ?    void alignItemsInRowsWithArray(CCArray* columns);     // 设置事件处理优先级,默认为kCCMenuTouchPriority    void setHandlerPriority(int newPriority);     // 基类方法    virtual void addChild(CCNode * child);    virtual void addChild(CCNode * child, int zOrder);    virtual void addChild(CCNode * child, int zOrder, int tag);    virtual void registerWithTouchDispatcher();    virtual void removeChild(CCNode* child, bool cleanup);     // 触摸事件    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);    virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);    virtual void ccTouchCancelled(CCTouch *touch, CCEvent* event);    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);     // 退出调用函数    virtual void onExit();     // ?    virtual void setOpacityModifyRGB(bool bValue) {CC_UNUSED_PARAM(bValue);}    virtual bool isOpacityModifyRGB(void) { return false;}    // 获取/设置菜单是否可用    virtual bool isEnabled() { return m_bEnabled; }    virtual void setEnabled(bool value) { m_bEnabled = value; }; protected:    // 返回被触摸的菜单项    CCMenuItem* itemForTouch(CCTouch * touch);    tCCMenuState m_eState;    CCMenuItem *m_pSelectedItem;};



.cpp  


static std::vector<unsigned int=""> ccarray_to_std_vector(CCArray* pArray) // 将CCArray转换成Vector{    std::vector<unsigned int=""> ret;    CCObject* pObj;    CCARRAY_FOREACH(pArray, pObj)    {        CCInteger* pInteger = (CCInteger*)pObj;        ret.push_back((unsigned int)pInteger->getValue());    }    return ret;} enum{    kDefaultPadding =  5,}; ////CCMenu// CCMenu* CCMenu::create(){ // 传空参数,创建空菜单    return CCMenu::create(NULL, NULL);} CCMenu * CCMenu::create(CCMenuItem* item, ...){    va_list args;    va_start(args,item); //处理...形参         CCMenu *pRet = CCMenu::createWithItems(item, args);         va_end(args);         return pRet;} CCMenu* CCMenu::createWithArray(CCArray* pArrayOfItems){    CCMenu *pRet = new CCMenu();    if (pRet && pRet->initWithArray(pArrayOfItems))    {        pRet->autorelease(); // 设置自动释放    }    else    {        CC_SAFE_DELETE(pRet);    }         return pRet;} CCMenu* CCMenu::createWithItems(CCMenuItem* item, va_list args){    CCArray* pArray = NULL; // 将...形参转换成CCArray    if( item )    {        pArray = CCArray::create(item, NULL); // 创建一个CCArray        CCMenuItem *i = va_arg(args, CCMenuItem*);        while(i)        {            pArray->addObject(i); // 添加其他子菜单项            i = va_arg(args, CCMenuItem*);        }    }         return CCMenu::createWithArray(pArray);} CCMenu* CCMenu::createWithItem(CCMenuItem* item){    return CCMenu::create(item, NULL);} bool CCMenu::init(){    return initWithArray(NULL);} bool CCMenu::initWithArray(CCArray* pArrayOfItems){    if (CCLayer::init())    {        // 设置触摸相关信息        setTouchPriority(kCCMenuHandlerPriority);        setTouchMode(kCCTouchesOneByOne);        setTouchEnabled(true);         // 打开可用状态        m_bEnabled = true;                 // 菜单置于屏幕中间,并填充满整个屏幕        CCSize s = CCDirector::sharedDirector()->getWinSize();        this->ignoreAnchorPointForPosition(true);        setAnchorPoint(ccp(0.5f, 0.5f));        this->setContentSize(s);        setPosition(ccp(s.width/2, s.height/2));                 // 遍历添加菜单项        if (pArrayOfItems != NULL)        {            int z=0;            CCObject* pObj = NULL;            CCARRAY_FOREACH(pArrayOfItems, pObj)            {                CCMenuItem* item = (CCMenuItem*)pObj;                this->addChild(item, z);                z++;            }        }             // 将选中项置为空        m_pSelectedItem = NULL;        // 将状态置为没有菜单项被选中        m_eState = kCCMenuStateWaiting;                 // enable cascade color and opacity on menus        setCascadeColorEnabled(true);        setCascadeOpacityEnabled(true);                 return true;    }    return false;} /** override add:*/void CCMenu::addChild(CCNode * child){    CCLayer::addChild(child);} void CCMenu::addChild(CCNode * child, int zOrder){    CCLayer::addChild(child, zOrder);} void CCMenu::addChild(CCNode * child, int zOrder, int tag){ // 确保child为CCMenuItem之内    CCAssert( dynamic_cast<ccmenuitem*>(child) != NULL, Menu only supports MenuItem objects as children);    CCLayer::addChild(child, zOrder, tag);} void CCMenu::onExit(){    if (m_eState == kCCMenuStateTrackingTouch) // 如果有菜单项被选中,则取消选择状态    {        if (m_pSelectedItem)        { // 将选中项菜单选中状态清除,并将用于记录的变量置为空            m_pSelectedItem->unselected();            m_pSelectedItem = NULL;        }        // 将状态置为等待触摸        m_eState = kCCMenuStateWaiting;    }     CCLayer::onExit();} void CCMenu::removeChild(CCNode* child, bool cleanup){    CCMenuItem *pMenuItem = dynamic_cast<ccmenuitem*>(child);    CCAssert(pMenuItem != NULL, Menu only supports MenuItem objects as children);         // 如果是被选中的菜单项,则先把选中置为空    if (m_pSelectedItem == pMenuItem)    {        m_pSelectedItem = NULL;    }         CCNode::removeChild(child, cleanup);} //Menu - Events void CCMenu::setHandlerPriority(int newPriority){    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();    pDispatcher->setPriority(newPriority, this); // 设置触摸优先级} void CCMenu::registerWithTouchDispatcher(){ // 注册触摸    CCDirector* pDirector = CCDirector::sharedDirector();    pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), true);} bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event){    CC_UNUSED_PARAM(event);    if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled)    { // 排除不执行触摸事件状态        return false;    }     for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())    { // 如果菜单父类不可见则返回        if (c->isVisible() == false)        {            return false;        }    }<span style="white-space: pre;">    </span>// 选出触摸项    m_pSelectedItem = this->itemForTouch(touch);    if (m_pSelectedItem)    {        m_eState = kCCMenuStateTrackingTouch; // 将状态变为有菜单项被选中        m_pSelectedItem->selected(); // 对触摸项执行触摸事件        return true;    }    return false;} void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event){    CC_UNUSED_PARAM(touch); // 消除不使用警告    CC_UNUSED_PARAM(event);    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchEnded] -- invalid state);    if (m_pSelectedItem)    { // 解除菜单项选中状态        m_pSelectedItem->unselected();        m_pSelectedItem->activate();    }    m_eState = kCCMenuStateWaiting;} void CCMenu::ccTouchCancelled(CCTouch *touch, CCEvent* event){    CC_UNUSED_PARAM(touch);    CC_UNUSED_PARAM(event);    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchCancelled] -- invalid state);    if (m_pSelectedItem)    { // 解除菜单项选中状态        m_pSelectedItem->unselected();    }    m_eState = kCCMenuStateWaiting;} void CCMenu::ccTouchMoved(CCTouch* touch, CCEvent* event){    CC_UNUSED_PARAM(event);    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchMoved] -- invalid state);    CCMenuItem *currentItem = this->itemForTouch(touch); // 获取当前触摸的子菜单项    if (currentItem != m_pSelectedItem)     {        if (m_pSelectedItem) // 如果当前有选中项,先取消选中        {            m_pSelectedItem->unselected();        }        m_pSelectedItem = currentItem; // 重新设置选中项        if (m_pSelectedItem)        {            m_pSelectedItem->selected();        }    }} //Menu - Alignment// 垂直方向默认间隙排列void CCMenu::alignItemsVertically(){    this->alignItemsVerticallyWithPadding(kDefaultPadding);} // 垂直方向指定间隙排列void CCMenu::alignItemsVerticallyWithPadding(float padding){    float height = -padding;  // 第一个子菜单没有间隔,用于去掉这个重复    if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode*>(pObject);            if (pChild)            {                height += pChild->getContentSize().height * pChild->getScaleY() + padding; // 获取子菜单项和间隔的总高度            }        }    }     float y = height / 2.0f; // 因为定位点在中间,所以先除去2    if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode*>(pObject);            if (pChild)            {                pChild->setPosition(ccp(0, y - pChild->getContentSize().height * pChild->getScaleY() / 2.0f)); // 锚点在中间,所以除2                y -= pChild->getContentSize().height * pChild->getScaleY() + padding; // 减去一个子菜单项和间隔的高度            }        }    }} // 水平方向默认间隙排列void CCMenu::alignItemsHorizontally(void){    this->alignItemsHorizontallyWithPadding(kDefaultPadding);} // 水平方向指定间隙排列void CCMenu::alignItemsHorizontallyWithPadding(float padding) // 原理和垂直排列相似,只不过将x变成y{     float width = -padding;    if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode*>(pObject);            if (pChild)            {                width += pChild->getContentSize().width * pChild->getScaleX() + padding;            }        }    }     float x = -width / 2.0f;    if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode*>(pObject);            if (pChild)            {                pChild->setPosition(ccp(x + pChild->getContentSize().width * pChild->getScaleX() / 2.0f, 0));                 x += pChild->getContentSize().width * pChild->getScaleX() + padding;            }        }    }} void CCMenu::alignItemsInColumns(unsigned int columns, ...){ // 将...转换成va_list    va_list args;    va_start(args, columns);     this->alignItemsInColumns(columns, args);     va_end(args);} void CCMenu::alignItemsInColumns(unsigned int columns, va_list args){ // 将va_list转换成CCArray    CCArray* rows = CCArray::create();    while (columns)    {        rows->addObject(CCInteger::create(columns));        columns = va_arg(args, unsigned int);    }    alignItemsInColumnsWithArray(rows);} void CCMenu::alignItemsInColumnsWithArray(CCArray* rowsArray){    vector<unsigned int=""> rows = ccarray_to_std_vector(rowsArray); // 将CCArray转换成vector     int height = -5; // 为-5是用于除去第一行的间隙    unsigned int row = 0; // 索引,当前行序号    unsigned int rowHeight = 0; // 放置每一行高度(即每行最大高度)    unsigned int columnsOccupied = 0; // 已放置列数    unsigned int rowColumns; // 放置该行有多少列     if (m_pChildren && m_pChildren->count() > 0) // 子菜单项数组存在且个数不为0    {        CCObject* pObject = NULL; // 临时变量        CCARRAY_FOREACH(m_pChildren, pObject) // 遍历子菜单项数组        {            CCNode* pChild = dynamic_cast<ccnode*>(pObject); // 将CCObject对象强制转换成CCNode对象            if (pChild) // 如果强制转换成功            {                CCAssert(row < rows.size(), );                 rowColumns = rows[row]; // 获取该行列数                CCAssert(rowColumns, );                 float tmp = pChild->getContentSize().height; // 获取当前子菜单高度                rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 如果行高大于当前子菜单项高度,或者当前子菜单项高度越界,rowHeight不变,否则rowHeight等于当前子菜单项高度(用来存放最大菜单项高度)                 ++columnsOccupied; // 已放置列数自加                if (columnsOccupied >= rowColumns) // 如果已放置列数大于等于该行可容纳列数                {                    height += rowHeight + 5; // 高度加上当前高度加上间隙                    columnsOccupied = 0; // 重置已放置列数                    rowHeight = 0; // 重置每行行高的临时变量                    ++row; // 跳转至下一行                }            }        }    }         // check if too many rows/columns for available menu items    CCAssert(! columnsOccupied, );     CCSize winSize = CCDirector::sharedDirector()->getWinSize(); // 获取屏幕大小     row = 0;    rowHeight = 0;    rowColumns = 0;    float w = 0.0;    float x = 0.0;    float y = (float)(height / 2);     if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode>(pObject);            if (pChild)            {                if (rowColumns == 0) // 如果需要放置数量为0才重新读取数据                {                    rowColumns = rows[row]; // 获取需要放置的数量                    w = winSize.width / (1 + rowColumns); //占屏幕宽度大小平分                    x = w;                }                 float tmp = pChild->getContentSize().height;                rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 获取当前行高度最大高度                pChild->setPosition(ccp(x - winSize.width / 2,                                       y - pChild->getContentSize().height / 2)); // 重新放置位置                 x += w;                ++columnsOccupied;                 if (columnsOccupied >= rowColumns) // 如果已放置列数大于等于需要放置的列数                {                    y -= rowHeight + 5; // 减去一个子菜单项和间隔的高度                     columnsOccupied = 0;                    rowColumns = 0;                    rowHeight = 0;                    ++row;                }            }        }    }    } //alignItemsInRows系列方法参照alignItemsInColumnsvoid CCMenu::alignItemsInRows(unsigned int rows, ...){    va_list args;    va_start(args, rows);     this->alignItemsInRows(rows, args);     va_end(args);} void CCMenu::alignItemsInRows(unsigned int rows, va_list args){    CCArray* pArray = CCArray::create();    while (rows)    {        pArray->addObject(CCInteger::create(rows));        rows = va_arg(args, unsigned int);    }    alignItemsInRowsWithArray(pArray);} void CCMenu::alignItemsInRowsWithArray(CCArray* columnArray){    vector<unsigned int=""> columns = ccarray_to_std_vector(columnArray);     vector<unsigned int=""> columnWidths;    vector<unsigned int=""> columnHeights;     int width = -10;    int columnHeight = -5;    unsigned int column = 0;    unsigned int columnWidth = 0;    unsigned int rowsOccupied = 0;    unsigned int columnRows;     if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode>(pObject);            if (pChild)            {                // check if too many menu items for the amount of rows/columns                CCAssert(column < columns.size(), );                 columnRows = columns[column];                // can't have zero rows on a column                CCAssert(columnRows, );                 // columnWidth = fmaxf(columnWidth, [item contentSize].width);                float tmp = pChild->getContentSize().width;                columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);                 columnHeight += (int)(pChild->getContentSize().height + 5);                ++rowsOccupied;                 if (rowsOccupied >= columnRows)                {                    columnWidths.push_back(columnWidth);                    columnHeights.push_back(columnHeight);                    width += columnWidth + 10;                     rowsOccupied = 0;                    columnWidth = 0;                    columnHeight = -5;                    ++column;                }            }        }    }     // check if too many rows/columns for available menu items.    CCAssert(! rowsOccupied, );     CCSize winSize = CCDirector::sharedDirector()->getWinSize();     column = 0;    columnWidth = 0;    columnRows = 0;    float x = (float)(-width / 2);    float y = 0.0;     if (m_pChildren && m_pChildren->count() > 0)    {        CCObject* pObject = NULL;        CCARRAY_FOREACH(m_pChildren, pObject)        {            CCNode* pChild = dynamic_cast<ccnode>(pObject);            if (pChild)            {                if (columnRows == 0)                {                    columnRows = columns[column];                    y = (float) columnHeights[column];                }                 // columnWidth = fmaxf(columnWidth, [item contentSize].width);                float tmp = pChild->getContentSize().width;                columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);                 pChild->setPosition(ccp(x + columnWidths[column] / 2,                                       y - winSize.height / 2));                 y -= pChild->getContentSize().height + 10;                ++rowsOccupied;                 if (rowsOccupied >= columnRows)                {                    x += columnWidth + 5;                    rowsOccupied = 0;                    columnRows = 0;                    columnWidth = 0;                    ++column;                }            }        }    }} CCMenuItem* CCMenu::itemForTouch(CCTouch *touch){    CCPoint touchLocation = touch->getLocation(); // 获取当前触摸点    if (m_pChildren && m_pChildren->count() > 0) // 判断子节点(子菜单项)是否存在,且个数不为0    {        CCObject* pObject = NULL; // 临时变量        CCARRAY_FOREACH(m_pChildren, pObject) // 遍历子菜单项数组        {            CCMenuItem* pChild = dynamic_cast<ccmenuitem>(pObject); // 强制转换,获取当前子菜单项            if (pChild && pChild->isVisible() && pChild->isEnabled()) // 如果当前子菜单项不为空、且可见、且可用            {                CCPoint local = pChild->convertToNodeSpace(touchLocation); // 把世界坐标转换到当前节点的本地坐标系中                CCRect r = pChild->rect(); // 获取子菜单项范围                r.origin = CCPointZero;                if (r.containsPoint(local)) // 判断子菜单项范围中是否包括触摸点                {                    return pChild; // 返回该子菜单项                }            }        }    }    return NULL; // 返回空}


看懂了。就是知识



0 0