CCControlButtton
CCMenu简介
CCControlButtton是按钮控件,由一个标签加背景图片构成。cocos的CCMenu以及提供了相应的控件功能,CCMenu同CCControlButtton一样派生于CCLayer,可以开启触摸。CCMenu的孩子CCMenuItem已经提供了简单的按钮功能了,可以由图片或者文字构建一个菜单项,CCMenu可以看成一个控件层,在上面布局子控件CCMenuItem。下面是CCMenu的应用代码:
CCLabelTTF *label = CCLabelTTF::create("CCSequence test", "Arial", 24);
CCMenuItemLabel *item = CCMenuItemLabel::create(label, this, menu_selector(ActionScene::menuAction1));
item->setPosition(ccp(320, 1100));
menu->addChild(item);
上面在创建的时候直接指定了目标与动作,其中动作部分是menu_selector(ActionScene::menuAction1),menu_selector的作用是取ActionScene::menuAction1函数指针并向上转型,这里ActionScene必须是CCObject子类。相关代码如下:
typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
#define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
SEL_MenuHandler是void CCObject::fun(CCObject*)类型函数指针。
这里指定菜单项是一个标签,可以使用CCMenuItemSprite、CCMenuItemImage创建带有图片的菜单项。
可以先创建好菜单项,然后再指定目标动作,代码如下:
CCMenuItemImage *img = CCMenuItemImage::create("reset.png", "reset.png");
img->setTarget(this, menu_selector(ActionScene::reset));
img->setPosition(ccp(50, 1000));
menu->addChild(img);
菜单项的触摸事件比较简单,只有touchdown这个事件,无法处理touchdown后的手指拖拽。CCMenu是cocos标准库中的内容,后来cocos引入了其他人对cocos的扩展,其中有一个是控件扩展,提供的控件挺多,其中有一个控件是CCControlButtton,它同CCMenuItem一样,可以有标签和图片,图片使用CCScale9Sprite,支持九宫格图片。除此之外,CControlButtton定义了更多的事件,这些事件如下:
事件 | 描述 |
---|---|
CCControlEventTouchDown | 开始触摸 |
CCControlEventTouchDragEnter | 触摸后从控件外拖拽进来 |
CCControlEventTouchDragInside | 触摸后在控件内拖拽 |
CCControlEventTouchDragExit | 触摸后从控件里拖拽出来 |
CCControlEventTouchDragOutside | 触摸后在控件外拖拽 |
CCControlEventTouchUpInside | 触摸后在控件里触摸结束 |
CCControlEventTouchUpOutside | 触摸后在控件外触摸结束 |
上面看出CCControlButtton对手势的事件支持挺多的,这里主要根据用户的手在不在控件里进行手势划分的。
接下来对CCControlButtton进行详细的分析,目的除了了解使用背后的原理,更是为了方便自定义一些控件,构造游戏中的控件库。
CCControlButtton代码分析
CCControlButtton的应用代码如下:
CCSpriteFrame *btnSf = CCSpriteFrame::create("ui.png", CCRectMake(760, 163, 64, 60));
CCScale9Sprite *btnS9 = CCScale9Sprite::createWithSpriteFrame(btnSf);
CCControlButton *btn = CCControlButton::create(btnS9);
btn->setPreferredSize(CCSizeMake(64, 64));
btn->setPosition(ccp(100, 50));
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(TilemapTest::itemsBarClick), CCControlEventTouchDown);
this->addChild(btn);
上面的代码添加了一个CCControlButton,我使用了一张网上download下来的游戏资源图片创建了一个精灵帧(描述纹理以及纹理的使用范围),然后用此创建一个九宫格精灵,再用它创建CCControlButton,setPreferredSize目的是防止九宫格精灵被变形,这里只想保持纹理的默认大小。由于网上并没有提供图片中每个子图片的属性描述文件,所以只能手动测量子图片在大图片中的的纹理范围了,这里范围是CCRectMake(760, 163, 64, 60)。最终在场景中创建了一个物品按钮,图片如下:
上面是一个RPG小游戏,黑色区域是碰撞区,占时没有隐藏。图片左下角是加入的按钮
现在分析CCControlButton是如何创建的,以及如何响应用户触摸事件的。CCScale9Sprite::createWithSpriteFrame(btnSf)代码如下:
CCControlButton* CCControlButton::create(CCScale9Sprite* sprite)
{
CCControlButton *pRet = new CCControlButton(); //创建CCControlButton
pRet->initWithBackgroundSprite(sprite); //初始化
pRet->autorelease();
return pRet;
}
pRet->initWithBackgroundSprite(sprite);代码如下:
bool CCControlButton::initWithBackgroundSprite(CCScale9Sprite* sprite)
{
CCLabelTTF *label = CCLabelTTF::create("", "Arial", 30); //创建标签
return initWithLabelAndBackgroundSprite(label, sprite); //继续初始化
}
initWithLabelAndBackgroundSprite(label, sprite);代码如下:
bool CCControlButton::initWithLabelAndBackgroundSprite(CCNode* node, CCScale9Sprite* backgroundSprite)
{
if (CCControl::init())
{
CCAssert(node != NULL, "Label must not be nil.");
CCLabelProtocol* label = dynamic_cast<CCLabelProtocol*>(node); //向上转型用于下面检查
CCRGBAProtocol* rgbaLabel = dynamic_cast<CCRGBAProtocol*>(node); //向上转型用于下面检查
CCAssert(backgroundSprite != NULL, "Background sprite must not be nil.");
CCAssert(label != NULL || rgbaLabel!=NULL || backgroundSprite != NULL, "");
m_bParentInited = true;
// Initialize the button state tables
this->setTitleDispatchTable(CCDictionary::create()); //用字典初始化4个表
this->setTitleColorDispatchTable(CCDictionary::create());
this->setTitleLabelDispatchTable(CCDictionary::create());
this->setBackgroundSpriteDispatchTable(CCDictionary::create());
setTouchEnabled(true); //开启触摸
m_isPushed = false; //是否被按下
m_zoomOnTouchDown = true; //按下时是否执行放大与缩小动作
m_currentTitle=NULL; //标签内容为空
// Adjust the background image by default
setAdjustBackgroundImage(true); //调整背景图片
setPreferredSize(CCSizeZero); //设置偏爱的大小
// Zooming button by default
m_zoomOnTouchDown = true;
// Set the default anchor point
ignoreAnchorPointForPosition(false);
setAnchorPoint(ccp(0.5f, 0.5f));
// Set the nodes
setTitleLabel(node);
setBackgroundSprite(backgroundSprite);
// Set the default color and opacity
setColor(ccc3(255.0f, 255.0f, 255.0f));
setOpacity(255.0f);
setOpacityModifyRGB(true);
// Initialize the dispatch table
CCString* tempString = CCString::create(label->getString());
//tempString->autorelease();
setTitleForState(tempString, CCControlStateNormal);
setTitleColorForState(rgbaLabel->getColor(), CCControlStateNormal);
setTitleLabelForState(node, CCControlStateNormal);
setBackgroundSpriteForState(backgroundSprite, CCControlStateNormal);
setLabelAnchorPoint(ccp(0.5f, 0.5f));
// Layout update
needsLayout();
return true;
}
//couldn't init the CCControl
else
{
return false;
}
}
this->setTitleDispatchTable(CCDictionary::create()); 等四个函数就是设置4个表,分别对应标签文本、标签颜色、标签、背景图片。用户可以存储标签与图片的不同状态在这些表中,控件在每一个状态,就会使用此状态下的标签与图片。
setTouchEnabled(true); 开启触摸,这里将调用CCLayer的,因为CCControlButton是CCLayer子类,并且cocos2dx2.2.2也就CCLayer实现了向触摸事件分发器注册与移除触摸代理的功能,继承CCLayer就可以使用这些功能了。这里是cocos2dx的触摸机制的详细分析cocos2dx-2.2.2触摸详解
setAdjustBackgroundImage(true);代码如下:
void CCControlButton::setAdjustBackgroundImage(bool adjustBackgroundImage)
{
m_doesAdjustBackgroundImage=adjustBackgroundImage;