cocos2d-x学习笔记-CCMenu和CCMenuItem详解

2023-12-25 01:50

本文主要是介绍cocos2d-x学习笔记-CCMenu和CCMenuItem详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文:http://codingnow.cn/cocos2d-x/832.html

1. CCMenu
菜单,是CCLayer的子类,是一个层(容器),可以往里面添加菜单项。下面是它的类结构图:
CCMenu默认接受触屏事件的优先级是-128(优先级很高,因为值越小,响应触屏事件的优先级越高),可以通过继承它实现自定义的效果,创建CCMenu对象的函数:

1
2
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的子类,用来渲染文字标签的,可以指定字体,每次设置字符串内容时都需要重新创建纹理和渲染,性能不好,可以看它的相关源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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格式的文件(字库),如:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
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的部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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对象的代码如下:

1
2
3
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)中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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类,分别表示左上,左下,右上,右下。看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//! 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码值的偏移量,得到该字符在图片上的区域的,然后生成绘制矩形所需要的数据,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//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有两个子类CCMenuItemAtlasFontCCMenuItemFont,CCMenuItemAtlasFont是使用CCLabelAtlas创建MenuItemLabel的辅助类,CCMenuItemFont是使用CCLabelTTF创建MenuItemLabel的辅助类。如下源码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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的子类。可以使用三套图片:未选中状态、选中状态、不可用状态,前面两种状态的图片是必需的,不可用状态的图片可选。如下代码所示:

1
2
3
4
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。代码示例:

1
2
3
4
5
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 );

这篇关于cocos2d-x学习笔记-CCMenu和CCMenuItem详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/533890

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)