zoukankan      html  css  js  c++  java
  • Cocos2d中的Menu使用

    学习cocos2d-x中的菜单主要须要了解:菜单(CCMenu)和菜单项(CCMenuItem)以及CCMenuItem的详细子类。


    a. 以下来学习一下相关的类。
    1. CCMenu
    菜单,是CCLayer的子类,是一个层(容器),能够往里面加入菜单项。以下是它的类结构图:

     

    CCMenu默认接受触屏事件的优先级是-128(优先级非常高,由于值越小,响应触屏事件的优先级越高),能够通过继承它实现自己定义的效果,创建CCMenu对象的函数:

    static CCMenu* menuWithItems(CCMenuItem* item, ...);
    static CCMenu* menuWithItem(CCMenuItem* item);

    2. CCMenuItem
    菜单项,开发中通常是直接使用它的子类。CCMenuItem有三个直接子类:
    CCMenuItemLabel(字符标签菜单)、CCMenuItemSprite(图片菜单)、CCMenuItemToggle(开关菜单)。
    以下是CCMenuItem的类结构图:

     

    如今分别来了解一下各个不同的菜单项。


    (1) CCMenuItemLabel:使用文字标签创建菜单项
    全部支持CCLabelProtocol的节点都能够用来创建CCMenuItemLabel。CCLabelProtocol是标签的共同接口。CCLabelProtocol也有三个直接子类,以下是类结构图:

     

    CCLabelTTF:同一时候也是CCSprite的子类,用来渲染文字标签的,能够指定字体。每次设置字符串内容时都须要又一次创建纹理和渲染,性能不好。能够看它的相关源代码:

    复制代码
    void CCLabelTTF::setString(const char *label)
    {
        if (m_pString)
        {
            delete m_pString;
            m_pString = NULL;
        }
        m_pString = new std::string(label);
     
        CCTexture2D *texture;
        if( CCSize::CCSizeEqualToSize( m_tDimensions, CCSizeZero ) )
        {
            texture = new CCTexture2D();
            texture->initWithString(label, m_pFontName->c_str(), m_fFontSize);
        }
        else
        {
            texture = new CCTexture2D();
            texture->initWithString(label, m_tDimensions, m_eAlignment, m_pFontName->c_str(), m_fFontSize);
        }
        this->setTexture(texture);
        texture->release();
     
        CCRect rect = CCRectZero;
        rect.size = m_pobTexture->getContentSize();
        this->setTextureRect(rect);
    }
    复制代码

    能够用CCLabelBMFont或者CCLabelAtlas取代它。
    CCLabelBMFont:也是CCSpriteBatchNode的子类,创建CCLabelBMFont对象须要一个字符串和一个fnt格式的文件(字库),如:

    CCLabelBMFont *label = CCLabelBMFont::labelWithString("Bitmap Font Atlas", "fonts/bitmapFontTest.fnt");

    这个fnt文件包括了这些信息:相应图片的名字(图片包括了全部你要绘制的字符)、图片中的字符相应的unicode编码、字符在图片中的坐标、宽高等。初始化CCLabelBMFont对象时,会把图片加入到缓存(CCTextureCache)中。解析fnt文件,把fnt文件里相应的信息保存到一个ccBMFontDef类型的数组里面,数组的索引是charId(字符的unicode编码值),ccBMFontDef是一个结构体:

    复制代码
    typedef struct _BMFontDef {
            //! ID of the character
            unsigned int charID;
            //! origin and size of the font
            CCRect rect;
            //! The X amount the image should be offset when drawing the image (in pixels)
            int xOffset;
            //! The Y amount the image should be offset when drawing the image (in pixels)
            int yOffset;
            //! The amount to move the current position after drawing the character (in pixels)
            int xAdvance;
        } ccBMFontDef;
    复制代码

    绘制字符串时,依据字符相应的unicode码去查找ccBMFontDef信息,从缓存中取出图片。再依据ccBMFontDef中坐标、宽高取出相应区域的字符图片,把字符在字符串中的索引位置作为tag加入到CCLabelBMFont中。由于CCLabelBMFont本身是CCSpriteBatchNode。这样就实现了批处理渲染精灵,提高了性能。以下是创建字符相应的CCSprite的部分代码:

    复制代码
    void CCLabelBMFont::createFontChars()
    {
    /** .... */
    //以下代码是遍历字符串时:for循环内的代码
        const ccBMFontDef& fontDef = (*(m_pConfiguration->m_pBitmapFontArray))[c];
     
        CCRect rect = fontDef.rect;
     
        CCSprite *fontChar;
     
        fontChar = (CCSprite*)(this->getChildByTag(i));
        if( ! fontChar )
        {
            fontChar = new CCSprite();
            fontChar->initWithBatchNodeRectInPixels(this, rect);
            this->addChild(fontChar, 0, i);
            fontChar->release();
        }
        else
        {
            // reusing fonts
            fontChar->setTextureRectInPixels(rect, false, rect.size);
     
            // restore to default in case they were modified
            fontChar->setIsVisible(true);
            fontChar->setOpacity(255);
        }
     
    /** .... */
    }
    复制代码

    CCLabelAtlas:也是CCAtlasNode的子类,创建一个CCLabelAtlas对象的代码例如以下:

    static CCLabelAtlas * labelWithString(const char *label, const char *charMapFile, unsigned int itemWidth, unsigned int itemHeight, unsigned char startCharMap);
    //演示样例
    CCLabelAtlas* label1 = CCLabelAtlas::labelWithString("123 Test", "fonts/tuffy_bold_italic-charmap.png", 48, 64, ' ');

    參数的含义:要绘制的字符,图片文件,图片文件里每一个字符的宽度。图片文件里每一个字符的高度,图片的起始字符。
    CCAtlasNode封装了一个CCTextureAtlas的变量。CCTextureAtlas初始化图片文件的时候会把图片载入到缓存(CCTextureCache)中:

    复制代码
    bool CCTextureAtlas::initWithFile(const char * file, unsigned int capacity)
    {
        // retained in property
        CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(file);
     
        if (texture)
        {
            return initWithTexture(texture, capacity);
        }
        else
        {
            CCLOG("cocos2d: Could not open file: %s", file);
            delete this;
     
            return NULL;
        }
    }
    复制代码

    接下来CCTextureAtlas负责管理该大图。能够任意绘制图片的某一矩形区域,渲染方式採用的是OpenGL ES VBO(顶点缓冲对象。保存在显存中)。 CCTextureAtlas有一个m_pQuads属性,它是CCTextureAtlas类的核心。是一个ccV3F_C4B_T2F_Quad类型的数组,ccV3F_C4B_T2F_Quad是一个结构体,有四个成员属性。它们都是ccV3F_C4B_T2F类,分别表示左上。左下,右上,右下。看源代码:

    复制代码
    //! a Point with a vertex point, a tex coord point and a color 4B
    typedef struct _ccV3F_C4B_T2F
    {
        //! vertices (3F)
        ccVertex3F      vertices;           // 12 bytes
    //  char __padding__[4];
     
        //! colors (4B)
        ccColor4B       colors;             // 4 bytes
    //  char __padding2__[4];
     
        // tex coords (2F)
        ccTex2F         texCoords;          // 8 byts
    } ccV3F_C4B_T2F;
     
    //! 4 ccVertex2FTex2FColor4B Quad
    typedef struct _ccV2F_C4B_T2F_Quad
    {
        //! bottom left
        ccV2F_C4B_T2F   bl;
        //! bottom right
        ccV2F_C4B_T2F   br;
        //! top left
        ccV2F_C4B_T2F   tl;
        //! top right
        ccV2F_C4B_T2F   tr;
    } ccV2F_C4B_T2F_Quad;
    复制代码

    ccV3F_C4B_T2F有三个成员,分别表示:顶点、颜色、纹理坐标。
    CCTextureAtlas类就是依据这个数组来绘制矩形的,数组的容量就是要绘制的字符数量。指定字符串的时候:是依据指定字符的ASCII码值跟startCharMap(图片起始字符)ASCII码值的偏移量。得到该字符在图片上的区域的。然后生成绘制矩形所须要的数据,源代码:

    复制代码
    //CCLabelAtlas - CCLabelProtocol
        void CCLabelAtlas::setString(const char *label)
        {
            /** .... */
            this->updateAtlasValues();
     
            /** .... */
        }
     
        //CCLabelAtlas - Atlas generation
        void CCLabelAtlas::updateAtlasValues()
        {
            unsigned int n = m_sString.length();
     
            ccV3F_C4B_T2F_Quad quad;
     
            const unsigned char *s = (unsigned char*)m_sString.c_str();
     
            CCTexture2D *texture = m_pTextureAtlas->getTexture();
            float textureWide = (float) texture->getPixelsWide();
            float textureHigh = (float) texture->getPixelsHigh();
     
            for(unsigned int i = 0; i < n; i++) {
                unsigned char a = s[i] - m_cMapStartChar;
                float row = (float) (a % m_uItemsPerRow);
                float col = (float) (a / m_uItemsPerRow);
     
    #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
                // Issue #938. Don't use texStepX & texStepY
                float left      = (2 * row * m_uItemWidth + 1) / (2 * textureWide);
                float right     = left + (m_uItemWidth * 2 - 2) / (2 * textureWide);
                float top       = (2 * col * m_uItemHeight + 1) / (2 * textureHigh);
                float bottom    = top + (m_uItemHeight * 2 - 2) / (2 * textureHigh);
    #else
                float left      = row * m_uItemWidth / textureWide;
                float right     = left + m_uItemWidth / textureWide;
                float top       = col * m_uItemHeight / textureHigh;
                float bottom    = top + m_uItemHeight / textureHigh;
    #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
     
                quad.tl.texCoords.u = left;
                quad.tl.texCoords.v = top;
                quad.tr.texCoords.u = right;
                quad.tr.texCoords.v = top;
                quad.bl.texCoords.u = left;
                quad.bl.texCoords.v = bottom;
                quad.br.texCoords.u = right;
                quad.br.texCoords.v = bottom;
     
                quad.bl.vertices.x = (float) (i * m_uItemWidth);
                quad.bl.vertices.y = 0;
                quad.bl.vertices.z = 0.0f;
                quad.br.vertices.x = (float)(i * m_uItemWidth + m_uItemWidth);
                quad.br.vertices.y = 0;
                quad.br.vertices.z = 0.0f;
                quad.tl.vertices.x = (float)(i * m_uItemWidth);
                quad.tl.vertices.y = (float)(m_uItemHeight);
                quad.tl.vertices.z = 0.0f;
                quad.tr.vertices.x = (float)(i * m_uItemWidth + m_uItemWidth);
                quad.tr.vertices.y = (float)(m_uItemHeight);
                quad.tr.vertices.z = 0.0f;
     
                m_pTextureAtlas->updateQuad(&quad, i);
     
            }
        }
    复制代码

    所以图片上的字符排列顺序要依照ASCII码表的顺序连续排列。CCLabelAtlas的绘制效率高,可是限制性太多,没有CCLabelBMFont灵活。

    从类结构图能够看到CCMenuItemLabel有两个子类CCMenuItemAtlasFont和CCMenuItemFont。CCMenuItemAtlasFont是使用CCLabelAtlas创建MenuItemLabel的辅助类,CCMenuItemFont是使用CCLabelTTF创建MenuItemLabel的辅助类。例如以下源代码所看到的:

    复制代码
    bool CCMenuItemAtlasFont::initFromString(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap, CCObject* target, SEL_MenuHandler selector)
        {
            CCAssert( value != NULL && strlen(value) != 0, "value length must be greater than 0");
            CCLabelAtlas *label = new CCLabelAtlas();
            label->initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap);
            label->autorelease();
            if (CCMenuItemLabel::initWithLabel(label, target, selector))
            {
                // do something ?

    } return true; } bool CCMenuItemFont::initFromString(const char *value, CCObject* target, SEL_MenuHandler selector) { CCAssert( value != NULL && strlen(value) != 0, "Value length must be greater than 0"); m_strFontName = _fontName; m_uFontSize = _fontSize; CCLabelTTF *label = CCLabelTTF::labelWithString(value, m_strFontName.c_str(), (float)m_uFontSize); if (CCMenuItemLabel::initWithLabel(label, target, selector)) { // do something ?

    } return true; }

    复制代码

    2. CCMenuItemSprite和CCMenuItemImage本质上都是使用图片创建菜单项,前者是使用精灵对象创建,后者使用图片名称创建。CCMenuItemImage是CCMenuItemSprite的子类。能够使用三套图片:未选中状态、选中状态、不可用状态。前面两种状态的图片是必需的,不可用状态的图片可选。例如以下代码所看到的:

    static CCMenuItemSprite * itemFromNormalSprite(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite = NULL);
     
    static CCMenuItemImage* itemFromNormalImage(const char *normalImage, const char *selectedImage);
    static CCMenuItemImage* itemFromNormalImage(const char *normalImage, const char *selectedImage, const char *disabledImage);

    3. CCMenuItemToggle: 开关菜单
    它是一个容器,能够切换包括的子项(能够是不论什么的MenuItem对象)。

    它封装了一个CCMutableArray<CCMenuItem*>*类型的属性m_pSubItems。代码演示样例:

    static CCMenuItemToggle* itemWithTarget(CCObject* target, SEL_MenuHandler selector, CCMenuItem* item, ...);        
     
    CCMenuItemToggle* item1 = CCMenuItemToggle::itemWithTarget(this,menu_selector(MenuLayer4::menuCallback), 
    CCMenuItemFont::itemFromString( "On" ),
    CCMenuItemFont::itemFromString( "Off"),NULL );

    b. 分析了菜单的各个相关类的原理和使用方法后。如今来看看怎样使用它们,以下演示样例代码整合了各种菜单项的创建:

    复制代码
    void MenuLayer::onEnter()
    {
        CCLayer::onEnter();
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
     
        /**---CCMenuItemLabel:由指定的字符串标签创建菜单--**/
        //CCMenuItemFont:内部使用CCLabelTTF
        CCMenuItemFont::setFontName("Arial");
        CCMenuItemFont::setFontSize(22);
        CCMenuItemFont* pFontMenuItem = CCMenuItemFont::itemFromString("font item", this, menu_selector(MenuLayer::menuCallback));
        CCMenu* pFontMenu = CCMenu::menuWithItems(pFontMenuItem,NULL);
        pFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 30) );
        this->addChild(pFontMenu);
     
        //CCMenuItemAtlasFont:内部使用CCLabelAtlas
        CCMenuItemAtlasFont* pAtlasFontMenuItem = CCMenuItemAtlasFont::itemFromString("123456789", s_imgPathNum, 15, 19, '0', this, menu_selector(MenuLayer::menuCallback));
        CCMenu* pAtlasFontMenu = CCMenu::menuWithItems(pAtlasFontMenuItem,NULL);
        pAtlasFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 60) );
        this->addChild(pAtlasFontMenu);
     
        //CCLabelBMFont
        CCLabelBMFont* pBMFontLabel = CCLabelBMFont::labelWithString("configuration", s_imgPathBMFont);
        CCMenuItemLabel* pItemBMFontLabel = CCMenuItemLabel::itemWithLabel(pBMFontLabel, this, menu_selector(MenuLayer::menuCallback));
        CCMenu* pBMFontMenu = CCMenu::menuWithItems(pItemBMFontLabel,NULL);
        pBMFontMenu->setPosition( ccp(winSize.width/2,winSize.height - 90) );
        this->addChild(pBMFontMenu);
     
        /**--CCMenuItemSprite:由指定的精灵类创建菜单--**/
        CCSprite* spriteNormal = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*2,115,23));
        CCSprite* spriteSelected = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*1,115,23));
        CCSprite* spriteDisabled = CCSprite::spriteWithFile(s_imgPathMenuItem, CCRectMake(0,23*0,115,23));
        CCMenuItemSprite* pMenuItemSprite = CCMenuItemSprite::itemFromNormalSprite(spriteNormal, spriteSelected, spriteDisabled, this, menu_selector(MenuLayer::menuCallback));
        CCMenu* pSpriteMenu = CCMenu::menuWithItems(pMenuItemSprite,NULL);
        pSpriteMenu->setPosition( ccp(winSize.width/2,winSize.height - 120) );
        this->addChild(pSpriteMenu);
     
        //CCMenuItemImage:由指定的图片文件名称创建菜单
        CCMenuItemImage* pMenuItemImage = CCMenuItemImage::itemFromNormalImage(s_imgPathCloseNormal, s_imgPathCloseSelected, this, menu_selector(MenuLayer::menuCallback) );
        CCMenu* pImageMenu = CCMenu::menuWithItem(pMenuItemImage);
        pImageMenu->setPosition( ccp(winSize.width/2,winSize.height - 150) );
        this->addChild(pImageMenu);
     
        //CCMenuItemToggle:开关菜单,切换效果
        //这里仅仅使用了CCMenuItemFont。还能够使用其它的CCMenuItem
        CCMenuItemToggle* pMenuItemToggle = CCMenuItemToggle::itemWithTarget(this, menu_selector(MenuLayer::menuCallback), 
                                                                  CCMenuItemFont::itemFromString( "On" ),
                                                                  CCMenuItemFont::itemFromString( "Off"),
                                                                  NULL );
        CCMenu* pToggleMenu = CCMenu::menuWithItems(pMenuItemToggle,NULL);
        pToggleMenu->setPosition( ccp(winSize.width/2,winSize.height - 180) );
        this->addChild(pToggleMenu);
    }
    复制代码
  • 相关阅读:
    non-blocking I/O
    jetty netty
    Azkaban_Oozie_action
    权限过大 ssh协议通过pem文件登陆
    交易准实时预警 kafka topic 主题 异常交易主题 低延迟 event topic alert topic 内存 算法测试
    flink_action
    netty
    1970 ted codd
    Data dictionary
    mina
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8296255.html
Copyright © 2011-2022 走看看